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