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