summaryrefslogtreecommitdiff
path: root/emu/Nt/audio.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /emu/Nt/audio.c
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'emu/Nt/audio.c')
-rw-r--r--emu/Nt/audio.c810
1 files changed, 810 insertions, 0 deletions
diff --git a/emu/Nt/audio.c b/emu/Nt/audio.c
new file mode 100644
index 00000000..e1bc6f86
--- /dev/null
+++ b/emu/Nt/audio.c
@@ -0,0 +1,810 @@
+#define Unknown win_Unknown
+#include <windows.h>
+#include <mmsystem.h>
+#undef Unknown
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#define Audio_Mic_Val 0
+#define Audio_Linein_Val -1
+
+#define Audio_Speaker_Val 0
+#define Audio_Headphone_Val -1
+#define Audio_Lineout_Val -1
+
+#define Audio_Pcm_Val WAVE_FORMAT_PCM
+#define Audio_Ulaw_Val (WAVE_FORMAT_PCM+1)
+#define Audio_Alaw_Val (WAVE_FORMAT_PCM+2)
+
+#define Audio_Max_Queue 8
+
+#define BUFLEN 1000
+
+#define INISOPEN 0x00000002 // the microphone is open
+#define OUTISOPEN 0x00000004 // the speaker is open
+#define INPUTISGOING 0x00000020 // microphone is being recorded/read
+
+#include "audio.h"
+#include "audio-tbls.c"
+
+static int debug = 0;
+
+/* TO DO: sensible expression of double-buffering */
+#define Ping 0
+#define Pong 1
+
+static HWAVEIN audio_file_in;
+static HWAVEOUT audio_file_out;
+
+static long out_buf_count;
+
+typedef struct _awin {
+ WAVEHDR hdr;
+ long sz;
+ char* ptr;
+ char data[Audio_Max_Buf];
+} AWin;
+
+static AWin audio_ping;
+static AWin audio_pong;
+
+static long paddle = Ping;
+static int ping_is_filling;
+static int pong_is_filling;
+
+static long audio_flags = 0;
+static int audio_init = 0;
+
+static QLock flag_lock;
+
+static Audio_t av;
+
+static HANDLE outlock;
+static HANDLE inlock;
+
+static int audio_open_in(HWAVEIN*, Audio_d*);
+static int audio_open_out(HWAVEOUT*, Audio_d*);
+static void audio_close_in(void);
+static void audio_close_out(void);
+static void CALLBACK waveInProc(HWAVEIN, UINT, DWORD, DWORD, DWORD);
+static void CALLBACK waveOutProc(HWAVEOUT, UINT, DWORD, DWORD, DWORD);
+
+#define AUDIOIN 0
+#define AUDIOOUT 1
+
+/*
+* Error routines
+*/
+static int
+audioerror(unsigned int code, int in_out, char *msg)
+{
+ char errorText[MAXERRORLENGTH];
+
+ if (code != MMSYSERR_NOERROR) {
+ switch(in_out) {
+ case AUDIOIN:
+ waveInGetErrorText(code, errorText, sizeof(errorText));
+ //print("ERROR -- %s: %s\n", msg, errorText);
+ return(-1);
+ case AUDIOOUT:
+ waveOutGetErrorText(code, errorText, sizeof(errorText));
+ //print("ERROR -- %s: %s\n", msg, errorText);
+ return(-1);
+ default:
+ print("%s: Unknown device\n", msg);
+ }
+ }
+ //print("TRACE %s\n", msg);
+ return 0;
+}
+
+void
+audio_file_init(void)
+{
+ audio_info_init(&av);
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ int in_is_open = 0;
+
+ switch(omode){
+ case OREAD:
+ qlock(&flag_lock);
+
+ if(waserror()) {
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ if(audio_flags & INISOPEN)
+ error(Einuse);
+
+ inlock = CreateMutex(NULL, FALSE, NULL);
+ if(inlock == NULL)
+ error(Einuse);
+
+ if(!audio_open_in(&audio_file_in, &av.in) ) {
+ CloseHandle(inlock);
+ error(Ebadarg);
+ }
+
+ ping_is_filling = 0;
+ pong_is_filling = 0;
+ paddle = Ping;
+ audio_flags |= INISOPEN;
+
+ poperror();
+ qunlock(&flag_lock);
+ break;
+ case OWRITE:
+ qlock(&flag_lock);
+ if(waserror()){
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ if(audio_flags & OUTISOPEN)
+ error(Einuse);
+
+ outlock = CreateMutex(NULL, FALSE, NULL);
+ if(outlock == NULL)
+ error(Einuse);
+
+ if(!audio_open_out(&audio_file_out, &av.out) ) {
+ CloseHandle(outlock);
+ error(Ebadarg);
+ }
+
+ out_buf_count = 0;
+ audio_flags |= OUTISOPEN;
+
+ poperror();
+ qunlock(&flag_lock);
+ break;
+ case ORDWR:
+ qlock(&flag_lock);
+ if(waserror()){
+ qunlock(&flag_lock);
+ if(in_is_open)
+ audio_close_in();
+ nexterror();
+ }
+
+ if((audio_flags & INISOPEN) || (audio_flags & OUTISOPEN))
+ error(Einuse);
+
+ if(!audio_open_in(&audio_file_in, &av.in) )
+ error(Ebadarg);
+
+ in_is_open = 1;
+
+ if(!audio_open_out(&audio_file_out, &av.out)) {
+ CloseHandle(outlock);
+ error(Ebadarg);
+ }
+
+ inlock = CreateMutex(NULL, FALSE, NULL);
+ if(inlock == NULL)
+ error(Einuse);
+
+ outlock = CreateMutex(NULL, FALSE, NULL);
+ if(outlock == NULL) {
+ CloseHandle(inlock);
+ error(Einuse);
+ }
+
+ audio_flags |= INISOPEN;
+ audio_flags |= OUTISOPEN;
+ ping_is_filling = 0;
+ pong_is_filling = 0;
+ paddle = Ping;
+ out_buf_count = 0;
+
+ poperror();
+ qunlock(&flag_lock);
+ break;
+ default:
+ error(Egreg);
+ }
+}
+
+static int
+audio_open_in(HWAVEIN* h, Audio_d* d)
+{
+ HWAVEIN th;
+ WAVEFORMATEX format;
+
+ format.wFormatTag = d->enc;
+ format.nChannels = d->chan;
+ format.nSamplesPerSec = d->rate;
+ format.wBitsPerSample = d->bits;
+ format.nBlockAlign = (d->chan * d->bits) / Bits_Per_Byte;
+ format.nAvgBytesPerSec =
+ format.nSamplesPerSec * format.nBlockAlign;
+ format.cbSize = 0;
+
+ if (audioerror(
+ waveInOpen(&th, WAVE_MAPPER, &format, (DWORD)waveInProc, 0, CALLBACK_FUNCTION),
+ AUDIOIN,
+ "cannot open microphone/line-in") == 0) {
+ *h = th;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+audio_open_out(HWAVEOUT* h, Audio_d* d)
+{
+ unsigned int code;
+ HWAVEOUT th;
+ WAVEFORMATEX format;
+
+ format.wFormatTag = d->enc;
+ format.nChannels = d->chan;
+ format.nSamplesPerSec = d->rate;
+ format.wBitsPerSample = d->bits;
+ format.nBlockAlign = (d->chan * d->bits) / Bits_Per_Byte;
+ format.nAvgBytesPerSec =
+ format.nSamplesPerSec * format.nBlockAlign;
+ format.cbSize = 0;
+
+ code = waveOutOpen(&th, WAVE_MAPPER, &format, (DWORD)waveOutProc, 0, CALLBACK_FUNCTION);
+
+ if (audioerror(code, AUDIOOUT, "cannot open speaker/line-out") == 0) {
+ out_buf_count = 0;
+ *h = th;
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&flag_lock);
+ audio_close_in();
+ audio_flags &= ~(INISOPEN|INPUTISGOING);
+ CloseHandle(inlock);
+ qunlock(&flag_lock);
+ break;
+ case OWRITE:
+ qlock(&flag_lock);
+ audio_close_out();
+ audio_flags &= ~OUTISOPEN;
+ CloseHandle(outlock);
+ qunlock(&flag_lock);
+ break;
+ case ORDWR:
+ qlock(&flag_lock);
+ audio_close_in();
+ audio_close_out();
+
+ audio_flags &= ~(INISOPEN|INPUTISGOING|OUTISOPEN);
+
+ CloseHandle(outlock);
+ CloseHandle(inlock);
+ qunlock(&flag_lock);
+ break;
+ }
+}
+
+static void
+audio_close_in()
+{
+ audioerror(waveInStop(audio_file_in), AUDIOIN, "audio_close_in Stop");
+ audioerror(waveInReset(audio_file_in), AUDIOIN, "audio_close_in Reset");
+
+ audioerror(waveInUnprepareHeader(audio_file_in, &audio_ping.hdr,
+ sizeof(WAVEHDR)), AUDIOIN, "in un prepare ping header");
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ audioerror(waveInUnprepareHeader(audio_file_in, &audio_pong.hdr,
+ sizeof(WAVEHDR)), AUDIOIN, "in un prepare pong header");
+ audio_pong.sz = 0;
+ audio_pong.ptr = &audio_pong.data[0];
+
+ audioerror(waveInClose(audio_file_in), AUDIOIN, "in close");
+
+}
+
+static void
+audio_close_out()
+{
+Again:
+ WaitForSingleObject(outlock, INFINITE);
+ while(out_buf_count > 0) {
+ ReleaseMutex(outlock);
+ sleep(0);
+ goto Again;
+ }
+ ReleaseMutex(outlock);
+
+ audioerror(waveOutReset(audio_file_out), AUDIOOUT, "close wave out reset");
+ audioerror(waveOutClose(audio_file_out), AUDIOOUT, "closing out device");
+}
+
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ MMRESULT status;
+ long len = av.in.buf * Audio_Max_Buf / Audio_Max_Val;
+ char *v = (char *) va;
+ char *p;
+ long ba, n, chunk, total;
+
+
+ qlock(&flag_lock);
+ WaitForSingleObject(inlock, INFINITE);
+
+ if(waserror()) {
+ audioerror(waveInStop(audio_file_in), AUDIOIN,
+ "audio_file_read Stop 1");
+ audioerror(waveInReset(audio_file_in), AUDIOIN,
+ "audio_file_read Reset 1");
+ audioerror(waveInUnprepareHeader(audio_file_in,
+ &audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "in unprepare ping");
+ audioerror(waveInUnprepareHeader(audio_file_in,
+ &audio_pong.hdr, sizeof(WAVEHDR)),
+ AUDIOIN, "in unprepare pong");
+
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ audio_pong.sz = 0;
+ audio_pong.ptr = &audio_pong.data[0];
+
+ ping_is_filling = pong_is_filling = 0;
+ paddle = Ping;
+
+ qunlock(&flag_lock);
+ ReleaseMutex(inlock);
+
+ nexterror();
+ }
+
+ if(!(audio_flags & INISOPEN))
+ error(Eperm);
+
+ /* check for block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(len < 1 || count % ba)
+ error(Ebadarg);
+
+ if(!(audio_flags & INPUTISGOING)) {
+ if(audioerror(waveInStart(audio_file_in), AUDIOIN,
+ "in start") == -1)
+ error(Eio);
+
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ audio_ping.hdr.lpData = audio_ping.ptr;
+ audio_ping.hdr.dwBufferLength = len;
+ audio_ping.hdr.dwUser = Ping;
+ audio_ping.hdr.dwFlags = 0;
+
+ status = waveInPrepareHeader(audio_file_in, &audio_ping.hdr,
+ sizeof(WAVEHDR));
+
+ if (audioerror(status, AUDIOIN, "in prepare header") == -1)
+ error(Eio);
+
+ audio_pong.sz = 0;
+ audio_pong.ptr = &audio_pong.data[0];
+ audio_pong.hdr.lpData = audio_pong.ptr;
+ audio_pong.hdr.dwBufferLength = len;
+ audio_pong.hdr.dwUser = Pong;
+ audio_pong.hdr.dwFlags = 0;
+
+ status = waveInPrepareHeader(audio_file_in, &audio_pong.hdr,
+ sizeof(WAVEHDR));
+
+ if (audioerror(status, AUDIOIN, "in prepare header") == -1)
+ error(Eio);
+
+ status = waveInAddBuffer(audio_file_in, &audio_ping.hdr,
+ sizeof(WAVEHDR));
+ if (audioerror(status, AUDIOIN, "file_read Add Buffer")== -1){
+ waveInUnprepareHeader(audio_file_in, &audio_ping.hdr,
+ sizeof(WAVEHDR));
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ error(Eio);
+ }
+
+ ping_is_filling = 1;
+ pong_is_filling = 0;
+ paddle = Ping;
+ audio_flags |= INPUTISGOING;
+ }
+ poperror();
+ ReleaseMutex(inlock);
+
+ total = 0;
+
+Draining:
+
+ WaitForSingleObject(inlock, INFINITE);
+ if(waserror()) {
+ audioerror(waveInStop(audio_file_in), AUDIOIN,
+ "audio_file_read Stop 2");
+ audioerror(waveInReset(audio_file_in), AUDIOIN,
+ "audio_file_read Reset 2");
+ audioerror(waveInUnprepareHeader(audio_file_in,
+ &audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "in unprepare ping");
+ audioerror(waveInUnprepareHeader(audio_file_in,
+ &audio_pong.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "in unprepare pong");
+
+ audio_ping.sz = 0;
+ audio_ping.ptr = &audio_ping.data[0];
+ audio_pong.sz = 0;
+ audio_pong.ptr = &audio_pong.data[0];
+
+ audio_flags &= ~INPUTISGOING;
+
+ ReleaseMutex(inlock);
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ while((total < count) && ((audio_ping.sz > 0) || (audio_pong.sz > 0))) {
+ n = paddle == Ping ? audio_ping.sz : audio_pong.sz;
+ p = paddle == Ping ? audio_ping.ptr : audio_pong.ptr;
+
+ chunk = min(n, count - total);
+
+ memmove(v+total, p , chunk);
+
+ total += chunk;
+
+ if(paddle == Ping) {
+ if(!pong_is_filling) {
+
+ if(audioerror(waveInAddBuffer(audio_file_in,
+ &audio_pong.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "draining ping calling add buffer pong") == -1)
+ error(Eio);
+
+ pong_is_filling = 1;
+ }
+
+ audio_ping.sz -= chunk;
+ if(audio_ping.sz > 0) {
+ audio_ping.ptr += chunk;
+ } else {
+ audio_ping.ptr = &audio_ping.data[0];
+ ping_is_filling = 0;
+ paddle = Pong;
+ }
+ } else {
+ if(!ping_is_filling) {
+
+ if(audioerror(waveInAddBuffer(audio_file_in,
+ &audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN,
+ "draining pong calling add buffer ping") == -1)
+ error(Eio);
+
+ ping_is_filling = 1;
+ }
+
+ audio_pong.sz -= chunk;
+ if(audio_pong.sz > 0) {
+ audio_pong.ptr += chunk;
+ } else {
+ audio_pong.ptr = &audio_pong.data[0];
+ pong_is_filling = 0;
+ paddle = Ping;
+ }
+ }
+ }
+
+ poperror();
+
+ ReleaseMutex(inlock);
+
+ if(total == count) {
+ qunlock(&flag_lock);
+ return count;
+ }
+
+Filling:
+ WaitForSingleObject(inlock, INFINITE);
+ while((audio_ping.sz < 1) && (audio_pong.sz < 1)) {
+ ReleaseMutex(inlock);
+ sleep(0);
+ goto Filling;
+ }
+ ReleaseMutex(inlock);
+
+ goto Draining;
+}
+
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ MMRESULT status;
+ WAVEHDR *hHdr = (WAVEHDR *) NULL;
+ char *hData = NULL;
+ char *p = (char *) va;
+ long ba;
+ long bufsz;
+ long chunk;
+ long total;
+
+ qlock(&flag_lock);
+ if(waserror()){
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ if(!(audio_flags & OUTISOPEN))
+ error(Eperm);
+
+ /* check for block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz < 1)
+ error(Ebadarg);
+
+ total = 0;
+
+ while(total < count) {
+
+Again:
+ chunk = min(bufsz, count - total);
+
+Drain:
+ WaitForSingleObject(outlock, INFINITE);
+ while(out_buf_count > bufsz) {
+ ReleaseMutex(outlock);
+ sleep(0);
+ goto Drain;
+ }
+
+ if(out_buf_count == 0)
+ audioerror(waveOutReset(audio_file_out), AUDIOOUT, "wave out reset");
+ ReleaseMutex(outlock);
+
+ /*
+ * allocate and lock the memory for the wave header
+ * and data blocks
+ */
+ hHdr = (WAVEHDR *) malloc(sizeof(WAVEHDR));
+ if (!hHdr)
+ error(Enomem);
+
+ hData = malloc(chunk);
+ if (!hData) {
+ free(hHdr);
+ error(Enomem);
+ }
+
+ /*
+ * initialize the wave header struct
+ */
+
+ /*
+ * copy user data into write Q
+ */
+ memmove(hData, p+total, chunk);
+
+ hHdr->lpData = hData;
+ hHdr->dwBufferLength = chunk;
+ hHdr->dwBytesRecorded = 0;
+ hHdr->dwUser = chunk;
+ hHdr->dwFlags = 0;
+ hHdr->dwLoops = 0;
+ hHdr->lpNext = 0;
+ hHdr->reserved = 0;
+
+ status = waveOutPrepareHeader(audio_file_out, hHdr, sizeof(WAVEHDR));
+
+ if (audioerror(status, AUDIOOUT, "out prepare header") == -1) {
+ free(hHdr);
+ free(hData);
+ error(Eio);
+ }
+
+ status =
+ waveOutWrite(audio_file_out, hHdr, sizeof(WAVEHDR));
+
+ if (audioerror(status, AUDIOOUT, "out write data") == -1) {
+ waveOutUnprepareHeader(audio_file_out, hHdr, sizeof(WAVEHDR));
+ free(hHdr);
+ free(hData);
+ error(Eio);
+ }
+
+ WaitForSingleObject(outlock, INFINITE);
+ out_buf_count += chunk;
+ ReleaseMutex(outlock);
+
+ total += chunk;
+
+ }
+
+ poperror();
+ qunlock(&flag_lock);
+ osmillisleep(1); /* hack to get around thread scheduler */
+
+ return count;
+}
+
+void CALLBACK
+waveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
+{
+ LPWAVEHDR hHdr;
+ long count;
+
+ switch(uMsg) {
+ case WIM_OPEN:
+ break;
+ case WIM_CLOSE:
+ break;
+ case WIM_DATA:
+ hHdr = (LPWAVEHDR)dwParam1;
+ if(hHdr != NULL) {
+ count = hHdr->dwBytesRecorded;
+ if(count > 0) {
+ WaitForSingleObject(inlock, INFINITE);
+ if(hHdr->dwUser == Ping)
+ audio_ping.sz = count;
+ else
+ audio_pong.sz = count;
+ ReleaseMutex(inlock);
+ }
+ }
+ break;
+ }
+ return;
+}
+
+
+void CALLBACK
+waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwOutstance, DWORD dwParam1, DWORD dwParam2)
+{
+ LPWAVEHDR hHdr;
+
+ switch(uMsg) {
+ case WOM_DONE:
+ hHdr = (LPWAVEHDR)dwParam1;
+ if(hHdr != NULL) {
+ WaitForSingleObject(outlock, INFINITE);
+ out_buf_count -= hHdr->dwUser;
+ ReleaseMutex(outlock);
+ audioerror(
+ waveOutUnprepareHeader(
+ audio_file_out, hHdr, sizeof(WAVEHDR)),
+ AUDIOOUT, "out un prepare header");
+ if(hHdr->lpData != NULL)
+ free(hHdr->lpData);
+ free(hHdr);
+ }
+ break;
+ case WOM_CLOSE:
+ WaitForSingleObject(outlock, INFINITE);
+ out_buf_count = 0;
+ ReleaseMutex(outlock);
+ break;
+ case WOM_OPEN:
+ break;
+ }
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ WAVEFORMATEX format;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if(!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if((tmpav.in.enc != Audio_Pcm_Val) || (tmpav.out.enc != Audio_Pcm_Val))
+ error(Ebadarg);
+
+ if(tmpav.in.flags & AUDIO_MOD_FLAG) {
+ format.wFormatTag = tmpav.in.enc;
+ format.wBitsPerSample = tmpav.in.bits;
+ format.nChannels = tmpav.in.chan;
+ format.nSamplesPerSec = tmpav.in.rate;
+ format.nBlockAlign =
+ (tmpav.in.chan * tmpav.in.bits) / Bits_Per_Byte;
+ format.nAvgBytesPerSec =
+ format.nSamplesPerSec * format.nBlockAlign;
+ format.cbSize = 0;
+
+ if(audioerror(
+ waveInOpen(NULL, WAVE_MAPPER, &format, 0, 0, WAVE_FORMAT_QUERY),
+ AUDIOIN, "cannot open microphone/line-in to test parameters") == -1)
+ error(Ebadarg);
+
+ qlock(&flag_lock);
+
+ if(waserror()){
+ qunlock(&flag_lock);
+ nexterror();
+ }
+
+ if(audio_flags & INISOPEN) {
+ audio_close_in();
+ audio_flags &= ~INISOPEN;
+ audio_flags &= ~INPUTISGOING;
+ if(!audio_open_in(&audio_file_in, &tmpav.in))
+ error(Eio);
+ audio_flags |= INISOPEN;
+ }
+ poperror();
+ qunlock(&flag_lock);
+ }
+
+ if(tmpav.out.flags & AUDIO_MOD_FLAG) {
+
+ format.wFormatTag = tmpav.out.enc;
+ format.wBitsPerSample = tmpav.out.bits;
+ format.nChannels = tmpav.out.chan;
+ format.nSamplesPerSec = tmpav.out.rate;
+ format.nBlockAlign =
+ (tmpav.out.chan * tmpav.out.bits) / Bits_Per_Byte;
+ format.nAvgBytesPerSec =
+ format.nSamplesPerSec * format.nBlockAlign;
+ format.cbSize = 0;
+
+ if (audioerror(waveOutOpen(NULL, WAVE_MAPPER,
+ &format,
+ 0, 0, WAVE_FORMAT_QUERY),
+ AUDIOOUT, "cannot open output to test parameters") == -1)
+ error(Ebadarg);
+
+ qlock(&flag_lock);
+ if(waserror()){
+ qunlock(&flag_lock);
+ nexterror();
+ }
+ if(audio_flags & OUTISOPEN) {
+ audio_close_out();
+
+ audio_flags &= ~OUTISOPEN;
+ if(!audio_open_out(&audio_file_out, &tmpav.out)) {
+ error(Eio);
+ return -1;
+ }
+ audio_flags |= OUTISOPEN;
+ }
+ poperror();
+ qunlock(&flag_lock);
+ }
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}