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