1 // compAudio.cxx - description. -*- C++ -*-
3 // Copyright (C) 1999, 2000 Red Hat.
4 // This file is part of SID and is licensed under the GPL.
5 // See the file COPYING.SID for conditions for redistribution.
8 #include "components.h"
11 // Stuff usually defined in mmreg.h
12 #ifndef WAVE_FORMAT_ALAW
13 #define WAVE_FORMAT_ALAW 0x0006
16 #ifndef WAVE_FORMAT_MULAW
17 #define WAVE_FORMAT_MULAW 0x0007
20 #ifndef WAVE_FORMAT_ADPCM
21 #define WAVE_FORMAT_ADPCM 0x0002
26 // ----------------------------------------------------------------------------
30 operator << (ostream& o, const audio_config& c)
32 o << c.num_bits_per_sample << "-bit";
38 case audio_config::ulaw:
40 case audio_config::alaw:
42 case audio_config::pcm:
50 switch (c.num_channels)
62 o << c.sampling_frequency << "Hz";
68 operator >> (istream& i, audio_config& c)
70 // Don't support parsing above string.
71 i.setstate (ios::badbit);
77 audio_config::audio_config ()
79 this->num_bits_per_sample = 8;
80 this->encoding = audio_config::ulaw;
81 this->num_channels = 1;
82 this->sampling_frequency = 8000;
87 audio_config::encode () const
90 ((this->sampling_frequency << 0) & 0x0000FFFF) |
91 ((this->num_channels << 16) & 0x00070000) |
92 ((static_cast<int>(this->encoding) << 20) & 0x00300000) |
93 ((this->num_bits_per_sample << 24) & 0x0F000000);
97 audio_config::audio_config (host_int_4 value)
99 this->sampling_frequency = (value & 0x0000FFFF) >> 0;
100 this->num_channels = (value & 0x00070000) >> 16;
101 this->encoding = static_cast<encoding_t>((value & 0x00300000) >> 20);
102 this->num_bits_per_sample = (value & 0x0F000000) >> 24;
107 // ----------------------------------------------------------------------------
109 generic_audio::generic_audio ():
110 tx_mode_pin (this, & generic_audio::tx_mode_pin_handler),
111 tx_sample_pin (this, & generic_audio::tx_sample_pin_handler),
112 rx_mode_pin (this, & generic_audio::rx_mode_pin_handler),
113 config_set_pin (this, & generic_audio::config_set_pin_handler),
114 reset_pin (this, & generic_audio::reset_pin_handler),
115 poll_pin (this, & generic_audio::poll_pin_handler)
117 this->tx_active_p = false;
118 this->rx_active_p = false;
119 this->poll_count = 0;
120 this->tx_samples_count = 0;
121 this->tx_blocks_count = 0;
122 this->rx_samples_count = 0;
123 this->rx_blocks_count = 0;
125 // Use default audio_config
127 add_pin ("tx-mode", & this->tx_mode_pin);
128 add_attribute ("tx-mode", & this->tx_mode_pin, "pin");
129 add_pin ("tx-pending", & this->tx_pending_pin);
130 add_attribute ("tx-pending", & this->tx_pending_pin, "pin");
131 add_pin ("tx-sample", & this->tx_sample_pin);
132 add_attribute ("tx-sample", & this->tx_sample_pin, "pin");
133 add_attribute_ro ("tx-mode?", & this->tx_active_p, "register");
134 add_attribute_ro ("tx-buffer", & this->tx_buffer, "register");
136 add_pin ("rx-mode", & this->rx_mode_pin);
137 add_attribute ("rx-mode", & this->rx_mode_pin, "pin");
138 add_pin ("rx-pending", & this->rx_pending_pin);
139 add_attribute ("rx-pending", & this->rx_pending_pin, "pin");
140 add_pin ("rx-sample", & this->rx_sample_pin);
141 add_attribute ("rx-sample", & this->rx_sample_pin, "pin");
142 add_attribute_ro ("rx-mode?", & this->rx_active_p, "register");
143 add_attribute_ro ("rx-buffer", & this->rx_buffer, "register");
145 add_attribute_ro ("config", & this->config, "setting");
146 add_pin ("config-set", & this->config_set_pin);
147 add_attribute ("config-set", & this->config_set_pin, "pin");
149 add_pin ("reset", & this->reset_pin);
150 add_attribute ("reset", & this->reset_pin, "pin");
152 add_pin ("poll", & this->poll_pin);
153 add_attribute ("poll", & this->poll_pin, "pin");
155 add_attribute ("poll-count", & this->poll_count, "register");
156 add_attribute ("tx-blocks-count", & this->tx_blocks_count, "register");
157 add_attribute ("tx-samples-count", & this->tx_samples_count, "register");
158 add_attribute ("rx-blocks-count", & this->rx_blocks_count, "register");
159 add_attribute ("rx-samples-count", & this->rx_samples_count, "register");
164 generic_audio::pending_tx_p ()
166 return (this->tx_buffer != "");
171 generic_audio::update_txrx_pending_pins ()
173 // possible rising tx-pending edge
174 if (this->pending_tx_p ())
175 if (this->tx_pending_pin.recall() == 0)
176 tx_pending_pin.drive (1);
178 // possible falling tx-pending edge
179 if (! this->pending_tx_p ())
180 if (this->tx_pending_pin.recall() != 0)
181 tx_pending_pin.drive (0);
183 // possible rising rx-pending edge
184 if (this->rx_active_p)
185 if (this->rx_pending_pin.recall() == 0)
186 this->rx_pending_pin.drive (1);
188 // possible falling rx-pending edge
189 if (! this->rx_active_p)
190 if (this->rx_pending_pin.recall() != 0)
191 this->rx_pending_pin.drive (0);
196 generic_audio::tx_mode_pin_handler (host_int_4 value)
200 if (this->tx_active_p)
202 cerr << "sid-io-audio: already in tx mode" << endl;
206 bool ok = this->begin_tx (this->config);
209 cerr << "sid-io-audio: cannot begin tx" << endl;
213 this->tx_active_p = true;
214 this->tx_blocks_count ++;
218 if (! this->tx_active_p)
220 cerr << "sid-io-audio: already out of tx mode" << endl;
224 // try flushing tx-buffer once, as a last gasp measure
225 if (this->pending_tx_p ())
227 this->poll_pin_handler (0);
230 if (this->pending_tx_p ())
232 cerr << "sid-io-audio: flushing buffers on tx close" << endl;
233 this->tx_buffer = "";
237 this->tx_active_p = false;
240 this->update_txrx_pending_pins ();
245 generic_audio::rx_mode_pin_handler (host_int_4 value)
249 if (this->rx_active_p)
251 cerr << "sid-io-audio: already in rx mode" << endl;
255 bool ok = this->begin_rx (this->config);
258 cerr << "sid-io-audio: cannot begin rx" << endl;
262 this->rx_active_p = true;
263 this->rx_blocks_count ++;
267 if (! this->rx_active_p)
269 cerr << "sid-io-audio: already out of rx mode" << endl;
274 this->rx_active_p = false;
277 this->update_txrx_pending_pins ();
282 generic_audio::config_set_pin_handler (host_int_4 value)
284 // Update settings only if we are idle
285 if (! (this->rx_active_p || this->tx_active_p))
286 this->config = audio_config (value);
291 generic_audio::reset_pin_handler (host_int_4)
293 if (this->rx_active_p)
296 this->rx_active_p = false;
297 this->rx_buffer = "";
300 if (this->tx_active_p)
303 this->tx_active_p = false;
304 this->tx_buffer = "";
307 this->update_txrx_pending_pins ();
313 generic_audio::tx_sample_pin_handler (host_int_4 value)
315 if (this->tx_active_p)
316 this->tx_buffer += static_cast<unsigned char>((value & 0xFF));
319 // cerr << "sid-io-audio: Ignoring unexpected tx sample." << endl;
326 generic_audio::poll_pin_handler (host_int_4)
330 if (this->tx_active_p)
332 string remains = this->poll_tx (this->tx_buffer);
333 this->tx_samples_count += this->tx_buffer.size() - remains.size();
334 this->tx_buffer = remains;
337 if (this->rx_active_p)
339 string rxbuf = this->poll_rx ();
340 for (unsigned i=0; i<rxbuf.size(); i++)
342 this->rx_samples_count ++;
343 host_int_1 byte = rxbuf[i];
344 host_int_4 value = byte;
345 this->rx_sample_pin.drive (value);
349 this->update_txrx_pending_pins ();
353 // ----------------------------------------------------------------------------
360 this->devaudio = "/dev/audio";
362 add_attribute ("device", & this->devaudio, "setting");
366 fd_audio::~fd_audio()
368 if (this->rx_fd >= 0)
370 if (this->tx_fd >= 0)
376 asyncificate (int fd)
378 // Make this file descriptor nonblocking.
379 // Don't make it O_ASYNC though - we don't care about SIGIO.
382 int flags = fcntl (fd, F_GETFL, 0);
384 int rc = fcntl (fd, F_SETFL, flags);
387 cerr << "fcntl error: " << std_error_string() << endl;
389 // This was necessary for cygwin sockets - see sid/component/consoles/socketio.cxx
393 rc = ioctl (fd, FIONBIO, (void*) & yes);
396 cerr << "ioctl error: " << std_error_string() << endl;
402 fd_audio::begin_tx (const audio_config& c)
404 assert (this->tx_fd < 0);
406 this->tx_fd = open (this->devaudio.c_str(), O_WRONLY);
409 cerr << "sid-io-audio: error opening " << devaudio << ": " << std_error_string() << endl;
413 asyncificate (this->tx_fd);
415 bool ok = this->set_audio_config (this->tx_fd, c);
418 cerr << "sid-io-audio: error setting mode " << c << endl;
431 assert (this->tx_fd >= 0);
439 fd_audio::begin_rx (const audio_config& c)
441 assert (this->rx_fd < 0);
443 this->rx_fd = open (this->devaudio.c_str(), O_RDONLY);
446 cerr << "sid-io-audio: error opening " << devaudio << ": " << std_error_string() << endl;
450 asyncificate (this->rx_fd);
452 bool ok = this->set_audio_config (this->rx_fd, c);
455 cerr << "sid-io-audio: error setting mode " << c << endl;
468 assert (this->rx_fd >= 0);
475 innocent_errno_p (int err)
477 return ((err == EWOULDBLOCK)
478 || (err == EINPROGRESS)
485 fd_audio::poll_tx (const string& txbuf)
487 if (txbuf.length() == 0)
490 assert (this->tx_fd >= 0);
492 int count = write (this->tx_fd, txbuf.data(), txbuf.length());
493 if ((count < 0) && (! innocent_errno_p (errno)))
495 cerr << "sid-io-audio: write error: " << std_error_string() << endl;
496 // Act as if sample was consumed
499 else if (count <= 0 && innocent_errno_p (errno))
501 // Return entire string for future poll
504 else if (count == txbuf.length())
506 // Everything was sent!
511 // Return unsent portion
512 return txbuf.substr (count);
520 assert (this->rx_fd >= 0);
522 enum { rx_buffer_size = 65536 } ;
523 static char rx_buffer [rx_buffer_size];
525 int count = read (this->rx_fd, rx_buffer, rx_buffer_size);
526 if ((count < 0) && (! innocent_errno_p (errno)))
528 cerr << "sid-io-audio: read error: " << std_error_string() << endl;
529 // Act as if nothing was returned
532 else if (count <= 0 && innocent_errno_p (errno))
539 // Return received portion
540 return string (rx_buffer, count);
545 // ----------------------------------------------------------------------------
548 #if defined(SOUND_CYGWIN)
550 // XXX: CONTINUE HERE
552 cygwin_audio::cygwin_audio()
557 this->rx_buffer_size = 8000;
558 add_attribute ("rx-buffer-size", & this->rx_buffer_size, "setting");
559 this->rx_buffer_count = 8;
560 add_attribute ("rx-buffer-count", & this->rx_buffer_count, "setting");
564 cygwin_audio::~cygwin_audio ()
570 cygwin_audio::begin_tx (const audio_config& c)
572 assert (this->waveOut == 0);
574 WAVEFORMATEX win_format;
578 case audio_config::ulaw:
579 win_format.wFormatTag = WAVE_FORMAT_MULAW;
582 case audio_config::alaw:
583 win_format.wFormatTag = WAVE_FORMAT_ALAW;
586 case audio_config::pcm:
587 win_format.wFormatTag = WAVE_FORMAT_PCM;
594 win_format.wBitsPerSample = c.num_bits_per_sample;
595 win_format.nChannels = c.num_channels;
596 win_format.nSamplesPerSec = c.sampling_frequency;
597 win_format.nAvgBytesPerSec =
598 c.sampling_frequency * (c.num_bits_per_sample / 8) * c.num_channels;
599 win_format.nBlockAlign = (c.num_bits_per_sample / 8) * c.num_channels;
600 win_format.cbSize = 0;
602 unsigned res = waveOutOpen (& this->waveOut, WAVE_MAPPER,
603 & win_format, 0, 0L, CALLBACK_NULL);
604 if (res || (this->waveOut == 0))
606 cerr << "sid-io-audio: waveOutOpen error " << res << endl;
615 cygwin_audio::pending_tx_p ()
617 return generic_audio::pending_tx_p () || (! this->tx_bufs.empty ());
622 cygwin_audio::end_tx ()
624 assert (this->waveOut != 0);
626 unsigned res = waveOutReset (this->waveOut);
628 cerr << "sid-io-audio: waveOutReset rc=" << res << endl;
630 // Free up tx buffers
631 while (! this->tx_bufs.empty())
633 win32_audio_tx_buf* b = this->tx_bufs.front();
635 this->tx_bufs.pop_front ();
638 res = waveOutClose (this->waveOut);
640 cerr << "sid-io-audio: waveOutClose rc=" << res << endl;
648 cygwin_audio::begin_rx (const audio_config& c)
650 assert (this->waveIn == 0);
652 WAVEFORMATEX win_format;
656 case audio_config::ulaw:
657 win_format.wFormatTag = WAVE_FORMAT_MULAW;
660 case audio_config::alaw:
661 win_format.wFormatTag = WAVE_FORMAT_ALAW;
664 case audio_config::pcm:
665 win_format.wFormatTag = WAVE_FORMAT_PCM;
672 win_format.wBitsPerSample = c.num_bits_per_sample;
673 win_format.nChannels = c.num_channels;
674 win_format.nSamplesPerSec = c.sampling_frequency;
675 win_format.nAvgBytesPerSec =
676 c.sampling_frequency * (c.num_bits_per_sample / 8) * c.num_channels;
677 win_format.nBlockAlign = (c.num_bits_per_sample / 8) * c.num_channels;
678 win_format.cbSize = 0;
680 unsigned res = waveInOpen (& this->waveIn, WAVE_MAPPER,
681 & win_format, 0, 0L, CALLBACK_NULL);
682 if (res || (this->waveIn == 0))
684 cerr << "sid-io-audio: waveInOpen error " << res << endl;
689 for (unsigned i=0; i<this->rx_buffer_count; i++)
691 win32_audio_rx_buf* b = new win32_audio_rx_buf (this->waveIn, this->rx_buffer_size);
692 this->rx_bufs.push_back (b);
695 // Start receiving into them
696 res = waveInStart (this->waveIn);
699 cerr << "sid-io-audio: waveInStart error " << res << endl;
707 cygwin_audio::end_rx ()
709 assert (this->waveIn != 0);
711 unsigned res = waveInStop (this->waveIn);
713 cerr << "sid-io-audio: waveInStop rc=" << res << endl;
715 res = waveInReset (this->waveIn);
717 cerr << "sid-io-audio: waveInReset rc=" << res << endl;
719 // Free up pending rx buffers
720 while (! this->rx_bufs.empty())
722 win32_audio_rx_buf* b = this->rx_bufs.front ();
724 this->rx_bufs.pop_front ();
727 res = waveInClose (this->waveIn);
729 cerr << "sid-io-audio: waveInClose rc=" << res << endl;
736 cygwin_audio::poll_tx (const string& buf)
738 // Free up spent tx buffers
739 while (! this->tx_bufs.empty())
741 win32_audio_tx_buf* b = this->tx_bufs.front();
746 this->tx_bufs.pop_front ();
749 if (buf.length() == 0)
752 win32_audio_tx_buf* b = new win32_audio_tx_buf (this->waveOut, buf);
753 this->tx_bufs.push_back (b);
755 // We always consume entire supplied buffer
761 cygwin_audio::poll_rx ()
765 // Free up pending rx buffers
766 while (! this->rx_bufs.empty())
768 win32_audio_buf* b = this->rx_bufs.front ();
772 everything += b->buffer ();
774 this->rx_bufs.pop_front ();
776 win32_audio_rx_buf* b2 = new win32_audio_rx_buf (this->waveIn, this->rx_buffer_size);
777 this->rx_bufs.push_back (b2);
784 win32_audio_buf::win32_audio_buf (host_int_4 size)
786 this->block_handle = GlobalAlloc (GMEM_MOVEABLE | GMEM_SHARE, size);
787 if (! this->block_handle)
789 cerr << "sid-io-audio: GlobalAlloc " << size << " failed." << endl;
790 // Act as if we're already done.
791 this->header.dwFlags |= WHDR_DONE;
794 this->block_addr = static_cast<HPSTR>(GlobalLock (this->block_handle));
796 this->header.lpData = this->block_addr;
797 this->header.dwBufferLength = size;
798 this->header.dwFlags = 0;
799 this->header.dwLoops = 0;
800 this->header.dwBytesRecorded = 0;
801 this->header.dwUser = 0;
805 win32_audio_buf::~win32_audio_buf ()
807 if (this->block_handle == 0)
810 GlobalUnlock (this->block_addr);
811 GlobalFree (this->block_handle);
816 win32_audio_buf::done_p ()
818 return (this->header.dwFlags & WHDR_DONE);
822 win32_audio_tx_buf::win32_audio_tx_buf (HWAVEOUT dev, const string& buf):
823 win32_audio_buf (buf.length ())
827 // Copy data into this buffer
828 memcpy (this->block_addr, buf.data(), buf.length());
830 unsigned res = waveOutPrepareHeader (this->device, & this->header, sizeof (this->header));
833 cerr << "sid-io-audio: waveOutPrepareHeader error " << res << endl;
837 res = waveOutWrite (this->device, & this->header, sizeof (this->header));
840 cerr << "sid-io-audio: waveOutWrite error " << res << endl;
846 win32_audio_rx_buf::win32_audio_rx_buf (HWAVEIN dev, host_int_4 size):
847 win32_audio_buf (size)
851 unsigned res = waveInPrepareHeader (this->device, & this->header, sizeof (this->header));
854 cerr << "sid-io-audio: waveInPrepareHeader error " << res << endl;
858 res = waveInAddBuffer (this->device, & this->header, sizeof (this->header));
861 cerr << "sid-io-audio: waveInAddBuffer error " << res << endl;
867 win32_audio_rx_buf::~win32_audio_rx_buf ()
869 unsigned res = waveInUnprepareHeader (this->device, & this->header, sizeof (this->header));
872 cerr << "sid-io-audio: waveInUnprepareHeader error " << res << endl;
878 win32_audio_tx_buf::~win32_audio_tx_buf ()
880 unsigned res = waveOutUnprepareHeader (this->device, & this->header, sizeof (this->header));
883 cerr << "sid-io-audio: waveOutUnprepareHeader error " << res << endl;
890 win32_audio_buf::buffer ()
892 // fetch buffer contents
893 string value = string (this->block_addr, this->header.dwBufferLength);