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