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
21 #include <sys/ioctl.h>
22
23 #ifdef HAVE_SYS_SOUNDCARD_H
24 #include <sys/soundcard.h>
25 #elif defined HAVE_MACHINE_SOUNDCARD_H
26 #include <machine/soundcard.h>
27 #else
28 #error "Something went wrong during configuration."
29 #endif
30
31 static smp_comm_pipe to_sound_pipe;
32 static uae_sem_t sound_comm_sem;
33
34 static int sound_fd;
35 static int have_sound = 0, have_thread = 0;
36 static int dont_block;
37 static unsigned long formats;
38
39 static int which_buffer;
40 static uae_u16 sndbuffer[2][44100];
41 uae_u16 *sndbufpt, *sndbuf_base;
42 int sndbufsize;
43
exact_log2(int v)44 static int exact_log2 (int v)
45 {
46 int l = 0;
47 while ((v >>= 1) != 0)
48 l++;
49 return l;
50 }
51
finish_sound_buffers(void)52 void finish_sound_buffers (void)
53 {
54 dont_block = currprefs.m68k_speed == -1 && (!regs.stopped || active_fs_packets > 0);
55 if (!dont_block) {
56 write (sound_fd, sndbuffer[which_buffer], sndbufsize);
57 } else {
58 write_comm_pipe_int (&to_sound_pipe, 2, 1);
59 uae_sem_wait (&sound_comm_sem);
60 }
61 sndbufpt = sndbuf_base = sndbuffer[which_buffer ^= 1];
62 }
63
close_sound(void)64 void close_sound (void)
65 {
66 sync_with_sound = 0;
67 if (have_sound)
68 close (sound_fd);
69
70 if (have_thread) {
71 write_comm_pipe_int (&to_sound_pipe, 1, 1);
72 uae_sem_wait (&sound_comm_sem);
73 uae_sem_destroy (&sound_comm_sem);
74 }
75 }
76
77 /* Try to determine whether sound is available. This is only for GUI purposes. */
setup_sound(void)78 int setup_sound (void)
79 {
80 sound_fd = open ("/dev/dsp", O_WRONLY);
81
82 sound_available = 0;
83
84 if (sound_fd < 0) {
85 perror ("Can't open /dev/dsp");
86 if (errno == EBUSY) {
87 /* We can hope, can't we ;) */
88 sound_available = 1;
89 return 1;
90 }
91 return 0;
92 }
93
94 if (ioctl (sound_fd, SNDCTL_DSP_GETFMTS, &formats) == -1) {
95 perror ("ioctl failed - can't use sound");
96 close (sound_fd);
97 return 0;
98 }
99
100 sound_available = 1;
101 close (sound_fd);
102 return 1;
103 }
104
open_sound(void)105 static void open_sound (void)
106 {
107 int tmp;
108 int rate;
109 int dspbits;
110
111 sync_with_sound = 0;
112
113 sound_fd = open ("/dev/dsp", O_WRONLY);
114 have_sound = !(sound_fd < 0);
115 if (! have_sound) {
116 perror ("Can't open /dev/dsp");
117 if (errno != EBUSY)
118 sound_available = 0;
119 return;
120 }
121 if (ioctl (sound_fd, SNDCTL_DSP_GETFMTS, &formats) == -1) {
122 perror ("ioctl failed - can't use sound");
123 goto out_err;
124 }
125
126 if (currprefs.sound_maxbsiz < 128 || currprefs.sound_maxbsiz > 16384) {
127 fprintf (stderr, "Sound buffer size %d out of range.\n", currprefs.sound_maxbsiz);
128 currprefs.sound_maxbsiz = 1024;
129 }
130
131 dspbits = 16;
132 ioctl (sound_fd, SNDCTL_DSP_SAMPLESIZE, &dspbits);
133 ioctl (sound_fd, SOUND_PCM_READ_BITS, &dspbits);
134 if (dspbits != 16) {
135 fprintf (stderr, "Can't use sound with 16 bits\n");
136 goto out_err;
137 }
138
139 tmp = 1;
140 ioctl (sound_fd, SNDCTL_DSP_STEREO, &tmp);
141
142 rate = currprefs.sound_freq;
143 ioctl (sound_fd, SNDCTL_DSP_SPEED, &rate);
144 ioctl (sound_fd, SOUND_PCM_READ_RATE, &rate);
145 /* Some soundcards have a bit of tolerance here. */
146 if (rate < currprefs.sound_freq * 90 / 100 || rate > currprefs.sound_freq * 110 / 100) {
147 fprintf (stderr, "Can't use sound with desired frequency %d\n", currprefs.sound_freq);
148 goto out_err;
149 }
150
151 tmp = 1 << exact_log2 (currprefs.sound_maxbsiz);
152 while (tmp * 1000 / (rate * 2 * 2) < 6)
153 tmp *= 2;
154 if (tmp != currprefs.sound_maxbsiz) {
155 fprintf (stderr, "Increasing sound buffer size to sane minimum of %d bytes.\n",
156 tmp);
157 }
158 tmp = 0x00040000 + exact_log2 (tmp);
159 ioctl (sound_fd, SNDCTL_DSP_SETFRAGMENT, &tmp);
160 ioctl (sound_fd, SNDCTL_DSP_GETBLKSIZE, &sndbufsize);
161
162 obtainedfreq = rate;
163
164 if (!(formats & AFMT_S16_NE))
165 goto out_err;
166 init_sound_table16 ();
167 sample_handler = sample16s_handler;
168
169 sound_available = 1;
170 printf ("Sound driver found and configured at %d Hz, buffer is %d bytes (%d ms).\n",
171 rate, sndbufsize, sndbufsize * 1000 / (rate * 2 * 2));
172 sndbufpt = sndbuf_base = sndbuffer[which_buffer = 0];
173
174 sync_with_sound = 1;
175 return;
176
177 out_err:
178 close (sound_fd);
179 have_sound = 0;
180 return;
181 }
182
sound_thread(void * dummy)183 static void *sound_thread (void *dummy)
184 {
185 uae_u16 *base;
186 for (;;) {
187 int cmd = read_comm_pipe_int_blocking (&to_sound_pipe);
188 int n;
189
190 switch (cmd) {
191 case 0:
192 open_sound ();
193 uae_sem_post (&sound_comm_sem);
194 break;
195 case 1:
196 uae_sem_post (&sound_comm_sem);
197 return 0;
198 case 2:
199 /* If trying for maximum CPU speed, don't block the main
200 thread, instead set the delaying_for_sound variable. If
201 not trying for maximum CPU speed, synchronize here by
202 delaying the sem_post until after the write. */
203 delaying_for_sound = dont_block;
204 base = sndbuf_base;
205 if (dont_block)
206 uae_sem_post (&sound_comm_sem);
207
208 write (sound_fd, sndbuf_base, sndbufsize);
209 if (!dont_block)
210 uae_sem_post (&sound_comm_sem);
211
212 delaying_for_sound = 0;
213 break;
214 }
215 }
216 }
217
218 /* We use a thread so that we can use the time spent waiting for the sound
219 driver for executing m68k instructions, rather than just blocking. */
init_sound_thread(void)220 static void init_sound_thread (void)
221 {
222 uae_thread_id tid;
223
224 init_comm_pipe (&to_sound_pipe, 20, 1);
225 uae_sem_init (&sound_comm_sem, 0, 0);
226 uae_start_thread (sound_thread, NULL, &tid);
227 have_thread = 1;
228 }
229
init_sound(void)230 int init_sound (void)
231 {
232 init_sound_thread ();
233 write_comm_pipe_int (&to_sound_pipe, 0, 1);
234 uae_sem_wait (&sound_comm_sem);
235
236 return have_sound;
237 }
238