1  /*
2   * UAE - The Un*x Amiga Emulator
3   *
4   * Support for Linux/USS sound
5   *
6   * Copyright 1997 Bernd Schmidt
7   */
8 
9 #include "sysconfig.h"
10 #include "sysdeps.h"
11 
12 #include "options.h"
13 #include "memory.h"
14 #include "events.h"
15 #include "custom.h"
16 #include "newcpu.h"
17 #include "gensound.h"
18 #include "sounddep/sound.h"
19 #include "threaddep/thread.h"
20 #include "SDL_audio.h"
21 
22 int sound_fd;
23 static int have_sound = 0;
24 static unsigned long formats;
25 
26 uae_u16 sndbuffer[2][44100];
27 uae_u16 *sndbufpt, *sndbuf_base, *callback_sndbuf;
28 int which_buffer;
29 int sndbufsize;
30 static SDL_AudioSpec spec;
31 
32 static smp_comm_pipe to_sound_pipe;
33 static uae_sem_t data_available_sem, callback_done_sem, sound_init_sem;
34 
35 static int dont_block;
36 
37 static int in_callback, closing_sound;
38 
sound_callback(void * userdata,Uint8 * stream,int len)39 static void sound_callback (void *userdata, Uint8 *stream, int len)
40 {
41     if (closing_sound)
42 	return;
43     in_callback = 1;
44     delaying_for_sound = 0;
45 
46     /* Wait for data to finish.  */
47     uae_sem_wait (&data_available_sem);
48     if (! closing_sound) {
49 	memcpy (stream, callback_sndbuf, sndbufsize);
50 
51 	/* Notify writer that we're done.  */
52 	if (!dont_block)
53 	    uae_sem_post (&callback_done_sem);
54     }
55     in_callback = 0;
56 }
57 
finish_sound_buffer(void)58 void finish_sound_buffer (void)
59 {
60     dont_block = currprefs.m68k_speed == -1 && (!regs.stopped || active_fs_packets > 0);
61     callback_sndbuf = sndbuf_base;
62 
63     if (dont_block)
64 	delaying_for_sound = 1;
65     uae_sem_post (&data_available_sem);
66     if (!dont_block)
67 	uae_sem_wait (&callback_done_sem);
68     sndbufpt = sndbuf_base = sndbuffer[which_buffer ^= 1];
69 }
70 
71 /* Try to determine whether sound is available.  This is only for GUI purposes.  */
setup_sound(void)72 int setup_sound (void)
73 {
74     int size = currprefs.sound_maxbsiz;
75 
76     spec.freq = currprefs.sound_freq;
77     spec.format = AUDIO_S16;
78     spec.channels = 2;
79     size >>= spec.channels - 1;
80     size >>= 1;
81     while (size & (size - 1))
82 	size &= size - 1;
83     if (size < 512)
84 	size = 512;
85     spec.samples = size;
86     spec.callback = sound_callback;
87     spec.userdata = 0;
88 
89     if (SDL_OpenAudio (&spec, NULL) < 0) {
90 	write_log ("Couldn't open audio: %s\n", SDL_GetError());
91 	return 0;
92     }
93     sound_available = 1;
94     SDL_CloseAudio ();
95     return 1;
96 }
97 
open_sound(void)98 static int open_sound (void)
99 {
100     SDL_AudioSpec obtained;
101     int size = currprefs.sound_maxbsiz;
102 
103     sync_with_sound = 0;
104 
105     spec.freq = currprefs.sound_freq;
106     spec.format = AUDIO_S16;
107     spec.channels = 2;
108 
109     /* Always interpret buffer size as number of samples, not as actual
110        buffer size.  Of course, since 8192 is the default, we'll have to
111        scale that to a sane value (assuming that otherwise 16 bits and
112        stereo would have been enabled and we'd have done the shift by
113        two anyway).  */
114     size >>= spec.channels;
115 
116     while (size & (size - 1))
117 	size &= size - 1;
118     if (size < 512)
119 	size = 512;
120     spec.samples = size;
121     spec.callback = sound_callback;
122     spec.userdata = 0;
123 
124     if (SDL_OpenAudio (&spec, &obtained) < 0) {
125 	write_log ("Couldn't open audio: %s\n", SDL_GetError());
126 	return 0;
127     }
128     have_sound = 1;
129 
130     obtainedfreq = obtained.freq;
131 
132     init_sound_table16 ();
133     sample_handler = sample16s_handler;
134 
135     sound_available = 1;
136     sndbufpt = sndbuf_base = sndbuffer[which_buffer = 0];
137     sndbufsize = size * 2 * obtained.channels;
138     write_log ("SDL sound driver found and configured at %d Hz, buffer is %d samples (%d ms).\n",
139 	       obtainedfreq, obtained.samples, obtained.samples * 1000 / obtainedfreq);
140     sync_with_sound = 1;
141     return 1;
142 }
143 
sound_thread(void * dummy)144 static void *sound_thread (void *dummy)
145 {
146     for (;;) {
147 	int cmd = read_comm_pipe_int_blocking (&to_sound_pipe);
148 	int n;
149 
150 	switch (cmd) {
151 	case 0:
152 	    open_sound ();
153 	    uae_sem_post (&sound_init_sem);
154 	    break;
155 	case 1:
156 	    uae_sem_post (&sound_init_sem);
157 	    return 0;
158 	}
159     }
160 }
161 
162 /* We need a thread for this, since communication between finish_sound_buffer
163    and the callback works through semaphores.  In theory, this is unnecessary,
164    since SDL uses a sound thread internally, and the callback runs in its
165    context.  But we don't want to depend on SDL's internals too much.  */
init_sound_thread(void)166 static void init_sound_thread (void)
167 {
168     uae_thread_id tid;
169 
170     init_comm_pipe (&to_sound_pipe, 20, 1);
171     uae_sem_init (&data_available_sem, 0, 0);
172     uae_sem_init (&callback_done_sem, 0, 0);
173     uae_sem_init (&sound_init_sem, 0, 0);
174     uae_start_thread (sound_thread, NULL, &tid);
175 }
176 
close_sound(void)177 void close_sound (void)
178 {
179     sync_with_sound = 0;
180 
181     if (! have_sound)
182 	return;
183 
184     SDL_PauseAudio (1);
185     if (in_callback) {
186 	closing_sound = 1;
187 	uae_sem_post (&data_available_sem);
188     }
189     write_comm_pipe_int (&to_sound_pipe, 1, 1);
190     uae_sem_wait (&sound_init_sem);
191     SDL_CloseAudio ();
192     uae_sem_destroy (&data_available_sem);
193     uae_sem_destroy (&sound_init_sem);
194     uae_sem_destroy (&callback_done_sem);
195     have_sound = 0;
196 }
197 
init_sound(void)198 int init_sound (void)
199 {
200     in_callback = 0;
201     closing_sound = 0;
202 
203     init_sound_thread ();
204     write_comm_pipe_int (&to_sound_pipe, 0, 1);
205     uae_sem_wait (&sound_init_sem);
206     SDL_PauseAudio (0);
207 
208     return have_sound;
209 }
210 
pause_sound(void)211 void pause_sound (void)
212 {
213     SDL_PauseAudio (1);
214 }
215 
resume_sound(void)216 void resume_sound (void)
217 {
218     SDL_PauseAudio (0);
219 }
220