|
|
1.1.1.2 root 1: /*
1.1 root 2: * UAE - The Un*x Amiga Emulator
1.1.1.2 root 3: *
1.1 root 4: * Support for Linux/ALSA sound
1.1.1.2 root 5: *
1.1 root 6: * Copyright 1997 Bernd Schmidt
7: * Copyright 2004 Heikki Orsila
1.1.1.4 root 8: * Copyright 2006-2007 Richard Drummond
1.1 root 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 "options.h"
1.1.1.4 root 20: #include "gensound.h"
1.1 root 21: #include "memory.h"
22: #include "events.h"
23: #include "custom.h"
1.1.1.4 root 24: #include "newcpu.h"
1.1 root 25: #include "sounddep/sound.h"
1.1.1.4 root 26: #include "threaddep/thread.h"
1.1 root 27:
28: #include <alsa/asoundlib.h>
29:
1.1.1.4 root 30: static smp_comm_pipe to_sound_pipe;
31: static uae_sem_t sound_comm_sem;
32:
33: static char alsa_device[256] = "default";
34: static int alsa_verbose = 0;
35:
36: static int have_sound = 0, have_thread = 0;
37: static int dont_block;
1.1 root 38:
1.1.1.4 root 39: static int which_buffer;
40: static uae_u16 sndbuffer[2][44100];
41: uae_u16 *sndbufpt, *sndbuf_base;
1.1 root 42: int sndbufsize;
43:
1.1.1.4 root 44: static snd_pcm_t *alsa_playback_handle = 0;
45: static int alsa_to_frames_divisor;
46: static snd_pcm_uframes_t period_frames;
47:
48: /* alsa_xrun_recovery() function is copied from ALSA manual. why the hell did
49: * they make ALSA this hard?! i bet 95% of ALSA programmers would like a
50: * simpler way to do error handling.. let the 5% use tricky APIs. */
51: static int alsa_xrun_recovery (snd_pcm_t *handle, int err)
52: {
53: if (err == -EPIPE) {
54: /* under-run */
55: err = snd_pcm_prepare (handle);
56: if (err < 0)
57: fprintf (stderr, "uae: no recovery with alsa from underrun, prepare failed: %s\n", snd_strerror (err));
58: return 0;
59: } else if (err == -ESTRPIPE) {
60: while ((err = snd_pcm_resume (handle)) == -EAGAIN) {
61: /* wait until the suspend flag is released */
62: fprintf (stderr, "uae: sleeping for alsa.\n");
63: sleep (1);
64: }
65: if (err < 0) {
66: err = snd_pcm_prepare (handle);
67: if (err < 0)
68: fprintf (stderr, "uae: no recovery with alsa from suspend, prepare failed: %s\n", snd_strerror (err));
69: }
70: return 0;
71: }
72: return err;
73: }
74:
75: static void write_sound_frames (uae_u16 *bufbase)
76: {
77: char *buf = (char *) bufbase;
78: int ret;
79:
80: int frames = period_frames;
81: while (frames > 0) {
82: ret = snd_pcm_writei (alsa_playback_handle, buf, frames);
83: if (ret < 0) {
84: if (ret == -EAGAIN || ret == -EINTR)
85: continue;
86: if (alsa_xrun_recovery (alsa_playback_handle, ret) < 0) {
87: fprintf (stderr, "uae: write error with alsa: %s\n", snd_strerror (ret));
88: exit (-1);
89: }
90: continue;
91: }
92: frames -= ret;
93: buf += ret * alsa_to_frames_divisor;
94: }
95: }
96:
97: void finish_sound_buffers ()
98: {
99: dont_block = currprefs.m68k_speed == -1 && (!regs.stopped || active_fs_packets > 0);
100: if (!dont_block) {
101: write_sound_frames (sndbuf_base);
102: } else {
103: write_comm_pipe_int (&to_sound_pipe, 2, 1);
104: uae_sem_wait (&sound_comm_sem);
105: }
106:
107: sndbufpt = sndbuf_base = sndbuffer[which_buffer ^ 1];
108: }
1.1 root 109:
110: void close_sound (void)
111: {
1.1.1.4 root 112: sync_with_sound = 0;
113: if (alsa_playback_handle) {
114: snd_pcm_close (alsa_playback_handle);
115: alsa_playback_handle = 0;
116: }
117: if (have_thread) {
118: write_comm_pipe_int (&to_sound_pipe, 1, 1);
119: uae_sem_wait (&sound_comm_sem);
120: uae_sem_destroy (&sound_comm_sem);
121: have_thread = 0;
122: }
1.1 root 123: }
124:
1.1.1.4 root 125: static int open_sound_device (void)
1.1 root 126: {
1.1.1.4 root 127: return snd_pcm_open (&alsa_playback_handle, alsa_device, SND_PCM_STREAM_PLAYBACK, 0);
1.1 root 128: }
129:
130: /* Try to determine whether sound is available. This is only for GUI purposes. */
131: int setup_sound (void)
132: {
1.1.1.4 root 133: int err;
134: sound_available = 0;
135: if ((err = open_sound_device ()) < 0) {
136: /* TODO: if the pcm was busy, we should the same as sd-uss does.
137: tell the caller that sound is available. in any other
138: condition we should just return 0. */
139: write_log ("Cannot open audio device: %s.\n", snd_strerror (err));
140: return 0;
141: }
142: snd_pcm_close (alsa_playback_handle);
143: alsa_playback_handle = 0;
144: sound_available = 1;
145: return 1;
146: }
147:
148: static int set_hw_params (snd_pcm_t *pcm, snd_pcm_hw_params_t *hw_params,
149: unsigned int *rate, unsigned int channels,
150: snd_pcm_format_t format, unsigned int *buffer_time,
151: snd_pcm_uframes_t *buffer_frames,
152: snd_pcm_uframes_t *per_frames)
153: {
154: int err;
155: unsigned int periods = 2;
156:
157: err = snd_pcm_hw_params_any (pcm, hw_params);
158: if (err < 0)
159: return err;
160: err = snd_pcm_hw_params_set_access (pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
161: if (err < 0)
162: return err;
163: err = snd_pcm_hw_params_set_format (pcm, hw_params, format);
164: if (err < 0)
165: return err;
166: err = snd_pcm_hw_params_set_channels (pcm, hw_params, channels);
167: if (err < 0)
168: return err;
169: err = snd_pcm_hw_params_set_rate_near (pcm, hw_params, rate, 0);
170: if (err < 0)
171: return err;
172: err = snd_pcm_hw_params_set_buffer_time_near (pcm, hw_params, buffer_time, NULL);
173: if (err < 0)
174: return err;
175: snd_pcm_hw_params_get_buffer_size (hw_params, buffer_frames);
176: err = snd_pcm_hw_params_set_periods_near (pcm, hw_params, &periods, NULL);
177: if (err < 0)
178: return err;
179: if (periods == 1)
180: return -EINVAL;
181: err = snd_pcm_hw_params (pcm, hw_params);
182:
183: snd_pcm_hw_params_get_period_size (hw_params, per_frames, NULL);
184: return 0;
185: }
186:
187: static int set_sw_params (snd_pcm_t * pcm,
188: snd_pcm_sw_params_t * sw_params, snd_pcm_uframes_t buffer_frames, snd_pcm_uframes_t period_frames)
189: {
190: int err;
191:
192: err = snd_pcm_sw_params_current (pcm, sw_params);
193: if (err < 0)
194: return err;
195: err = snd_pcm_sw_params_set_start_threshold (pcm, sw_params, (buffer_frames / period_frames) * period_frames);
196: if (err < 0)
197: return err;
198: err = snd_pcm_sw_params_set_avail_min (pcm, sw_params, period_frames);
199: if (err < 0)
200: return err;
201: err = snd_pcm_sw_params_set_stop_threshold (pcm, sw_params, buffer_frames);
202: if (err < 0)
203: return err;
204: err = snd_pcm_sw_params_set_xfer_align (pcm, sw_params, 1);
205: if (err < 0)
206: return err;
207: err = snd_pcm_sw_params (pcm, sw_params);
208: if (err < 0)
209: return err;
1.1 root 210: return 0;
1.1.1.4 root 211: }
212:
213: static void open_sound (void)
214: {
215: unsigned int rate;
216: snd_pcm_format_t format;
217: unsigned int channels;
218:
219: snd_pcm_hw_params_t *hw_params = 0;
220: snd_pcm_sw_params_t *sw_params = 0;
221: snd_pcm_uframes_t buffer_frames;
222: unsigned int buffer_time;
223:
224: snd_output_t *alsa_out;
225:
226: int err;
227:
228: sync_with_sound = 0;
229:
230: snd_output_stdio_attach (&alsa_out, stderr, 0);
231:
1.1.1.5 ! root 232: channels = 2;
1.1.1.4 root 233: rate = currprefs.sound_freq;
234:
235: have_sound = 0;
236: alsa_playback_handle = 0;
237: if ((err = open_sound_device ()) < 0) {
238: write_log ("Cannot open audio device: %s\n", snd_strerror (err));
239: goto nosound;
240: }
241:
242: buffer_time = currprefs.sound_maxbsiz * 1000;
243:
244: while (buffer_time / (rate * 2 * channels) < 6)
245: buffer_time *= 2;
246: if (buffer_time != currprefs.sound_maxbsiz * 1000) {
247: fprintf (stderr, "Increasing sound buffer size to sane minimum of %d bytes.\n",
248: buffer_time / 1000);
249: }
250: if (buffer_time < 1000 || buffer_time > 500000)
251: buffer_time = 100000;
252:
253:
254: if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
255: write_log ("Cannot allocate hardware parameter structure: %s.\n", snd_strerror (err));
256: goto nosound;
257: }
258: if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) {
259: write_log ("Cannot allocate software parameter structure: %s.\n", snd_strerror (err));
260: goto nosound;
261: }
262:
263: format = SND_PCM_FORMAT_S16;
264:
265: alsa_to_frames_divisor = 2 * channels;
266:
267: if ((err =
268: set_hw_params (alsa_playback_handle, hw_params, &rate, channels, format, &buffer_time, &buffer_frames,
269: &period_frames)) < 0) {
270: write_log ("Cannot set hw parameters: %s.\n", snd_strerror (err));
271: goto nosound;
272: }
273:
274: if ((err = set_sw_params (alsa_playback_handle, sw_params, buffer_frames, period_frames)) < 0) {
275: write_log ("Cannot set sw parameters: %s.\n", snd_strerror (err));
276: goto nosound;
277: }
278:
279: sndbufsize = period_frames * alsa_to_frames_divisor;
280: snd_pcm_hw_params_free (hw_params);
281: snd_pcm_sw_params_free (sw_params);
282:
283: if ((err = snd_pcm_prepare (alsa_playback_handle)) < 0) {
284: write_log ("Cannot prepare audio interface for use: %s.\n", snd_strerror (err));
285: goto nosound;
286: }
287:
288: obtainedfreq = rate;
289:
290: init_sound_table16 ();
1.1.1.5 ! root 291: sample_handler = sample16s_handler;
1.1.1.4 root 292:
293: have_sound = 1;
294: sound_available = 1;
295:
296: write_log ("ALSA: Using device '%s'.\n", alsa_device);
297: write_log ("ALSA: Sound configured for %d Hz. Buffer length is %u us, period size %d bytes.\n",
298: rate, buffer_time, period_frames * alsa_to_frames_divisor);
299:
300: if (alsa_verbose)
301: snd_pcm_dump (alsa_playback_handle, alsa_out);
302:
303: sndbufpt = sndbuf_base = sndbuffer[which_buffer = 0];
304:
305: sync_with_sound = 1;
306: return;
307:
308: nosound:
309: have_sound = 0;
310: if (hw_params)
311: snd_pcm_hw_params_free (hw_params);
312: if (sw_params)
313: snd_pcm_sw_params_free (sw_params);
314:
315: close_sound ();
316: }
317:
318: #if 0
319: /*
320: * Handle audio specific cfgfile options
321: */
322: void audio_default_options (struct uae_prefs *p)
323: {
324: strncpy (alsa_device, "default", 256);
325: alsa_verbose = 1;
326: }
327:
328: void audio_save_options (FILE * f, const struct uae_prefs *p)
329: {
330: cfgfile_write (f, "alsa.device=%s\n", alsa_device);
331: cfgfile_write (f, "alsa.verbose=%s\n", alsa_verbose ? "true" : "false");
332: }
333:
334: int audio_parse_option (struct uae_prefs *p, const char *option, const char *value)
335: {
336: return (cfgfile_string (option, value, "device", alsa_device, 256)
337: || cfgfile_yesno (option, value, "verbose", &alsa_verbose));
338: }
339: #endif
340:
341: static void *sound_thread (void *dummy)
342: {
343: for (;;) {
344: int cmd = read_comm_pipe_int_blocking (&to_sound_pipe);
345: int n;
346:
347: switch (cmd) {
348: case 0:
349: open_sound ();
350: uae_sem_post (&sound_comm_sem);
351: break;
352: case 1:
353: uae_sem_post (&sound_comm_sem);
354: return 0;
355: case 2:
356: /* If trying for maximum CPU speed, don't block the main
357: thread, instead set the delaying_for_sound variable. If
358: not trying for maximum CPU speed, synchronize here by
359: delaying the sem_post until after the write. */
360: delaying_for_sound = dont_block;
361: if (dont_block)
362: uae_sem_post (&sound_comm_sem);
363:
364: write_sound_frames (sndbuf_base);
365: if (!dont_block)
366: uae_sem_post (&sound_comm_sem);
367:
368: delaying_for_sound = 0;
369: break;
370: }
371: }
372: }
373:
374: /* We use a thread so that we can use the time spent waiting for the sound
375: driver for executing m68k instructions, rather than just blocking. */
376: static void init_sound_thread (void)
377: {
378: uae_thread_id tid;
379:
380: init_comm_pipe (&to_sound_pipe, 20, 1);
381: uae_sem_init (&sound_comm_sem, 0, 0);
382: uae_start_thread (sound_thread, NULL, &tid);
383: have_thread = 1;
1.1 root 384: }
385:
386: int init_sound (void)
387: {
1.1.1.4 root 388: init_sound_thread ();
389: write_comm_pipe_int (&to_sound_pipe, 0, 1);
390: uae_sem_wait (&sound_comm_sem);
391:
392: return have_sound;
1.1 root 393: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.