- ALSA code cleanup: removed unused client and queue
[bochs-mirror.git] / iodev / soundlnx.cc
blob0ac91034c6fd2d2002bbc3e5c49172152a65ac24
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: soundlnx.cc,v 1.17 2008/07/20 08:08:23 vruppert Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2001 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 /////////////////////////////////////////////////////////////////////////
28 // Josef Drexler coded the original version of the lowlevel sound support
29 // for Linux using OSS. The current version also supports OSS on FreeBSD and
30 // ALSA PCM output on Linux.
32 #define NO_DEVICE_INCLUDES
33 #include "iodev.h"
34 #define BX_SOUNDLOW
35 #include "sb16.h"
37 #if (defined(linux) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) && BX_SUPPORT_SB16
39 #define LOG_THIS bx_devices.pluginSB16Device->
41 #include "soundlnx.h"
43 #include <errno.h>
44 #include <sys/ioctl.h>
45 #include <sys/soundcard.h>
47 bx_sound_linux_c::bx_sound_linux_c(bx_sb16_c *sb16)
48 :bx_sound_output_c(sb16)
50 this->sb16 = sb16;
51 #if BX_HAVE_ALSASOUND
52 alsa_seq.handle = NULL;
53 alsa_pcm.handle = NULL;
54 alsa_buffer = NULL;
55 #endif
56 midi = NULL;
57 wavedevice = NULL;
58 wave = -1;
61 bx_sound_linux_c::~bx_sound_linux_c()
63 // nothing for now
67 int bx_sound_linux_c::waveready()
69 return BX_SOUND_OUTPUT_OK;
72 int bx_sound_linux_c::midiready()
74 return BX_SOUND_OUTPUT_OK;
77 #if BX_HAVE_ALSASOUND
78 int bx_sound_linux_c::alsa_seq_open(char *device)
80 char *mididev, *ptr;
81 int client, port, ret = 0;
82 int length = strlen(device) + 1;
84 mididev = new char[length];
86 if (mididev == NULL)
87 return BX_SOUND_OUTPUT_ERR;
89 strcpy(mididev, device);
90 ptr = strtok(mididev, ":");
91 if (ptr == NULL) {
92 WRITELOG(MIDILOG(2), "ALSA sequencer setup: missing client parameters");
93 return BX_SOUND_OUTPUT_ERR;
95 client = atoi(ptr);
96 ptr = strtok(NULL, ":");
97 if (ptr == NULL) {
98 WRITELOG(MIDILOG(2), "ALSA sequencer setup: missing port parameter");
99 return BX_SOUND_OUTPUT_ERR;
101 port = atoi(ptr);
103 delete(mididev);
105 if (snd_seq_open(&alsa_seq.handle, "default", SND_SEQ_OPEN_OUTPUT, 0) < 0) {
106 WRITELOG(MIDILOG(2), "Couldn't open ALSA sequencer for midi output");
107 return BX_SOUND_OUTPUT_ERR;
109 ret = snd_seq_create_simple_port(alsa_seq.handle, NULL,
110 SND_SEQ_PORT_CAP_WRITE |
111 SND_SEQ_PORT_CAP_SUBS_WRITE |
112 SND_SEQ_PORT_CAP_READ,
113 SND_SEQ_PORT_TYPE_MIDI_GENERIC);
114 if (ret < 0) {
115 WRITELOG(MIDILOG(2), "ALSA sequencer: error creating port %s\n", snd_strerror(errno));
116 } else {
117 alsa_seq.source_port = ret;
118 ret = snd_seq_connect_to(alsa_seq.handle, alsa_seq.source_port, client, port);
119 if (ret < 0) {
120 WRITELOG(MIDILOG(2), "ALSA sequencer: could not connect to port %d:%d\n", client, port);
123 if (ret < 0) {
124 snd_seq_close(alsa_seq.handle);
125 return BX_SOUND_OUTPUT_ERR;
126 } else {
127 return BX_SOUND_OUTPUT_OK;
130 #endif
132 int bx_sound_linux_c::openmidioutput(char *device)
134 if ((device == NULL) || (strlen(device) < 1))
135 return BX_SOUND_OUTPUT_ERR;
137 #if BX_HAVE_ALSASOUND
138 use_alsa_seq = !strncmp(device, "alsa:", 5);
139 if (use_alsa_seq) {
140 return alsa_seq_open(device+5);
142 #endif
144 midi = fopen(device,"w");
146 if (midi == NULL)
148 WRITELOG(MIDILOG(2), "Couldn't open midi output device %s: %s.",
149 device, strerror(errno));
150 return BX_SOUND_OUTPUT_ERR;
153 return BX_SOUND_OUTPUT_OK;
157 #if BX_HAVE_ALSASOUND
158 int bx_sound_linux_c::alsa_seq_output(int delta, int command, int length, Bit8u data[])
160 int cmd, chan, value;
161 snd_seq_event_t ev;
163 snd_seq_ev_clear(&ev);
164 snd_seq_ev_set_source(&ev, alsa_seq.source_port);
165 snd_seq_ev_set_subs(&ev);
166 snd_seq_ev_set_direct(&ev);
167 cmd = command & 0xf0;
168 chan = command & 0x0f;
169 switch (cmd) {
170 case 0x80:
171 ev.type = SND_SEQ_EVENT_NOTEOFF;
172 ev.data.note.channel = chan;
173 ev.data.note.note = data[0];
174 ev.data.note.velocity = data[1];
175 ev.data.note.duration = delta;
176 break;
177 case 0x90:
178 ev.type = SND_SEQ_EVENT_NOTEON;
179 ev.data.note.channel = chan;
180 ev.data.note.note = data[0];
181 ev.data.note.velocity = data[1];
182 ev.data.note.duration = 0;
183 break;
184 case 0xa0:
185 ev.type = SND_SEQ_EVENT_KEYPRESS;
186 ev.data.control.channel = chan;
187 ev.data.control.param = data[0];
188 ev.data.control.value = data[1];
189 break;
190 case 0xb0:
191 ev.type = SND_SEQ_EVENT_CONTROLLER;
192 ev.data.control.channel = chan;
193 ev.data.control.param = data[0];
194 ev.data.control.value = data[1];
195 break;
196 case 0xc0:
197 ev.type = SND_SEQ_EVENT_PGMCHANGE;
198 ev.data.control.channel = chan;
199 ev.data.control.value = data[0];
200 break;
201 case 0xd0:
202 ev.type = SND_SEQ_EVENT_CHANPRESS;
203 ev.data.control.channel = chan;
204 ev.data.control.value = data[0];
205 break;
206 case 0xe0:
207 ev.type = SND_SEQ_EVENT_PITCHBEND;
208 ev.data.control.channel = chan;
209 value = data[0] | (data[1] << 7);
210 value -= 0x2000;
211 ev.data.control.value = value;
212 break;
213 case 0xf0:
214 WRITELOG(MIDILOG(3), "alsa_seq_output(): SYSEX not implemented, length=%d", length);
215 return BX_SOUND_OUTPUT_ERR;
216 default:
217 WRITELOG(MIDILOG(3), "alsa_seq_output(): unknown command 0x%02x, length=%d", command, length);
218 return BX_SOUND_OUTPUT_ERR;
220 snd_seq_event_output(alsa_seq.handle, &ev);
221 snd_seq_drain_output(alsa_seq.handle);
222 return BX_SOUND_OUTPUT_OK;
224 #endif
226 int bx_sound_linux_c::sendmidicommand(int delta, int command, int length, Bit8u data[])
228 #if BX_HAVE_ALSASOUND
229 if ((use_alsa_seq) && (alsa_seq.handle != NULL)) {
230 return alsa_seq_output(delta, command, length, data);
232 #endif
234 UNUSED(delta);
236 fputc(command, midi);
237 fwrite(data, 1, length, midi);
238 fflush(midi); // to start playing immediately
240 return BX_SOUND_OUTPUT_OK;
244 int bx_sound_linux_c::closemidioutput()
246 #if BX_HAVE_ALSASOUND
247 if ((use_alsa_seq) && (alsa_seq.handle != NULL)) {
248 snd_seq_close(alsa_seq.handle);
249 return BX_SOUND_OUTPUT_OK;
251 #endif
252 fclose(midi);
254 return BX_SOUND_OUTPUT_OK;
258 int bx_sound_linux_c::openwaveoutput(char *device)
260 #if BX_HAVE_ALSASOUND
261 use_alsa_pcm = !strcmp(device, "alsa");
262 if (use_alsa_pcm) {
263 return BX_SOUND_OUTPUT_OK;
265 #endif
266 int length = strlen(device) + 1;
268 if (wavedevice != NULL)
269 delete(wavedevice);
271 wavedevice = new char[length];
273 if (wavedevice == NULL)
274 return BX_SOUND_OUTPUT_ERR;
276 strncpy(wavedevice, device, length);
278 return BX_SOUND_OUTPUT_OK;
281 #if BX_HAVE_ALSASOUND
282 int bx_sound_linux_c::alsa_pcm_open(int frequency, int bits, int stereo, int format)
284 int ret;
285 snd_pcm_format_t fmt;
286 snd_pcm_hw_params_t *params;
287 unsigned int size, freq;
288 int signeddata = format & 1;
290 audio_bufsize = 0;
292 if (alsa_pcm.handle == NULL) {
293 ret = snd_pcm_open(&alsa_pcm.handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
294 if (ret < 0) {
295 return BX_SOUND_OUTPUT_ERR;
297 WRITELOG(WAVELOG(1), "ALSA: opened default PCM output device");
299 snd_pcm_hw_params_alloca(&params);
300 snd_pcm_hw_params_any(alsa_pcm.handle, params);
301 snd_pcm_hw_params_set_access(alsa_pcm.handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
303 if ((frequency == oldfreq) &&
304 (bits == oldbits) &&
305 (stereo == oldstereo) &&
306 (format == oldformat))
307 return BX_SOUND_OUTPUT_OK; // nothing to do
309 oldfreq = frequency;
310 oldbits = bits;
311 oldstereo = stereo;
312 oldformat = format;
314 freq = (unsigned int)frequency;
316 if (bits == 16) {
317 if (signeddata == 1)
318 fmt = SND_PCM_FORMAT_S16_LE;
319 else
320 fmt = SND_PCM_FORMAT_U16_LE;
321 size = 2;
322 } else if (bits == 8) {
323 if (signeddata == 1)
324 fmt = SND_PCM_FORMAT_S8;
325 else
326 fmt = SND_PCM_FORMAT_U8;
327 size = 1;
328 } else
329 return BX_SOUND_OUTPUT_ERR;
331 if (stereo) size *= 2;
333 snd_pcm_hw_params_set_format(alsa_pcm.handle, params, fmt);
334 snd_pcm_hw_params_set_channels(alsa_pcm.handle, params, (stereo != 0) ? 2 : 1);
335 snd_pcm_hw_params_set_rate_near(alsa_pcm.handle, params, &freq, &dir);
337 alsa_pcm.frames = 32;
338 snd_pcm_hw_params_set_period_size_near(alsa_pcm.handle, params, &alsa_pcm.frames, &dir);
340 ret = snd_pcm_hw_params(alsa_pcm.handle, params);
341 if (ret < 0) {
342 return BX_SOUND_OUTPUT_ERR;
344 snd_pcm_hw_params_get_period_size(params, &alsa_pcm.frames, &dir);
345 alsa_bufsize = alsa_pcm.frames * size;
346 WRITELOG(WAVELOG(4), "ALSA: buffer size set to %d", alsa_bufsize);
347 if (alsa_buffer != NULL) {
348 free(alsa_buffer);
349 alsa_buffer = NULL;
352 return BX_SOUND_OUTPUT_OK;
354 #endif
356 int bx_sound_linux_c::startwaveplayback(int frequency, int bits, int stereo, int format)
358 int fmt, ret;
359 int signeddata = format & 1;
361 #if BX_HAVE_ALSASOUND
362 if (use_alsa_pcm) {
363 return alsa_pcm_open(frequency, bits, stereo, format);
365 #endif
366 if ((wavedevice == NULL) || (strlen(wavedevice) < 1))
367 return BX_SOUND_OUTPUT_ERR;
369 if (wave == -1) {
370 wave = open(wavedevice, O_WRONLY);
371 if (wave == -1) {
372 return BX_SOUND_OUTPUT_ERR;
373 } else {
374 WRITELOG(WAVELOG(1), "OSS: opened output device %s", wavedevice);
376 } else {
377 if ((frequency == oldfreq) &&
378 (bits == oldbits) &&
379 (stereo == oldstereo) &&
380 (format == oldformat))
381 return BX_SOUND_OUTPUT_OK; // nothing to do
383 oldfreq = frequency;
384 oldbits = bits;
385 oldstereo = stereo;
386 oldformat = format;
388 if (bits == 16)
389 if (signeddata == 1)
390 fmt = AFMT_S16_LE;
391 else
392 fmt = AFMT_U16_LE;
393 else if (bits == 8)
394 if (signeddata == 1)
395 fmt = AFMT_S8;
396 else
397 fmt = AFMT_U8;
398 else
399 return BX_SOUND_OUTPUT_ERR;
401 // set frequency etc.
402 ret = ioctl(wave, SNDCTL_DSP_RESET);
403 if (ret != 0)
404 WRITELOG(WAVELOG(4), "ioctl(SNDCTL_DSP_RESET): %s", strerror(errno));
407 ret = ioctl(wave, SNDCTL_DSP_SETFRAGMENT, &fragment);
408 if (ret != 0)
409 WRITELOG(WAVELOG(4), "ioctl(SNDCTL_DSP_SETFRAGMENT, %d): %s",
410 fragment, strerror(errno));
413 ret = ioctl(wave, SNDCTL_DSP_SETFMT, &fmt);
414 if (ret != 0) // abort if the format is unknown, to avoid playing noise
416 WRITELOG(WAVELOG(4), "ioctl(SNDCTL_DSP_SETFMT, %d): %s",
417 fmt, strerror(errno));
418 return BX_SOUND_OUTPUT_ERR;
421 ret = ioctl(wave, SNDCTL_DSP_STEREO, &stereo);
422 if (ret != 0)
423 WRITELOG(WAVELOG(4), "ioctl(SNDCTL_DSP_STEREO, %d): %s",
424 stereo, strerror(errno));
426 ret = ioctl(wave, SNDCTL_DSP_SPEED, &frequency);
427 if (ret != 0)
428 WRITELOG(WAVELOG(4), "ioctl(SNDCTL_DSP_SPEED, %d): %s",
429 frequency, strerror(errno));
431 // ioctl(wave, SNDCTL_DSP_GETBLKSIZE, &fragment);
432 // WRITELOG(WAVELOG(4), "current output block size is %d", fragment);
434 return BX_SOUND_OUTPUT_OK;
437 #if BX_HAVE_ALSASOUND
438 int bx_sound_linux_c::alsa_pcm_write()
440 int ret;
442 if (alsa_buffer == NULL) {
443 alsa_buffer = (char *)malloc(alsa_bufsize);
445 while (audio_bufsize >= alsa_bufsize) {
446 memcpy(alsa_buffer, audio_buffer, alsa_bufsize);
447 ret = snd_pcm_writei(alsa_pcm.handle, alsa_buffer, alsa_pcm.frames);
448 if (ret == -EPIPE) {
449 /* EPIPE means underrun */
450 WRITELOG(WAVELOG(3), "ALSA: underrun occurred");
451 snd_pcm_prepare(alsa_pcm.handle);
452 } else if (ret < 0) {
453 WRITELOG(WAVELOG(3), "ALSA: error from writei: %s", snd_strerror(ret));
454 } else if (ret != (int)alsa_pcm.frames) {
455 WRITELOG(WAVELOG(3), "ALSA: short write, write %d frames", ret);
457 audio_bufsize -= alsa_bufsize;
458 memcpy(audio_buffer, audio_buffer+alsa_bufsize, audio_bufsize);
460 if ((audio_bufsize == 0) && (alsa_buffer != NULL)) {
461 free(alsa_buffer);
462 alsa_buffer = NULL;
465 return BX_SOUND_OUTPUT_OK;
467 #endif
469 int bx_sound_linux_c::sendwavepacket(int length, Bit8u data[])
471 #if BX_HAVE_ALSASOUND
472 if (use_alsa_pcm) {
473 if ((audio_bufsize+length) <= BX_SOUND_LINUX_BUFSIZE) {
474 memcpy(audio_buffer+audio_bufsize, data, length);
475 audio_bufsize += length;
476 } else {
477 WRITELOG(WAVELOG(3), "ALSA: audio buffer overflow");
478 return BX_SOUND_OUTPUT_ERR;
480 if (audio_bufsize < alsa_bufsize) {
481 return BX_SOUND_OUTPUT_OK;
482 } else {
483 return alsa_pcm_write();
486 #endif
487 int ret = write(wave, data, length);
489 if (ret == length) {
490 return BX_SOUND_OUTPUT_OK;
491 } else {
492 WRITELOG(WAVELOG(3), "OSS: write error");
493 return BX_SOUND_OUTPUT_ERR;
497 int bx_sound_linux_c::stopwaveplayback()
499 #if BX_HAVE_ALSASOUND
500 if (use_alsa_pcm && (audio_bufsize > 0)) {
501 if (audio_bufsize < alsa_bufsize) {
502 memset(audio_buffer+audio_bufsize, 0, alsa_bufsize-audio_bufsize);
503 audio_bufsize = alsa_bufsize;
505 alsa_pcm_write();
507 #endif
508 // ioctl(wave, SNDCTL_DSP_SYNC);
509 // close(wave);
510 // wave = -1;
512 return BX_SOUND_OUTPUT_OK;
515 int bx_sound_linux_c::closewaveoutput()
517 #if BX_HAVE_ALSASOUND
518 if (use_alsa_pcm && (alsa_pcm.handle != NULL)) {
519 snd_pcm_drain(alsa_pcm.handle);
520 snd_pcm_close(alsa_pcm.handle);
521 alsa_pcm.handle = NULL;
523 #endif
524 if (wavedevice != NULL)
525 delete(wavedevice);
527 if (wave != -1)
529 close(wave);
530 wave = -1;
532 wavedevice = NULL;
534 return BX_SOUND_OUTPUT_OK;
537 #endif