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