1 /*****************************************************************************\
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3 This file is licensed under the Snes9x License.
4 For further information, consult the LICENSE file in the root directory.
5 \*****************************************************************************/
6
7 #include "gtk_sound_driver_oss.h"
8 #include "gtk_s9x.h"
9
10 #include <fcntl.h>
11 #include <sys/ioctl.h>
12 #include <sys/soundcard.h>
13 #include <sys/time.h>
14
oss_samples_available(void * data)15 static void oss_samples_available(void *data)
16 {
17 ((S9xOSSSoundDriver *)data)->samples_available();
18 }
19
S9xOSSSoundDriver()20 S9xOSSSoundDriver::S9xOSSSoundDriver()
21 {
22 filedes = -1;
23 sound_buffer = NULL;
24 sound_buffer_size = 0;
25 }
26
init()27 void S9xOSSSoundDriver::init()
28 {
29 }
30
terminate()31 void S9xOSSSoundDriver::terminate()
32 {
33 stop();
34
35 S9xSetSamplesAvailableCallback(NULL, NULL);
36
37 if (filedes >= 0)
38 {
39 close(filedes);
40 }
41
42 if (sound_buffer)
43 {
44 free(sound_buffer);
45 sound_buffer = NULL;
46 }
47 }
48
start()49 void S9xOSSSoundDriver::start()
50 {
51 }
52
stop()53 void S9xOSSSoundDriver::stop()
54 {
55 }
56
open_device()57 bool S9xOSSSoundDriver::open_device()
58 {
59 int temp;
60 audio_buf_info info;
61
62 output_buffer_size = (gui_config->sound_buffer_size * Settings.SoundPlaybackRate) / 1000;
63
64 output_buffer_size *= 4;
65 if (output_buffer_size < 256)
66 output_buffer_size = 256;
67
68 printf("OSS sound driver initializing...\n");
69
70 printf("Device: /dev/dsp: ");
71
72 filedes = open("/dev/dsp", O_WRONLY | O_NONBLOCK);
73
74 if (filedes < 0)
75 {
76 printf("Failed.\n");
77 char dspstring[16] = "/dev/dspX\0";
78
79 for (int i = 1; i <= 9; i++)
80 {
81 dspstring[8] = '0' + i;
82
83 printf("Trying %s: ", dspstring);
84
85 filedes = open(dspstring, O_WRONLY | O_NONBLOCK);
86
87 if (filedes < 0)
88 {
89 if (i == 9)
90 goto fail;
91 printf("Failed.\n");
92 }
93 else
94 break;
95 }
96 }
97
98 printf("OK\n");
99
100 printf(" --> (Format: 16-bit)...");
101
102 temp = AFMT_S16_LE;
103 if (ioctl(filedes, SNDCTL_DSP_SETFMT, &temp) < 0)
104 goto close_fail;
105
106 printf("OK\n");
107
108 temp = 2;
109 printf(" --> (Stereo)...");
110
111 if (ioctl(filedes, SNDCTL_DSP_CHANNELS, &temp) < 0)
112 goto close_fail;
113
114 printf("OK\n");
115
116 printf(" --> (Frequency: %d)...", Settings.SoundPlaybackRate);
117 if (ioctl(filedes, SNDCTL_DSP_SPEED, &Settings.SoundPlaybackRate) < 0)
118 goto close_fail;
119
120 printf("OK\n");
121
122 /* OSS requires a power-of-two buffer size, first 16 bits are the number
123 * of fragments to generate, second 16 are the respective power-of-two. */
124 temp = (4 << 16) | (S9xSoundBase2log(output_buffer_size / 4));
125
126 if (ioctl(filedes, SNDCTL_DSP_SETFRAGMENT, &temp) < 0)
127 goto close_fail;
128
129 ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
130
131 output_buffer_size = info.fragsize * info.fragstotal;
132
133 printf(" --> (Buffer size: %d bytes, %dms latency)...",
134 output_buffer_size,
135 (output_buffer_size * 250) / Settings.SoundPlaybackRate);
136
137 printf("OK\n");
138
139 S9xSetSamplesAvailableCallback(oss_samples_available, this);
140
141 return true;
142
143 close_fail:
144
145 close(filedes);
146
147 fail:
148 printf("failed\n");
149
150 return false;
151 }
152
samples_available()153 void S9xOSSSoundDriver::samples_available()
154 {
155 audio_buf_info info;
156 int samples_to_write;
157 int bytes_to_write;
158 int bytes_written;
159
160 ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
161
162 if (Settings.DynamicRateControl)
163 {
164 S9xUpdateDynamicRate(info.bytes, output_buffer_size);
165 }
166
167 samples_to_write = S9xGetSampleCount();
168
169 if (Settings.DynamicRateControl && !Settings.SoundSync)
170 {
171 // Using rate control, we should always keep the emulator's sound buffers empty to
172 // maintain an accurate measurement.
173 if (samples_to_write > (info.bytes >> 1))
174 {
175 S9xClearSamples();
176 return;
177 }
178 }
179
180 if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
181 {
182 while (info.bytes >> 1 < samples_to_write)
183 {
184 int usec_to_sleep = ((samples_to_write >> 1) - (info.bytes >> 2)) * 10000 /
185 (Settings.SoundPlaybackRate / 100);
186 usleep(usec_to_sleep > 0 ? usec_to_sleep : 0);
187 ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
188 }
189 }
190 else
191 {
192 samples_to_write = MIN(info.bytes >> 1, samples_to_write) & ~1;
193 }
194
195 if (samples_to_write < 0)
196 return;
197
198 if (sound_buffer_size < samples_to_write * 2)
199 {
200 sound_buffer = (uint8 *)realloc(sound_buffer, samples_to_write * 2);
201 sound_buffer_size = samples_to_write * 2;
202 }
203
204 S9xMixSamples(sound_buffer, samples_to_write);
205
206 bytes_written = 0;
207 bytes_to_write = samples_to_write * 2;
208
209 while (bytes_to_write > bytes_written)
210 {
211 int result;
212
213 result = write(filedes,
214 ((char *)sound_buffer) + bytes_written,
215 bytes_to_write - bytes_written);
216
217 if (result < 0)
218 break;
219
220 bytes_written += result;
221 }
222 }
223