1  /*
2   * UAE - The Un*x Amiga Emulator
3   *
4   * Support for Linux/ALSA sound
5   *
6   * Copyright 1997 Bernd Schmidt
7   * Copyright 2004 Heikki Orsila
8   * Copyright 2006-2007 Richard Drummond
9   *
10   * BUGS: certainly
11   * TODO:
12   * - if setup_sound() fails, there may still be hope to get the
13   *   sound device, but we totally give up.. see sd-uss.
14   */
15 
16 #include "sysconfig.h"
17 #include "sysdeps.h"
18 
19 #include "cfgfile.h"
20 #include "gensound.h"
21 #include "sounddep/sound.h"
22 #include "driveclick.h"
23 
24 #include <alsa/asoundlib.h>
25 
26 /* internal types */
27 char alsa_device[256];
28 bool alsa_verbose;
29 
30 unsigned int have_sound = 0;
31 
32 uae_u16 paula_sndbuffer[44100];
33 uae_u16 *paula_sndbufpt;
34 int paula_sndbufsize;
35 
36 snd_pcm_t *alsa_playback_handle = 0;
37 int bytes_per_frame;
38 
39 static struct sound_data sdpaula;
40 static struct sound_data *sdp = &sdpaula;
41 
42 /* internal prototypes */
43 void audio_default_options (struct uae_prefs *);
44 void audio_save_options (struct zfile *, const struct uae_prefs *);
45 int audio_parse_option (struct uae_prefs *, const char *, const char *);
46 void set_volume_sound_device (struct sound_data *, int, int);
47 void set_volume (int, int);
48 void master_sound_volume (int);
49 void sound_mute (int);
50 
51 
close_sound(void)52 void close_sound (void)
53 {
54   if (alsa_playback_handle) {
55     snd_pcm_close (alsa_playback_handle);
56     alsa_playback_handle = 0;
57   }
58 }
59 
open_sound(void)60 static int open_sound(void)
61 {
62   return snd_pcm_open (&alsa_playback_handle, alsa_device, SND_PCM_STREAM_PLAYBACK, 0);
63 }
64 
65 /* Try to determine whether sound is available.  This is only for GUI purposes.  */
setup_sound(void)66 int setup_sound (void)
67 {
68 	int err;
69 	sound_available = 0;
70 
71 	printf("ALSA lib version: %s\n", SND_LIB_VERSION_STR);
72 	if ((err = open_sound()) < 0) {
73 		/* TODO: if the pcm was busy, we should the same as sd-uss does.
74 		tell the caller that sound is available. in any other
75 		condition we should just return 0. */
76 	    	write_log ("ALSA: Can't open audio device: %s\n", snd_strerror (err));
77     		return 0;
78 	}
79 	snd_pcm_close (alsa_playback_handle);
80 	alsa_playback_handle = 0;
81 	sound_available = 1;
82 	return 1;
83 }
84 
set_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * hw_params,unsigned int * rate,unsigned int channels,snd_pcm_format_t format,unsigned int * buffer_time,snd_pcm_uframes_t * buffer_frames,snd_pcm_uframes_t * period_frames)85 static int set_hw_params(snd_pcm_t *pcm,
86 			 snd_pcm_hw_params_t *hw_params,
87 			 unsigned int *rate,
88 			 unsigned int channels,
89 			 snd_pcm_format_t format,
90 			 unsigned int *buffer_time,
91 			 snd_pcm_uframes_t *buffer_frames,
92 			 snd_pcm_uframes_t *period_frames)
93 {
94     int err;
95     unsigned int periods = 2;
96 
97     err = snd_pcm_hw_params_any (pcm, hw_params);
98     if (err < 0)
99 		return err;
100 
101     err = snd_pcm_hw_params_set_access (pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
102     if (err < 0)
103 		return err;
104 
105     err = snd_pcm_hw_params_set_format (pcm, hw_params, format);
106     if (err < 0)
107 		return err;
108 
109     err = snd_pcm_hw_params_set_channels (pcm, hw_params, channels);
110     if (err < 0)
111 		return err;
112 
113     err = snd_pcm_hw_params_set_rate_near (pcm, hw_params, rate, 0);
114     if (err < 0)
115 		return err;
116 
117     err = snd_pcm_hw_params_set_buffer_time_near (pcm, hw_params, buffer_time, NULL);
118     if (err < 0)
119 		return err;
120 
121     snd_pcm_hw_params_get_buffer_size (hw_params, buffer_frames);
122     err = snd_pcm_hw_params_set_periods_near (pcm, hw_params, &periods, NULL);
123     if (err < 0)
124        return err;
125     if (periods == 1)
126 		return -EINVAL;
127     err = snd_pcm_hw_params(pcm, hw_params);
128     snd_pcm_hw_params_get_period_size (hw_params, period_frames, NULL);
129     return 0;
130 }
131 
set_sw_params(snd_pcm_t * pcm,snd_pcm_sw_params_t * sw_params,snd_pcm_uframes_t buffer_frames,snd_pcm_uframes_t period_frames)132 static int set_sw_params(snd_pcm_t *pcm,
133 			 snd_pcm_sw_params_t *sw_params,
134 			 snd_pcm_uframes_t buffer_frames,
135 			 snd_pcm_uframes_t period_frames)
136 {
137     int err;
138 
139     err = snd_pcm_sw_params_current (pcm, sw_params);
140     if (err < 0)
141 		return err;
142 
143     err = snd_pcm_sw_params_set_start_threshold(pcm, sw_params, (buffer_frames / period_frames) * period_frames);
144     if (err < 0)
145 		return err;
146 
147     err = snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_frames);
148     if (err < 0)
149 		return err;
150 
151     err = snd_pcm_sw_params_set_stop_threshold(pcm, sw_params, buffer_frames);
152     if (err < 0)
153 		return err;
154 
155 	/* deprecated. No longer needed? There doesn't seem to be a substitute.
156     err = snd_pcm_sw_params_set_xfer_align(pcm, sw_params, 1);
157     if (err < 0)
158 		return err;
159 	*/
160 
161     err = snd_pcm_sw_params(pcm, sw_params);
162     if (err < 0)
163 		return err;
164     return 0;
165 }
166 
init_sound(void)167 int init_sound (void)
168 {
169     unsigned int rate;
170     snd_pcm_format_t format;
171     unsigned int channels;
172     unsigned int dspbits;
173 
174     snd_pcm_hw_params_t *hw_params = 0;
175     snd_pcm_sw_params_t *sw_params = 0;
176     snd_pcm_uframes_t    buffer_frames;
177     snd_pcm_uframes_t    period_frames;
178     unsigned int         buffer_time;
179 
180     snd_output_t *alsa_out;
181 
182     int err;
183 
184     snd_output_stdio_attach (&alsa_out, stderr, 0);
185 
186     dspbits  = 16;
187     channels = currprefs.sound_stereo ? 2 : 1;
188     rate     = currprefs.sound_freq;
189 
190 	have_sound = 0;
191 	alsa_playback_handle = 0;
192 	printf("ALSA lib version: %s\n", SND_LIB_VERSION_STR);
193     if ((err = open_sound()) < 0) {
194 		write_log ("ALSA: Can't open audio device: %s\n", snd_strerror (err));
195 		goto nosound;
196     }
197 
198     /* this is no longer configurable ? - Sven
199     buffer_time = currprefs.sound_latency * 1000;
200     if (buffer_time < 1000 || buffer_time > 500000)
201 		buffer_time = 100000;
202 	*/
203 	buffer_time = 100000;
204 
205     if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
206 		write_log ("Cannot allocate hardware parameter structure: %s.\n", snd_strerror (err));
207 		goto nosound;
208     }
209     if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) {
210 		write_log ("Cannot allocate software parameter structure: %s.\n", snd_strerror (err));
211 		goto nosound;
212     }
213 
214     switch (dspbits) {
215 		case 8:
216 		    format = SND_PCM_FORMAT_S8;
217 		    break;
218 		case 16:
219 		    format = SND_PCM_FORMAT_S16;
220 		    break;
221 		default:
222 		    write_log ("%d-bit samples not supported by UAE.\n", dspbits);
223 		    goto nosound;
224     }
225 
226     bytes_per_frame = dspbits / 8 * channels;
227 
228     if ((err = set_hw_params (alsa_playback_handle, hw_params, &rate, channels, format, &buffer_time, &buffer_frames, &period_frames)) < 0) {
229 		write_log ("Cannot set hw parameters: %s.\n", snd_strerror (err));
230 		goto nosound;
231     }
232 
233     if ((err = set_sw_params (alsa_playback_handle, sw_params, buffer_frames, period_frames)) < 0) {
234 		write_log ("Cannot set sw parameters: %s.\n", snd_strerror (err));
235 		goto nosound;
236     }
237 
238     paula_sndbufsize = period_frames * bytes_per_frame;
239     snd_pcm_hw_params_free (hw_params);
240     snd_pcm_sw_params_free (sw_params);
241 
242     if ((err = snd_pcm_prepare (alsa_playback_handle)) < 0) {
243 		write_log ("Cannot prepare audio interface for use: %s.\n", snd_strerror (err));
244 		goto nosound;
245     }
246 
247     obtainedfreq = currprefs.sound_freq;
248 
249     sample_handler = currprefs.sound_stereo ? sample16s_handler : sample16_handler;
250 
251     have_sound = 1;
252     sound_available = 1;
253 
254     write_log ("ALSA: Using device '%s'.\n", alsa_device);
255     write_log ("ALSA: Sound configured for %d bits at %d Hz. Buffer length is %u us, period size %lu bytes.\n",
256 	       dspbits, rate, buffer_time, (unsigned long)(period_frames * bytes_per_frame) );
257 
258     if (alsa_verbose)
259 		snd_pcm_dump (alsa_playback_handle, alsa_out);
260 
261     paula_sndbufpt = paula_sndbuffer;
262 
263     return 1;
264 
265  nosound:
266     have_sound = 0;
267     if (hw_params)
268 		snd_pcm_hw_params_free (hw_params);
269     if (sw_params)
270 		snd_pcm_sw_params_free (sw_params);
271 
272     close_sound ();
273     return 0;
274 }
275 
reset_sound(void)276 void reset_sound (void)
277 {
278 }
279 
pause_sound(void)280 void pause_sound (void)
281 {
282     if (alsa_playback_handle)
283 		snd_pcm_drop (alsa_playback_handle);
284 }
285 
resume_sound(void)286 void resume_sound (void)
287 {
288     if (alsa_playback_handle)
289 		snd_pcm_prepare (alsa_playback_handle);
290 }
291 
sound_volume(int dir)292 void sound_volume (int dir)
293 {
294 }
295 
restart_sound_buffer(void)296 void restart_sound_buffer (void)
297 {
298 }
299 
300 /*
301  * Handle audio specific cfgfile options
302  */
audio_default_options(struct uae_prefs * p)303 void audio_default_options (struct uae_prefs *p)
304 {
305     strncpy (alsa_device, "default", 256);
306     alsa_verbose = false;
307 }
308 
audio_save_options(struct zfile * f,const struct uae_prefs * p)309 void audio_save_options (struct zfile *f, const struct uae_prefs *p)
310 {
311     cfgfile_write (f, "alsa.device=%s\n", alsa_device);
312     cfgfile_write (f, "alsa.verbose=%s\n", alsa_verbose ? "true" : "false");
313 }
314 
audio_parse_option(struct uae_prefs * p,const char * option,const char * value)315 int audio_parse_option (struct uae_prefs *p, const char *option, const char *value)
316 {
317     return (cfgfile_string (option, value, "device",   alsa_device, 256)
318 	 || cfgfile_yesno  (option, value, "verbose", &alsa_verbose));
319 }
320 
set_volume_sound_device(struct sound_data * sd,int volume,int mute)321 void set_volume_sound_device (struct sound_data *sd, int volume, int mute)
322 {
323 }
324 
set_volume(int volume,int mute)325 void set_volume (int volume, int mute)
326 {
327         set_volume_sound_device (sdp, volume, mute);
328         config_changed = 1;
329 }
330 
setget_master_volume_linux(int setvolume,int * volume,int * mute)331 static int setget_master_volume_linux (int setvolume, int *volume, int *mute)
332 {
333         unsigned int ok = 0;
334 
335         if (setvolume) {
336                 ;//set
337         } else {
338                 ;//get
339         }
340 
341         return ok;
342 }
343 
set_master_volume(int volume,int mute)344 static int set_master_volume (int volume, int mute)
345 {
346         return setget_master_volume_linux (1, &volume, &mute);
347 }
348 
get_master_volume(int * volume,int * mute)349 static int get_master_volume (int *volume, int *mute)
350 {
351         *volume = 0;
352         *mute = 0;
353         return setget_master_volume_linux (0, volume, mute);
354 }
355 
master_sound_volume(int dir)356 void master_sound_volume (int dir)
357 {
358         int vol, mute, r;
359 
360         r = get_master_volume (&vol, &mute);
361         if (!r)
362                 return;
363         if (dir == 0)
364                 mute = mute ? 0 : 1;
365         vol += dir * (65536 / 10);
366         if (vol < 0)
367                 vol = 0;
368         if (vol > 65535)
369                 vol = 65535;
370         set_master_volume (vol, mute);
371         config_changed = 1;
372 }
373 
sound_mute(int newmute)374 void sound_mute (int newmute)
375 {
376         if (newmute < 0)
377                 sdp->mute = sdp->mute ? 0 : 1;
378         else
379                 sdp->mute = newmute;
380         set_volume (currprefs.sound_volume, sdp->mute);
381         config_changed = 1;
382 }
383 
384