1 /////////////////////////////////////////////////////////////////////////
2 // $Id: soundoss.cc 14181 2021-03-11 21:46:25Z vruppert $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 //  Copyright (C) 2001-2021  The Bochs Project
6 //
7 //  This library is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU Lesser General Public
9 //  License as published by the Free Software Foundation; either
10 //  version 2 of the License, or (at your option) any later version.
11 //
12 //  This library is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 //  Lesser General Public License for more details.
16 //
17 //  You should have received a copy of the GNU Lesser General Public
18 //  License along with this library; if not, write to the Free Software
19 //  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20 /////////////////////////////////////////////////////////////////////////
21 
22 // Josef Drexler coded the original version of the lowlevel sound support
23 // for Linux using OSS. The current version also supports OSS on FreeBSD.
24 
25 // Define BX_PLUGGABLE in files that can be compiled into plugins.  For
26 // platforms that require a special tag on exported symbols, BX_PLUGGABLE
27 // is used to know when we are exporting symbols and when we are importing.
28 #define BX_PLUGGABLE
29 
30 #include "bochs.h"
31 #include "plugin.h"
32 #include "pc_system.h"
33 #include "soundlow.h"
34 #include "soundmod.h"
35 #include "soundoss.h"
36 
37 #if BX_HAVE_SOUND_OSS && BX_SUPPORT_SOUNDLOW
38 
39 #define LOG_THIS
40 
41 #include <errno.h>
42 #include <sys/ioctl.h>
43 #include <sys/soundcard.h>
44 
45 // sound driver plugin entry point
46 
PLUGIN_ENTRY_FOR_SND_MODULE(oss)47 PLUGIN_ENTRY_FOR_SND_MODULE(oss)
48 {
49   if (mode == PLUGIN_PROBE) {
50     return (int)PLUGTYPE_SND;
51   }
52   return 0; // Success
53 }
54 
55 // bx_soundlow_waveout_oss_c class implementation
56 
bx_soundlow_waveout_oss_c()57 bx_soundlow_waveout_oss_c::bx_soundlow_waveout_oss_c()
58     :bx_soundlow_waveout_c()
59 {
60   waveout_fd = -1;
61 }
62 
~bx_soundlow_waveout_oss_c()63 bx_soundlow_waveout_oss_c::~bx_soundlow_waveout_oss_c()
64 {
65   if (waveout_fd != -1) {
66     close(waveout_fd);
67     waveout_fd = -1;
68   }
69 }
70 
openwaveoutput(const char * wavedev)71 int bx_soundlow_waveout_oss_c::openwaveoutput(const char *wavedev)
72 {
73   if (waveout_fd == -1) {
74     waveout_fd = open(wavedev, O_WRONLY);
75     if (waveout_fd == -1) {
76       return BX_SOUNDLOW_ERR;
77     } else {
78       BX_INFO(("OSS: opened output device %s", wavedev));
79     }
80   }
81   set_pcm_params(&real_pcm_param);
82   pcm_callback_id = register_wave_callback(this, pcm_callback);
83   start_resampler_thread();
84   start_mixer_thread();
85   return BX_SOUNDLOW_OK;
86 }
87 
set_pcm_params(bx_pcm_param_t * param)88 int bx_soundlow_waveout_oss_c::set_pcm_params(bx_pcm_param_t *param)
89 {
90   int fmt, ret;
91   int frequency = param->samplerate;
92   int channels = param->channels;
93   int signeddata = param->format & 1;
94 
95   BX_DEBUG(("set_pcm_params(): %u, %u, %u, %02x", param->samplerate, param->bits,
96             param->channels, param->format));
97 
98   if (waveout_fd == -1) {
99     return BX_SOUNDLOW_ERR;
100   }
101   if (param->bits == 16) {
102     if (signeddata == 1) {
103       fmt = AFMT_S16_LE;
104     } else {
105       fmt = AFMT_U16_LE;
106     }
107   } else if (param->bits == 8) {
108     if (signeddata == 1) {
109       fmt = AFMT_S8;
110     } else {
111       fmt = AFMT_U8;
112     }
113   } else {
114     return BX_SOUNDLOW_ERR;
115   }
116   // set frequency etc.
117   ret = ioctl(waveout_fd, SNDCTL_DSP_RESET);
118   if (ret != 0)
119     BX_ERROR(("ioctl(SNDCTL_DSP_RESET): %s", strerror(errno)));
120 
121   /*
122   ret = ioctl(waveout_fd], SNDCTL_DSP_SETFRAGMENT, &fragment);
123   if (ret != 0)
124     BX_DEBUG(("ioctl(SNDCTL_DSP_SETFRAGMENT, %d): %s",
125              fragment, strerror(errno)));
126   */
127 
128   ret = ioctl(waveout_fd, SNDCTL_DSP_SETFMT, &fmt);
129   if (ret != 0) { // abort if the format is unknown, to avoid playing noise
130     BX_ERROR(("ioctl(SNDCTL_DSP_SETFMT, %d): %s",
131               fmt, strerror(errno)));
132     return BX_SOUNDLOW_ERR;
133   }
134 
135   ret = ioctl(waveout_fd, SNDCTL_DSP_CHANNELS, &channels);
136   if (ret != 0)
137     BX_ERROR(("ioctl(SNDCTL_DSP_CHANNELS, %d): %s",
138               channels, strerror(errno)));
139 
140   ret = ioctl(waveout_fd, SNDCTL_DSP_SPEED, &frequency);
141   if (ret != 0)
142     BX_ERROR(("ioctl(SNDCTL_DSP_SPEED, %d): %s",
143               frequency, strerror(errno)));
144 
145   // ioctl(wave_fd[0], SNDCTL_DSP_GETBLKSIZE, &fragment);
146   // BX_DEBUG(("current output block size is %d", fragment));
147 
148   return BX_SOUNDLOW_OK;
149 }
150 
output(int length,Bit8u data[])151 int bx_soundlow_waveout_oss_c::output(int length, Bit8u data[])
152 {
153   int odelay, delay;
154 
155   if (waveout_fd == -1) {
156     return BX_SOUNDLOW_ERR;
157   }
158   if (write(waveout_fd, data, length) == length) {
159     ioctl(waveout_fd, SNDCTL_DSP_GETODELAY, &odelay);
160     delay = odelay * 1000 / (real_pcm_param.samplerate * 4);
161     BX_MSLEEP(delay);
162     return BX_SOUNDLOW_OK;
163   } else {
164     return BX_SOUNDLOW_ERR;
165   }
166 }
167 
168 // bx_soundlow_wavein_oss_c class implementation
169 
bx_soundlow_wavein_oss_c()170 bx_soundlow_wavein_oss_c::bx_soundlow_wavein_oss_c()
171     :bx_soundlow_wavein_c()
172 {
173   wavein_fd = -1;
174 }
175 
~bx_soundlow_wavein_oss_c()176 bx_soundlow_wavein_oss_c::~bx_soundlow_wavein_oss_c()
177 {
178   if (wavein_fd != -1) {
179     close(wavein_fd);
180     wavein_fd = -1;
181   }
182 }
183 
openwaveinput(const char * wavedev,sound_record_handler_t rh)184 int bx_soundlow_wavein_oss_c::openwaveinput(const char *wavedev, sound_record_handler_t rh)
185 {
186   record_handler = rh;
187   if (rh != NULL) {
188     record_timer_index = DEV_register_timer(this, record_timer_handler, 1, 1, 0, "soundlnx");
189     // record timer: inactive, continuous, frequency variable
190   }
191   if (wavein_fd == -1) {
192     wavein_fd = open(wavedev, O_RDONLY);
193     if (wavein_fd == -1) {
194       return BX_SOUNDLOW_ERR;
195     } else {
196       BX_INFO(("OSS: opened input device %s", wavedev));
197     }
198   }
199   wavein_param.samplerate = 0;
200   return BX_SOUNDLOW_OK;
201 }
202 
startwaverecord(bx_pcm_param_t * param)203 int bx_soundlow_wavein_oss_c::startwaverecord(bx_pcm_param_t *param)
204 {
205   Bit64u timer_val;
206   Bit8u shift = 0;
207   int fmt, ret;
208   int frequency = param->samplerate;
209   int channels = param->channels;
210   int signeddata = param->format & 1;
211 
212   if (record_timer_index != BX_NULL_TIMER_HANDLE) {
213     if (param->bits == 16) shift++;
214     if (param->channels == 2) shift++;
215     record_packet_size = (param->samplerate / 10) << shift; // 0.1 sec
216     if (record_packet_size > BX_SOUNDLOW_WAVEPACKETSIZE) {
217       record_packet_size = BX_SOUNDLOW_WAVEPACKETSIZE;
218     }
219     timer_val = (Bit64u)record_packet_size * 1000000 / (param->samplerate << shift);
220     bx_pc_system.activate_timer(record_timer_index, (Bit32u)timer_val, 1);
221   }
222   if (wavein_fd == -1) {
223     return BX_SOUNDLOW_ERR;
224   } else {
225     if (memcmp(param, &wavein_param, sizeof(bx_pcm_param_t)) == 0) {
226       return BX_SOUNDLOW_OK; // nothing to do
227     }
228     wavein_param = *param;
229   }
230 
231   if (param->bits == 16) {
232     if (signeddata == 1) {
233       fmt = AFMT_S16_LE;
234     } else {
235       fmt = AFMT_U16_LE;
236     }
237   } else if (param->bits == 8) {
238     if (signeddata == 1) {
239       fmt = AFMT_S8;
240     } else {
241       fmt = AFMT_U8;
242     }
243   } else {
244     return BX_SOUNDLOW_ERR;
245   }
246 
247       // set frequency etc.
248   ret = ioctl(wavein_fd, SNDCTL_DSP_RESET);
249   if (ret != 0)
250     BX_ERROR(("ioctl(SNDCTL_DSP_RESET): %s", strerror(errno)));
251 
252   ret = ioctl(wavein_fd, SNDCTL_DSP_SETFMT, &fmt);
253   if (ret != 0) {  // abort if the format is unknown, to avoid playing noise
254     BX_ERROR(("ioctl(SNDCTL_DSP_SETFMT, %d): %s",
255               fmt, strerror(errno)));
256     return BX_SOUNDLOW_ERR;
257   }
258 
259   ret = ioctl(wavein_fd, SNDCTL_DSP_CHANNELS, &channels);
260   if (ret != 0) {
261     BX_ERROR(("ioctl(SNDCTL_DSP_CHANNELS, %d): %s",
262               channels, strerror(errno)));
263     return BX_SOUNDLOW_ERR;
264   }
265 
266   ret = ioctl(wavein_fd, SNDCTL_DSP_SPEED, &frequency);
267   if (ret != 0) {
268     BX_ERROR(("ioctl(SNDCTL_DSP_SPEED, %d): %s",
269               frequency, strerror(errno)));
270     return BX_SOUNDLOW_ERR;
271   }
272 
273   return BX_SOUNDLOW_OK;
274 }
275 
getwavepacket(int length,Bit8u data[])276 int bx_soundlow_wavein_oss_c::getwavepacket(int length, Bit8u data[])
277 {
278   int ret;
279 
280   ret = read(wavein_fd, data, length);
281 
282   if (ret == length) {
283     return BX_SOUNDLOW_OK;
284   } else {
285     BX_ERROR(("OSS: write error"));
286     return BX_SOUNDLOW_ERR;
287   }
288 }
289 
stopwaverecord()290 int bx_soundlow_wavein_oss_c::stopwaverecord()
291 {
292   if (record_timer_index != BX_NULL_TIMER_HANDLE) {
293     bx_pc_system.deactivate_timer(record_timer_index);
294   }
295   return BX_SOUNDLOW_OK;
296 }
297 
record_timer_handler(void * this_ptr)298 void bx_soundlow_wavein_oss_c::record_timer_handler(void *this_ptr)
299 {
300   bx_soundlow_wavein_oss_c *class_ptr = (bx_soundlow_wavein_oss_c *) this_ptr;
301 
302   class_ptr->record_timer();
303 }
304 
record_timer(void)305 void bx_soundlow_wavein_oss_c::record_timer(void)
306 {
307   record_handler(this, record_packet_size);
308 }
309 
310 // bx_soundlow_midiout_oss_c class implementation
311 
bx_soundlow_midiout_oss_c()312 bx_soundlow_midiout_oss_c::bx_soundlow_midiout_oss_c()
313     :bx_soundlow_midiout_c()
314 {
315   midi = NULL;
316 }
317 
~bx_soundlow_midiout_oss_c()318 bx_soundlow_midiout_oss_c::~bx_soundlow_midiout_oss_c()
319 {
320   closemidioutput();
321 }
322 
openmidioutput(const char * mididev)323 int bx_soundlow_midiout_oss_c::openmidioutput(const char *mididev)
324 {
325   if ((mididev == NULL) || (strlen(mididev) < 1))
326     return BX_SOUNDLOW_ERR;
327 
328   midi = fopen(mididev,"w");
329 
330   if (midi == NULL) {
331     BX_ERROR(("Couldn't open midi output device %s: %s",
332              mididev, strerror(errno)));
333     return BX_SOUNDLOW_ERR;
334   }
335 
336   return BX_SOUNDLOW_OK;
337 }
338 
339 
sendmidicommand(int delta,int command,int length,Bit8u data[])340 int bx_soundlow_midiout_oss_c::sendmidicommand(int delta, int command, int length, Bit8u data[])
341 {
342   UNUSED(delta);
343 
344   fputc(command, midi);
345   fwrite(data, 1, length, midi);
346   fflush(midi);       // to start playing immediately
347 
348   return BX_SOUNDLOW_OK;
349 }
350 
closemidioutput()351 int bx_soundlow_midiout_oss_c::closemidioutput()
352 {
353   if (midi != NULL) {
354     fclose(midi);
355     midi = NULL;
356   }
357   return BX_SOUNDLOW_OK;
358 }
359 
360 // bx_sound_oss_c class implementation
361 
get_waveout()362 bx_soundlow_waveout_c* bx_sound_oss_c::get_waveout()
363 {
364   if (waveout == NULL) {
365     waveout = new bx_soundlow_waveout_oss_c();
366   }
367   return waveout;
368 }
369 
get_wavein()370 bx_soundlow_wavein_c* bx_sound_oss_c::get_wavein()
371 {
372   if (wavein == NULL) {
373     wavein = new bx_soundlow_wavein_oss_c();
374   }
375   return wavein;
376 }
377 
get_midiout()378 bx_soundlow_midiout_c* bx_sound_oss_c::get_midiout()
379 {
380   if (midiout == NULL) {
381     midiout = new bx_soundlow_midiout_oss_c();
382   }
383   return midiout;
384 }
385 
386 #endif
387