Home | History | Annotate | Download | only in audio
      1 /*
      2  * QEMU "simple" Windows audio driver
      3  *
      4  * Copyright (c) 2007 The Android Open Source Project
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a copy
      7  * of this software and associated documentation files (the "Software"), to deal
      8  * in the Software without restriction, including without limitation the rights
      9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10  * copies of the Software, and to permit persons to whom the Software is
     11  * furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     22  * THE SOFTWARE.
     23  */
     24 #define WIN32_LEAN_AND_MEAN
     25 #include <windows.h>
     26 #include <mmsystem.h>
     27 
     28 #define AUDIO_CAP "winaudio"
     29 #include "audio_int.h"
     30 
     31 /* define DEBUG to 1 to dump audio debugging info at runtime to stderr */
     32 #define  DEBUG  0
     33 
     34 #if 1
     35 #  define  D_ACTIVE    1
     36 #else
     37 #  define  D_ACTIVE  DEBUG
     38 #endif
     39 
     40 #if DEBUG
     41 #  define  D(...)   do{ if (D_ACTIVE) printf(__VA_ARGS__); } while(0)
     42 #else
     43 #  define  D(...)   ((void)0)
     44 #endif
     45 
     46 static struct {
     47     int nb_samples;
     48 } conf = {
     49     1024
     50 };
     51 
     52 #if DEBUG
     53 int64_t  start_time;
     54 int64_t  last_time;
     55 #endif
     56 
     57 #define  NUM_OUT_BUFFERS  8  /* must be at least 2 */
     58 
     59 /** COMMON UTILITIES
     60  **/
     61 
     62 #if DEBUG
     63 static void
     64 dump_mmerror( const char*  func, MMRESULT  error )
     65 {
     66     const char*  reason = NULL;
     67 
     68     fprintf(stderr, "%s returned error: ", func);
     69     switch (error) {
     70             case MMSYSERR_ALLOCATED:   reason="specified resource is already allocated"; break;
     71             case MMSYSERR_BADDEVICEID: reason="bad device id"; break;
     72             case MMSYSERR_NODRIVER:    reason="no driver is present"; break;
     73             case MMSYSERR_NOMEM:       reason="unable to allocate or lock memory"; break;
     74             case WAVERR_BADFORMAT:     reason="unsupported waveform-audio format"; break;
     75             case WAVERR_SYNC:          reason="device is synchronous"; break;
     76             default:
     77                     fprintf(stderr, "unknown(%d)\n", error);
     78     }
     79     if (reason)
     80             fprintf(stderr, "%s\n", reason);
     81 }
     82 #else
     83 #  define  dump_mmerror(func,error)  ((void)0)
     84 #endif
     85 
     86 
     87 /** AUDIO OUT
     88  **/
     89 
     90 typedef struct WinAudioOut {
     91     HWVoiceOut        hw;
     92     HWAVEOUT          waveout;
     93     int               silence;
     94     CRITICAL_SECTION  lock;
     95     unsigned char*    buffer_bytes;
     96     WAVEHDR           buffers[ NUM_OUT_BUFFERS ];
     97     int               write_index;   /* starting first writable buffer      */
     98     int               write_count;   /* available writable buffers count    */
     99     int               write_pos;     /* position in current writable buffer */
    100     int               write_size;    /* size in bytes of each buffer        */
    101 } WinAudioOut;
    102 
    103 /* The Win32 callback that is called when a buffer has finished playing */
    104 static void CALLBACK
    105 winaudio_out_buffer_done (HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
    106 			              DWORD dwParam1, DWORD dwParam2)
    107 {
    108     WinAudioOut*  s = (WinAudioOut*) dwInstance;
    109 
    110     /* Only service "buffer done playing" messages */
    111     if ( uMsg != WOM_DONE )
    112             return;
    113 
    114     /* Signal that we are done playing a buffer */
    115     EnterCriticalSection( &s->lock );
    116     if (s->write_count < NUM_OUT_BUFFERS)
    117         s->write_count += 1;
    118     LeaveCriticalSection( &s->lock );
    119 }
    120 
    121 static int
    122 winaudio_out_write (SWVoiceOut *sw, void *buf, int len)
    123 {
    124     return audio_pcm_sw_write (sw, buf, len);
    125 }
    126 
    127 static void
    128 winaudio_out_fini (HWVoiceOut *hw)
    129 {
    130     WinAudioOut*  s = (WinAudioOut*) hw;
    131     int           i;
    132 
    133     if (s->waveout) {
    134         waveOutReset(s->waveout);
    135         s->waveout = 0;
    136     }
    137 
    138     for ( i=0; i<NUM_OUT_BUFFERS; ++i ) {
    139         if ( s->buffers[i].dwUser != 0xFFFF ) {
    140             waveOutUnprepareHeader(
    141                 s->waveout, &s->buffers[i], sizeof(s->buffers[i]) );
    142                 s->buffers[i].dwUser = 0xFFFF;
    143         }
    144     }
    145 
    146     if (s->buffer_bytes != NULL) {
    147         g_free(s->buffer_bytes);
    148         s->buffer_bytes = NULL;
    149     }
    150 
    151     if (s->waveout) {
    152         waveOutClose(s->waveout);
    153         s->waveout = NULL;
    154     }
    155 }
    156 
    157 
    158 static int
    159 winaudio_out_init (HWVoiceOut *hw, struct audsettings *as)
    160 {
    161     WinAudioOut*   s = (WinAudioOut*) hw;
    162     MMRESULT       result;
    163     WAVEFORMATEX   format;
    164     int            shift, i, samples_size;
    165 
    166     s->waveout = NULL;
    167     InitializeCriticalSection( &s->lock );
    168     for (i = 0; i < NUM_OUT_BUFFERS; i++) {
    169             s->buffers[i].dwUser = 0xFFFF;
    170     }
    171     s->buffer_bytes = NULL;
    172 
    173     /* compute desired wave output format */
    174     format.wFormatTag      = WAVE_FORMAT_PCM;
    175     format.nChannels       = as->nchannels;
    176     format.nSamplesPerSec  = as->freq;
    177     format.nAvgBytesPerSec = as->freq*as->nchannels;
    178 
    179     s->silence = 0;
    180 
    181     switch (as->fmt) {
    182         case AUD_FMT_S8:   shift = 0; break;
    183         case AUD_FMT_U8:   shift = 0; s->silence = 0x80; break;
    184         case AUD_FMT_S16:  shift = 1; break;
    185         case AUD_FMT_U16:  shift = 1; s->silence = 0x8000; break;
    186         default:
    187             fprintf(stderr, "qemu: winaudio: Bad output audio format: %d\n",
    188                     as->fmt);
    189                 return -1;
    190     }
    191 
    192     format.nAvgBytesPerSec = (format.nSamplesPerSec & format.nChannels) << shift;
    193     format.nBlockAlign     = format.nChannels << shift;
    194     format.wBitsPerSample  = 8 << shift;
    195     format.cbSize          = 0;
    196 
    197     /* open the wave out device */
    198     result = waveOutOpen( &s->waveout, WAVE_MAPPER, &format,
    199                                   (DWORD_PTR)winaudio_out_buffer_done, (DWORD_PTR) hw,
    200                                               CALLBACK_FUNCTION);
    201     if ( result != MMSYSERR_NOERROR ) {
    202         dump_mmerror( "qemu: winaudio: waveOutOpen()", result);
    203             return -1;
    204     }
    205 
    206     samples_size    = format.nBlockAlign * conf.nb_samples;
    207     s->buffer_bytes = g_malloc( NUM_OUT_BUFFERS * samples_size );
    208     if (s->buffer_bytes == NULL) {
    209             waveOutClose( s->waveout );
    210             s->waveout = NULL;
    211             fprintf(stderr, "not enough memory for Windows audio buffers\n");
    212             return -1;
    213     }
    214 
    215     for (i = 0; i < NUM_OUT_BUFFERS; i++) {
    216         memset( &s->buffers[i], 0, sizeof(s->buffers[i]) );
    217         s->buffers[i].lpData         = (LPSTR)(s->buffer_bytes + i*samples_size);
    218         s->buffers[i].dwBufferLength = samples_size;
    219         s->buffers[i].dwFlags        = WHDR_DONE;
    220 
    221         result = waveOutPrepareHeader( s->waveout, &s->buffers[i],
    222                                sizeof(s->buffers[i]) );
    223         if ( result != MMSYSERR_NOERROR ) {
    224             dump_mmerror("waveOutPrepareHeader()", result);
    225             return -1;
    226         }
    227     }
    228 
    229 #if DEBUG
    230     /* Check the sound device we retrieved */
    231     {
    232         WAVEOUTCAPS caps;
    233 
    234         result = waveOutGetDevCaps((UINT) s->waveout, &caps, sizeof(caps));
    235         if ( result != MMSYSERR_NOERROR ) {
    236             dump_mmerror("waveOutGetDevCaps()", result);
    237         } else
    238             printf("Audio out device: %s\n", caps.szPname);
    239     }
    240 #endif
    241 
    242     audio_pcm_init_info (&hw->info, as);
    243     hw->samples = conf.nb_samples*2;
    244 
    245     s->write_index = 0;
    246     s->write_count = NUM_OUT_BUFFERS;
    247     s->write_pos   = 0;
    248     s->write_size  = samples_size;
    249     return 0;
    250 }
    251 
    252 
    253 static int
    254 winaudio_out_run (HWVoiceOut *hw, int live)
    255 {
    256     WinAudioOut*  s      = (WinAudioOut*) hw;
    257     int           played = 0;
    258     int           has_buffer;
    259 
    260     if (!live) {
    261         return 0;
    262     }
    263 
    264     EnterCriticalSection( &s->lock );
    265     has_buffer = (s->write_count > 0);
    266     LeaveCriticalSection( &s->lock );
    267 
    268     if (has_buffer) {
    269         while (live > 0) {
    270             WAVEHDR*      wav_buffer  = s->buffers + s->write_index;
    271             int           wav_bytes   = (s->write_size - s->write_pos);
    272             int           wav_samples = audio_MIN(wav_bytes >> hw->info.shift, live);
    273             int           hw_samples  = audio_MIN(hw->samples - hw->rpos, live);
    274             struct st_sample*  src         = hw->mix_buf + hw->rpos;
    275             uint8_t*      dst         = (uint8_t*)wav_buffer->lpData + s->write_pos;
    276 
    277             if (wav_samples > hw_samples) {
    278                     wav_samples = hw_samples;
    279             }
    280 
    281             wav_bytes = wav_samples << hw->info.shift;
    282 
    283             //D("run_out: buffer:%d pos:%d size:%d wsamples:%d wbytes:%d live:%d rpos:%d hwsamples:%d\n", s->write_index,
    284             //   s->write_pos, s->write_size, wav_samples, wav_bytes, live, hw->rpos, hw->samples);
    285             hw->clip (dst, src, wav_samples);
    286             hw->rpos += wav_samples;
    287             if (hw->rpos >= hw->samples)
    288                     hw->rpos -= hw->samples;
    289 
    290             live         -= wav_samples;
    291             played       += wav_samples;
    292             s->write_pos += wav_bytes;
    293             if (s->write_pos == s->write_size) {
    294 #if xxDEBUG
    295                 int64_t  now  = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - start_time;
    296                 int64_t  diff = now - last_time;
    297 
    298                 D("run_out: (%7.3f:%7d):waveOutWrite buffer:%d\n",
    299                    now/1e9, (now-last_time)/1e9, s->write_index);
    300                 last_time = now;
    301 #endif
    302                 waveOutWrite( s->waveout, wav_buffer, sizeof(*wav_buffer) );
    303                 s->write_pos    = 0;
    304                 s->write_index += 1;
    305                 if (s->write_index == NUM_OUT_BUFFERS)
    306                     s->write_index = 0;
    307 
    308                 EnterCriticalSection( &s->lock );
    309                 if (--s->write_count == 0) {
    310                         live = 0;
    311                 }
    312                 LeaveCriticalSection( &s->lock );
    313             }
    314         }
    315 
    316     }
    317     return played;
    318 }
    319 
    320 static int
    321 winaudio_out_ctl (HWVoiceOut *hw, int cmd, ...)
    322 {
    323     WinAudioOut*  s = (WinAudioOut*) hw;
    324 
    325     switch (cmd) {
    326     case VOICE_ENABLE:
    327         waveOutRestart( s->waveout );
    328         break;
    329 
    330     case VOICE_DISABLE:
    331         waveOutPause( s->waveout );
    332         break;
    333     }
    334     return 0;
    335 }
    336 
    337 /** AUDIO IN
    338  **/
    339 
    340 #define  NUM_IN_BUFFERS  2
    341 
    342 typedef struct WinAudioIn {
    343     HWVoiceIn         hw;
    344     HWAVEIN           wavein;
    345     CRITICAL_SECTION  lock;
    346     unsigned char*    buffer_bytes;
    347     WAVEHDR           buffers[ NUM_IN_BUFFERS ];
    348     int               read_index;
    349     int               read_count;
    350     int               read_pos;
    351     int               read_size;
    352 } WinAudioIn;
    353 
    354 /* The Win32 callback that is called when a buffer has finished playing */
    355 static void CALLBACK
    356 winaudio_in_buffer_done (HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance,
    357                          DWORD dwParam1, DWORD dwParam2)
    358 {
    359     WinAudioIn*  s = (WinAudioIn*) dwInstance;
    360 
    361     /* Only service "buffer done playing" messages */
    362     if ( uMsg != WIM_DATA )
    363         return;
    364 
    365     /* Signal that we are done playing a buffer */
    366     EnterCriticalSection( &s->lock );
    367     if (s->read_count < NUM_IN_BUFFERS)
    368         s->read_count += 1;
    369         //D(".%c",s->read_count + '0'); fflush(stdout);
    370     LeaveCriticalSection( &s->lock );
    371 }
    372 
    373 static void
    374 winaudio_in_fini (HWVoiceIn *hw)
    375 {
    376     WinAudioIn*  s = (WinAudioIn*) hw;
    377     int           i;
    378 
    379     if (s->wavein) {
    380         waveInReset(s->wavein);
    381             s->wavein = 0;
    382     }
    383 
    384     for ( i=0; i<NUM_IN_BUFFERS; ++i ) {
    385         if ( s->buffers[i].dwUser != 0xFFFF ) {
    386             waveInUnprepareHeader(
    387                 s->wavein, &s->buffers[i], sizeof(s->buffers[i]) );
    388                 s->buffers[i].dwUser = 0xFFFF;
    389         }
    390     }
    391 
    392     if (s->buffer_bytes != NULL) {
    393         g_free(s->buffer_bytes);
    394         s->buffer_bytes = NULL;
    395     }
    396 
    397     if (s->wavein) {
    398         waveInClose(s->wavein);
    399         s->wavein = NULL;
    400     }
    401 }
    402 
    403 
    404 static int
    405 winaudio_in_init (HWVoiceIn *hw, struct audsettings *as)
    406 {
    407     WinAudioIn*   s = (WinAudioIn*) hw;
    408     MMRESULT       result;
    409     WAVEFORMATEX   format;
    410     int            shift, i, samples_size;
    411 
    412     s->wavein = NULL;
    413     InitializeCriticalSection( &s->lock );
    414     for (i = 0; i < NUM_IN_BUFFERS; i++) {
    415             s->buffers[i].dwUser = 0xFFFF;
    416     }
    417     s->buffer_bytes = NULL;
    418 
    419     /* compute desired wave input format */
    420     format.wFormatTag      = WAVE_FORMAT_PCM;
    421     format.nChannels       = as->nchannels;
    422     format.nSamplesPerSec  = as->freq;
    423     format.nAvgBytesPerSec = as->freq*as->nchannels;
    424 
    425     switch (as->fmt) {
    426         case AUD_FMT_S8:   shift = 0; break;
    427         case AUD_FMT_U8:   shift = 0; break;
    428         case AUD_FMT_S16:  shift = 1; break;
    429         case AUD_FMT_U16:  shift = 1; break;
    430         default:
    431             fprintf(stderr, "qemu: winaudio: Bad input audio format: %d\n",
    432                     as->fmt);
    433                 return -1;
    434     }
    435 
    436     format.nAvgBytesPerSec = (format.nSamplesPerSec * format.nChannels) << shift;
    437     format.nBlockAlign     = format.nChannels << shift;
    438     format.wBitsPerSample  = 8 << shift;
    439     format.cbSize          = 0;
    440 
    441     /* open the wave in device */
    442     result = waveInOpen( &s->wavein, WAVE_MAPPER, &format,
    443                          (DWORD_PTR)winaudio_in_buffer_done, (DWORD_PTR) hw,
    444                          CALLBACK_FUNCTION);
    445     if ( result != MMSYSERR_NOERROR ) {
    446         dump_mmerror( "qemu: winaudio: waveInOpen()", result);
    447             return -1;
    448     }
    449 
    450     samples_size    = format.nBlockAlign * conf.nb_samples;
    451     s->buffer_bytes = g_malloc( NUM_IN_BUFFERS * samples_size );
    452     if (s->buffer_bytes == NULL) {
    453             waveInClose( s->wavein );
    454             s->wavein = NULL;
    455             fprintf(stderr, "not enough memory for Windows audio buffers\n");
    456             return -1;
    457     }
    458 
    459     for (i = 0; i < NUM_IN_BUFFERS; i++) {
    460         memset( &s->buffers[i], 0, sizeof(s->buffers[i]) );
    461         s->buffers[i].lpData         = (LPSTR)(s->buffer_bytes + i*samples_size);
    462         s->buffers[i].dwBufferLength = samples_size;
    463         s->buffers[i].dwFlags        = WHDR_DONE;
    464 
    465         result = waveInPrepareHeader( s->wavein, &s->buffers[i],
    466                                sizeof(s->buffers[i]) );
    467         if ( result != MMSYSERR_NOERROR ) {
    468                 dump_mmerror("waveInPrepareHeader()", result);
    469                 return -1;
    470         }
    471 
    472         result = waveInAddBuffer( s->wavein, &s->buffers[i],
    473                               sizeof(s->buffers[i]) );
    474         if ( result != MMSYSERR_NOERROR ) {
    475             dump_mmerror("waveInAddBuffer()", result);
    476             return -1;
    477         }
    478     }
    479 
    480 #if DEBUG
    481     /* Check the sound device we retrieved */
    482     {
    483         WAVEINCAPS caps;
    484 
    485         result = waveInGetDevCaps((UINT) s->wavein, &caps, sizeof(caps));
    486         if ( result != MMSYSERR_NOERROR ) {
    487             dump_mmerror("waveInGetDevCaps()", result);
    488         } else
    489             printf("Audio in device: %s\n", caps.szPname);
    490     }
    491 #endif
    492 
    493     audio_pcm_init_info (&hw->info, as);
    494     hw->samples = conf.nb_samples*2;
    495 
    496     s->read_index = 0;
    497     s->read_count = 0;
    498     s->read_pos   = 0;
    499     s->read_size  = samples_size;
    500     return 0;
    501 }
    502 
    503 
    504 /* report the number of captured samples to the audio subsystem */
    505 static int
    506 winaudio_in_run (HWVoiceIn *hw)
    507 {
    508     WinAudioIn*  s        = (WinAudioIn*) hw;
    509     int          captured = 0;
    510     int          has_buffer;
    511     int          live = hw->samples - hw->total_samples_captured;
    512 
    513     if (!live) {
    514 #if 0
    515         static int  counter;
    516         if (++counter == 100) {
    517             D("0"); fflush(stdout);
    518             counter = 0;
    519         }
    520 #endif
    521             return 0;
    522     }
    523 
    524     EnterCriticalSection( &s->lock );
    525     has_buffer = (s->read_count > 0);
    526     LeaveCriticalSection( &s->lock );
    527 
    528     if (has_buffer > 0) {
    529         while (live > 0) {
    530             WAVEHDR*      wav_buffer  = s->buffers + s->read_index;
    531             int           wav_bytes   = (s->read_size - s->read_pos);
    532             int           wav_samples = audio_MIN(wav_bytes >> hw->info.shift, live);
    533             int           hw_samples  = audio_MIN(hw->samples - hw->wpos, live);
    534             struct st_sample*  dst    = hw->conv_buf + hw->wpos;
    535             uint8_t*      src         = (uint8_t*)wav_buffer->lpData + s->read_pos;
    536 
    537             if (wav_samples > hw_samples) {
    538                 wav_samples = hw_samples;
    539             }
    540 
    541             wav_bytes = wav_samples << hw->info.shift;
    542 
    543             D("%s: buffer:%d pos:%d size:%d wsamples:%d wbytes:%d live:%d wpos:%d hwsamples:%d\n",
    544                __FUNCTION__, s->read_index, s->read_pos, s->read_size, wav_samples, wav_bytes, live,
    545                hw->wpos, hw->samples);
    546 
    547             hw->conv(dst, src, wav_samples, &nominal_volume);
    548 
    549             hw->wpos += wav_samples;
    550             if (hw->wpos >= hw->samples)
    551                 hw->wpos -= hw->samples;
    552 
    553             live        -= wav_samples;
    554             captured    += wav_samples;
    555             s->read_pos += wav_bytes;
    556             if (s->read_pos == s->read_size) {
    557                 s->read_pos    = 0;
    558                 s->read_index += 1;
    559                 if (s->read_index == NUM_IN_BUFFERS)
    560                     s->read_index = 0;
    561 
    562                 waveInAddBuffer( s->wavein, wav_buffer, sizeof(*wav_buffer) );
    563 
    564                 EnterCriticalSection( &s->lock );
    565                 if (--s->read_count == 0) {
    566                     live = 0;
    567                 }
    568                 LeaveCriticalSection( &s->lock );
    569             }
    570         }
    571     }
    572     return  captured;
    573 }
    574 
    575 
    576 static int
    577 winaudio_in_read (SWVoiceIn *sw, void *buf, int len)
    578 {
    579     int  ret = audio_pcm_sw_read (sw, buf, len);
    580     if (ret > 0)
    581         D("%s: (%d) returned %d\n", __FUNCTION__, len, ret);
    582     return ret;
    583 }
    584 
    585 
    586 static int
    587 winaudio_in_ctl (HWVoiceIn *hw, int cmd, ...)
    588 {
    589 	WinAudioIn*  s = (WinAudioIn*) hw;
    590 
    591     switch (cmd) {
    592     case VOICE_ENABLE:
    593         D("%s: enable audio in\n", __FUNCTION__);
    594         waveInStart( s->wavein );
    595         break;
    596 
    597     case VOICE_DISABLE:
    598         D("%s: disable audio in\n", __FUNCTION__);
    599         waveInStop( s->wavein );
    600         break;
    601     }
    602     return 0;
    603 }
    604 
    605 /** AUDIO STATE
    606  **/
    607 
    608 typedef struct WinAudioState {
    609     int  dummy;
    610 } WinAudioState;
    611 
    612 static WinAudioState  g_winaudio;
    613 
    614 static void*
    615 winaudio_init(void)
    616 {
    617     WinAudioState*  s = &g_winaudio;
    618 
    619 #if DEBUG
    620     start_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    621     last_time  = 0;
    622 #endif
    623 
    624     return s;
    625 }
    626 
    627 
    628 static void
    629 winaudio_fini (void *opaque)
    630 {
    631 }
    632 
    633 static struct audio_option winaudio_options[] = {
    634     {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
    635      "Size of Windows audio buffer in samples", NULL, 0},
    636     {NULL, 0, NULL, NULL, NULL, 0}
    637 };
    638 
    639 static struct audio_pcm_ops winaudio_pcm_ops = {
    640     winaudio_out_init,
    641     winaudio_out_fini,
    642     winaudio_out_run,
    643     winaudio_out_write,
    644     winaudio_out_ctl,
    645 
    646     winaudio_in_init,
    647     winaudio_in_fini,
    648     winaudio_in_run,
    649     winaudio_in_read,
    650     winaudio_in_ctl
    651 };
    652 
    653 struct audio_driver win_audio_driver = {
    654     INIT_FIELD (name           = ) "winaudio",
    655     INIT_FIELD (descr          = ) "Windows wave audio",
    656     INIT_FIELD (options        = ) winaudio_options,
    657     INIT_FIELD (init           = ) winaudio_init,
    658     INIT_FIELD (fini           = ) winaudio_fini,
    659     INIT_FIELD (pcm_ops        = ) &winaudio_pcm_ops,
    660     INIT_FIELD (can_be_default = ) 1,
    661     INIT_FIELD (max_voices_out = ) 1,
    662     INIT_FIELD (max_voices_in  = ) 1,
    663     INIT_FIELD (voice_size_out = ) sizeof (WinAudioOut),
    664     INIT_FIELD (voice_size_in  = ) sizeof (WinAudioIn)
    665 };
    666