mirror of
				https://github.com/qemu/qemu.git
				synced 2025-10-31 12:07:31 +00:00 
			
		
		
		
	 7267c0947d
			
		
	
	
		7267c0947d
		
	
	
	
	
		
			
			qemu_malloc/qemu_free no longer exist after this commit. Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
		
			
				
	
	
		
			724 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			724 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* public domain */
 | |
| 
 | |
| #include "qemu-common.h"
 | |
| #include "sysemu.h"
 | |
| #include "audio.h"
 | |
| 
 | |
| #define AUDIO_CAP "winwave"
 | |
| #include "audio_int.h"
 | |
| 
 | |
| #include <windows.h>
 | |
| #include <mmsystem.h>
 | |
| 
 | |
| #include "audio_win_int.h"
 | |
| 
 | |
| static struct {
 | |
|     int dac_headers;
 | |
|     int dac_samples;
 | |
|     int adc_headers;
 | |
|     int adc_samples;
 | |
| } conf = {
 | |
|     .dac_headers = 4,
 | |
|     .dac_samples = 1024,
 | |
|     .adc_headers = 4,
 | |
|     .adc_samples = 1024
 | |
| };
 | |
| 
 | |
| typedef struct {
 | |
|     HWVoiceOut hw;
 | |
|     HWAVEOUT hwo;
 | |
|     WAVEHDR *hdrs;
 | |
|     HANDLE event;
 | |
|     void *pcm_buf;
 | |
|     int avail;
 | |
|     int pending;
 | |
|     int curhdr;
 | |
|     int paused;
 | |
|     CRITICAL_SECTION crit_sect;
 | |
| } WaveVoiceOut;
 | |
| 
 | |
| typedef struct {
 | |
|     HWVoiceIn hw;
 | |
|     HWAVEIN hwi;
 | |
|     WAVEHDR *hdrs;
 | |
|     HANDLE event;
 | |
|     void *pcm_buf;
 | |
|     int curhdr;
 | |
|     int paused;
 | |
|     int rpos;
 | |
|     int avail;
 | |
|     CRITICAL_SECTION crit_sect;
 | |
| } WaveVoiceIn;
 | |
| 
 | |
| static void winwave_log_mmresult (MMRESULT mr)
 | |
| {
 | |
|     const char *str = "BUG";
 | |
| 
 | |
|     switch (mr) {
 | |
|     case MMSYSERR_NOERROR:
 | |
|         str = "Success";
 | |
|         break;
 | |
| 
 | |
|     case MMSYSERR_INVALHANDLE:
 | |
|         str = "Specified device handle is invalid";
 | |
|         break;
 | |
| 
 | |
|     case MMSYSERR_BADDEVICEID:
 | |
|         str = "Specified device id is out of range";
 | |
|         break;
 | |
| 
 | |
|     case MMSYSERR_NODRIVER:
 | |
|         str = "No device driver is present";
 | |
|         break;
 | |
| 
 | |
|     case MMSYSERR_NOMEM:
 | |
|         str = "Unable to allocate or locl memory";
 | |
|         break;
 | |
| 
 | |
|     case WAVERR_SYNC:
 | |
|         str = "Device is synchronous but waveOutOpen was called "
 | |
|             "without using the WINWAVE_ALLOWSYNC flag";
 | |
|         break;
 | |
| 
 | |
|     case WAVERR_UNPREPARED:
 | |
|         str = "The data block pointed to by the pwh parameter "
 | |
|             "hasn't been prepared";
 | |
|         break;
 | |
| 
 | |
|     case WAVERR_STILLPLAYING:
 | |
|         str = "There are still buffers in the queue";
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         dolog ("Reason: Unknown (MMRESULT %#x)\n", mr);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     dolog ("Reason: %s\n", str);
 | |
| }
 | |
| 
 | |
| static void GCC_FMT_ATTR (2, 3) winwave_logerr (
 | |
|     MMRESULT mr,
 | |
|     const char *fmt,
 | |
|     ...
 | |
|     )
 | |
| {
 | |
|     va_list ap;
 | |
| 
 | |
|     va_start (ap, fmt);
 | |
|     AUD_vlog (AUDIO_CAP, fmt, ap);
 | |
|     va_end (ap);
 | |
| 
 | |
|     AUD_log (NULL, " failed\n");
 | |
|     winwave_log_mmresult (mr);
 | |
| }
 | |
| 
 | |
| static void winwave_anal_close_out (WaveVoiceOut *wave)
 | |
| {
 | |
|     MMRESULT mr;
 | |
| 
 | |
|     mr = waveOutClose (wave->hwo);
 | |
|     if (mr != MMSYSERR_NOERROR) {
 | |
|         winwave_logerr (mr, "waveOutClose");
 | |
|     }
 | |
|     wave->hwo = NULL;
 | |
| }
 | |
| 
 | |
| static void CALLBACK winwave_callback_out (
 | |
|     HWAVEOUT hwo,
 | |
|     UINT msg,
 | |
|     DWORD_PTR dwInstance,
 | |
|     DWORD_PTR dwParam1,
 | |
|     DWORD_PTR dwParam2
 | |
|     )
 | |
| {
 | |
|     WaveVoiceOut *wave = (WaveVoiceOut *) dwInstance;
 | |
| 
 | |
|     switch (msg) {
 | |
|     case WOM_DONE:
 | |
|         {
 | |
|             WAVEHDR *h = (WAVEHDR *) dwParam1;
 | |
|             if (!h->dwUser) {
 | |
|                 h->dwUser = 1;
 | |
|                 EnterCriticalSection (&wave->crit_sect);
 | |
|                 {
 | |
|                     wave->avail += conf.dac_samples;
 | |
|                 }
 | |
|                 LeaveCriticalSection (&wave->crit_sect);
 | |
|                 if (wave->hw.poll_mode) {
 | |
|                     if (!SetEvent (wave->event)) {
 | |
|                         dolog ("DAC SetEvent failed %lx\n", GetLastError ());
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case WOM_CLOSE:
 | |
|     case WOM_OPEN:
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         dolog ("unknown wave out callback msg %x\n", msg);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as)
 | |
| {
 | |
|     int i;
 | |
|     int err;
 | |
|     MMRESULT mr;
 | |
|     WAVEFORMATEX wfx;
 | |
|     WaveVoiceOut *wave;
 | |
| 
 | |
|     wave = (WaveVoiceOut *) hw;
 | |
| 
 | |
|     InitializeCriticalSection (&wave->crit_sect);
 | |
| 
 | |
|     err = waveformat_from_audio_settings (&wfx, as);
 | |
|     if (err) {
 | |
|         goto err0;
 | |
|     }
 | |
| 
 | |
|     mr = waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx,
 | |
|                       (DWORD_PTR) winwave_callback_out,
 | |
|                       (DWORD_PTR) wave, CALLBACK_FUNCTION);
 | |
|     if (mr != MMSYSERR_NOERROR) {
 | |
|         winwave_logerr (mr, "waveOutOpen");
 | |
|         goto err1;
 | |
|     }
 | |
| 
 | |
|     wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
 | |
|                                sizeof (*wave->hdrs));
 | |
|     if (!wave->hdrs) {
 | |
|         goto err2;
 | |
|     }
 | |
| 
 | |
|     audio_pcm_init_info (&hw->info, as);
 | |
|     hw->samples = conf.dac_samples * conf.dac_headers;
 | |
|     wave->avail = hw->samples;
 | |
| 
 | |
|     wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.dac_samples,
 | |
|                                   conf.dac_headers << hw->info.shift);
 | |
|     if (!wave->pcm_buf) {
 | |
|         goto err3;
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < conf.dac_headers; ++i) {
 | |
|         WAVEHDR *h = &wave->hdrs[i];
 | |
| 
 | |
|         h->dwUser = 0;
 | |
|         h->dwBufferLength = conf.dac_samples << hw->info.shift;
 | |
|         h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
 | |
|         h->dwFlags = 0;
 | |
| 
 | |
|         mr = waveOutPrepareHeader (wave->hwo, h, sizeof (*h));
 | |
|         if (mr != MMSYSERR_NOERROR) {
 | |
|             winwave_logerr (mr, "waveOutPrepareHeader(%d)", i);
 | |
|             goto err4;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| 
 | |
|  err4:
 | |
|     g_free (wave->pcm_buf);
 | |
|  err3:
 | |
|     g_free (wave->hdrs);
 | |
|  err2:
 | |
|     winwave_anal_close_out (wave);
 | |
|  err1:
 | |
|  err0:
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| static int winwave_write (SWVoiceOut *sw, void *buf, int len)
 | |
| {
 | |
|     return audio_pcm_sw_write (sw, buf, len);
 | |
| }
 | |
| 
 | |
| static int winwave_run_out (HWVoiceOut *hw, int live)
 | |
| {
 | |
|     WaveVoiceOut *wave = (WaveVoiceOut *) hw;
 | |
|     int decr;
 | |
|     int doreset;
 | |
| 
 | |
|     EnterCriticalSection (&wave->crit_sect);
 | |
|     {
 | |
|         decr = audio_MIN (live, wave->avail);
 | |
|         decr = audio_pcm_hw_clip_out (hw, wave->pcm_buf, decr, wave->pending);
 | |
|         wave->pending += decr;
 | |
|         wave->avail -= decr;
 | |
|     }
 | |
|     LeaveCriticalSection (&wave->crit_sect);
 | |
| 
 | |
|     doreset = hw->poll_mode && (wave->pending >= conf.dac_samples);
 | |
|     if (doreset && !ResetEvent (wave->event)) {
 | |
|         dolog ("DAC ResetEvent failed %lx\n", GetLastError ());
 | |
|     }
 | |
| 
 | |
|     while (wave->pending >= conf.dac_samples) {
 | |
|         MMRESULT mr;
 | |
|         WAVEHDR *h = &wave->hdrs[wave->curhdr];
 | |
| 
 | |
|         h->dwUser = 0;
 | |
|         mr = waveOutWrite (wave->hwo, h, sizeof (*h));
 | |
|         if (mr != MMSYSERR_NOERROR) {
 | |
|             winwave_logerr (mr, "waveOutWrite(%d)", wave->curhdr);
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         wave->pending -= conf.dac_samples;
 | |
|         wave->curhdr = (wave->curhdr + 1) % conf.dac_headers;
 | |
|     }
 | |
| 
 | |
|     return decr;
 | |
| }
 | |
| 
 | |
| static void winwave_poll (void *opaque)
 | |
| {
 | |
|     (void) opaque;
 | |
|     audio_run ("winwave_poll");
 | |
| }
 | |
| 
 | |
| static void winwave_fini_out (HWVoiceOut *hw)
 | |
| {
 | |
|     int i;
 | |
|     MMRESULT mr;
 | |
|     WaveVoiceOut *wave = (WaveVoiceOut *) hw;
 | |
| 
 | |
|     mr = waveOutReset (wave->hwo);
 | |
|     if (mr != MMSYSERR_NOERROR) {
 | |
|         winwave_logerr (mr, "waveOutReset");
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < conf.dac_headers; ++i) {
 | |
|         mr = waveOutUnprepareHeader (wave->hwo, &wave->hdrs[i],
 | |
|                                      sizeof (wave->hdrs[i]));
 | |
|         if (mr != MMSYSERR_NOERROR) {
 | |
|             winwave_logerr (mr, "waveOutUnprepareHeader(%d)", i);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     winwave_anal_close_out (wave);
 | |
| 
 | |
|     if (wave->event) {
 | |
|         qemu_del_wait_object (wave->event, winwave_poll, wave);
 | |
|         if (!CloseHandle (wave->event)) {
 | |
|             dolog ("DAC CloseHandle failed %lx\n", GetLastError ());
 | |
|         }
 | |
|         wave->event = NULL;
 | |
|     }
 | |
| 
 | |
|     g_free (wave->pcm_buf);
 | |
|     wave->pcm_buf = NULL;
 | |
| 
 | |
|     g_free (wave->hdrs);
 | |
|     wave->hdrs = NULL;
 | |
| }
 | |
| 
 | |
| static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | |
| {
 | |
|     MMRESULT mr;
 | |
|     WaveVoiceOut *wave = (WaveVoiceOut *) hw;
 | |
| 
 | |
|     switch (cmd) {
 | |
|     case VOICE_ENABLE:
 | |
|         {
 | |
|             va_list ap;
 | |
|             int poll_mode;
 | |
| 
 | |
|             va_start (ap, cmd);
 | |
|             poll_mode = va_arg (ap, int);
 | |
|             va_end (ap);
 | |
| 
 | |
|             if (poll_mode && !wave->event) {
 | |
|                 wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
 | |
|                 if (!wave->event) {
 | |
|                     dolog ("DAC CreateEvent: %lx, poll mode will be disabled\n",
 | |
|                            GetLastError ());
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (wave->event) {
 | |
|                 int ret;
 | |
| 
 | |
|                 ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
 | |
|                 hw->poll_mode = (ret == 0);
 | |
|             }
 | |
|             else {
 | |
|                 hw->poll_mode = 0;
 | |
|             }
 | |
|             if (wave->paused) {
 | |
|                 mr = waveOutRestart (wave->hwo);
 | |
|                 if (mr != MMSYSERR_NOERROR) {
 | |
|                     winwave_logerr (mr, "waveOutRestart");
 | |
|                 }
 | |
|                 wave->paused = 0;
 | |
|             }
 | |
|         }
 | |
|         return 0;
 | |
| 
 | |
|     case VOICE_DISABLE:
 | |
|         if (!wave->paused) {
 | |
|             mr = waveOutPause (wave->hwo);
 | |
|             if (mr != MMSYSERR_NOERROR) {
 | |
|                 winwave_logerr (mr, "waveOutPause");
 | |
|             }
 | |
|             else {
 | |
|                 wave->paused = 1;
 | |
|             }
 | |
|         }
 | |
|         if (wave->event) {
 | |
|             qemu_del_wait_object (wave->event, winwave_poll, wave);
 | |
|         }
 | |
|         return 0;
 | |
|     }
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| static void winwave_anal_close_in (WaveVoiceIn *wave)
 | |
| {
 | |
|     MMRESULT mr;
 | |
| 
 | |
|     mr = waveInClose (wave->hwi);
 | |
|     if (mr != MMSYSERR_NOERROR) {
 | |
|         winwave_logerr (mr, "waveInClose");
 | |
|     }
 | |
|     wave->hwi = NULL;
 | |
| }
 | |
| 
 | |
| static void CALLBACK winwave_callback_in (
 | |
|     HWAVEIN *hwi,
 | |
|     UINT msg,
 | |
|     DWORD_PTR dwInstance,
 | |
|     DWORD_PTR dwParam1,
 | |
|     DWORD_PTR dwParam2
 | |
|     )
 | |
| {
 | |
|     WaveVoiceIn *wave = (WaveVoiceIn *) dwInstance;
 | |
| 
 | |
|     switch (msg) {
 | |
|     case WIM_DATA:
 | |
|         {
 | |
|             WAVEHDR *h = (WAVEHDR *) dwParam1;
 | |
|             if (!h->dwUser) {
 | |
|                 h->dwUser = 1;
 | |
|                 EnterCriticalSection (&wave->crit_sect);
 | |
|                 {
 | |
|                     wave->avail += conf.adc_samples;
 | |
|                 }
 | |
|                 LeaveCriticalSection (&wave->crit_sect);
 | |
|                 if (wave->hw.poll_mode) {
 | |
|                     if (!SetEvent (wave->event)) {
 | |
|                         dolog ("ADC SetEvent failed %lx\n", GetLastError ());
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case WIM_CLOSE:
 | |
|     case WIM_OPEN:
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         dolog ("unknown wave in callback msg %x\n", msg);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void winwave_add_buffers (WaveVoiceIn *wave, int samples)
 | |
| {
 | |
|     int doreset;
 | |
| 
 | |
|     doreset = wave->hw.poll_mode && (samples >= conf.adc_samples);
 | |
|     if (doreset && !ResetEvent (wave->event)) {
 | |
|         dolog ("ADC ResetEvent failed %lx\n", GetLastError ());
 | |
|     }
 | |
| 
 | |
|     while (samples >= conf.adc_samples) {
 | |
|         MMRESULT mr;
 | |
|         WAVEHDR *h = &wave->hdrs[wave->curhdr];
 | |
| 
 | |
|         h->dwUser = 0;
 | |
|         mr = waveInAddBuffer (wave->hwi, h, sizeof (*h));
 | |
|         if (mr != MMSYSERR_NOERROR) {
 | |
|             winwave_logerr (mr, "waveInAddBuffer(%d)", wave->curhdr);
 | |
|         }
 | |
|         wave->curhdr = (wave->curhdr + 1) % conf.adc_headers;
 | |
|         samples -= conf.adc_samples;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as)
 | |
| {
 | |
|     int i;
 | |
|     int err;
 | |
|     MMRESULT mr;
 | |
|     WAVEFORMATEX wfx;
 | |
|     WaveVoiceIn *wave;
 | |
| 
 | |
|     wave = (WaveVoiceIn *) hw;
 | |
| 
 | |
|     InitializeCriticalSection (&wave->crit_sect);
 | |
| 
 | |
|     err = waveformat_from_audio_settings (&wfx, as);
 | |
|     if (err) {
 | |
|         goto err0;
 | |
|     }
 | |
| 
 | |
|     mr = waveInOpen (&wave->hwi, WAVE_MAPPER, &wfx,
 | |
|                      (DWORD_PTR) winwave_callback_in,
 | |
|                      (DWORD_PTR) wave, CALLBACK_FUNCTION);
 | |
|     if (mr != MMSYSERR_NOERROR) {
 | |
|         winwave_logerr (mr, "waveInOpen");
 | |
|         goto err1;
 | |
|     }
 | |
| 
 | |
|     wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
 | |
|                                sizeof (*wave->hdrs));
 | |
|     if (!wave->hdrs) {
 | |
|         goto err2;
 | |
|     }
 | |
| 
 | |
|     audio_pcm_init_info (&hw->info, as);
 | |
|     hw->samples = conf.adc_samples * conf.adc_headers;
 | |
|     wave->avail = 0;
 | |
| 
 | |
|     wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.adc_samples,
 | |
|                                   conf.adc_headers << hw->info.shift);
 | |
|     if (!wave->pcm_buf) {
 | |
|         goto err3;
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < conf.adc_headers; ++i) {
 | |
|         WAVEHDR *h = &wave->hdrs[i];
 | |
| 
 | |
|         h->dwUser = 0;
 | |
|         h->dwBufferLength = conf.adc_samples << hw->info.shift;
 | |
|         h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
 | |
|         h->dwFlags = 0;
 | |
| 
 | |
|         mr = waveInPrepareHeader (wave->hwi, h, sizeof (*h));
 | |
|         if (mr != MMSYSERR_NOERROR) {
 | |
|             winwave_logerr (mr, "waveInPrepareHeader(%d)", i);
 | |
|             goto err4;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     wave->paused = 1;
 | |
|     winwave_add_buffers (wave, hw->samples);
 | |
|     return 0;
 | |
| 
 | |
|  err4:
 | |
|     g_free (wave->pcm_buf);
 | |
|  err3:
 | |
|     g_free (wave->hdrs);
 | |
|  err2:
 | |
|     winwave_anal_close_in (wave);
 | |
|  err1:
 | |
|  err0:
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| static void winwave_fini_in (HWVoiceIn *hw)
 | |
| {
 | |
|     int i;
 | |
|     MMRESULT mr;
 | |
|     WaveVoiceIn *wave = (WaveVoiceIn *) hw;
 | |
| 
 | |
|     mr = waveInReset (wave->hwi);
 | |
|     if (mr != MMSYSERR_NOERROR) {
 | |
|         winwave_logerr (mr, "waveInReset");
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < conf.adc_headers; ++i) {
 | |
|         mr = waveInUnprepareHeader (wave->hwi, &wave->hdrs[i],
 | |
|                                      sizeof (wave->hdrs[i]));
 | |
|         if (mr != MMSYSERR_NOERROR) {
 | |
|             winwave_logerr (mr, "waveInUnprepareHeader(%d)", i);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     winwave_anal_close_in (wave);
 | |
| 
 | |
|     if (wave->event) {
 | |
|         qemu_del_wait_object (wave->event, winwave_poll, wave);
 | |
|         if (!CloseHandle (wave->event)) {
 | |
|             dolog ("ADC CloseHandle failed %lx\n", GetLastError ());
 | |
|         }
 | |
|         wave->event = NULL;
 | |
|     }
 | |
| 
 | |
|     g_free (wave->pcm_buf);
 | |
|     wave->pcm_buf = NULL;
 | |
| 
 | |
|     g_free (wave->hdrs);
 | |
|     wave->hdrs = NULL;
 | |
| }
 | |
| 
 | |
| static int winwave_run_in (HWVoiceIn *hw)
 | |
| {
 | |
|     WaveVoiceIn *wave = (WaveVoiceIn *) hw;
 | |
|     int live = audio_pcm_hw_get_live_in (hw);
 | |
|     int dead = hw->samples - live;
 | |
|     int decr, ret;
 | |
| 
 | |
|     if (!dead) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     EnterCriticalSection (&wave->crit_sect);
 | |
|     {
 | |
|         decr = audio_MIN (dead, wave->avail);
 | |
|         wave->avail -= decr;
 | |
|     }
 | |
|     LeaveCriticalSection (&wave->crit_sect);
 | |
| 
 | |
|     ret = decr;
 | |
|     while (decr) {
 | |
|         int left = hw->samples - hw->wpos;
 | |
|         int conv = audio_MIN (left, decr);
 | |
|         hw->conv (hw->conv_buf + hw->wpos,
 | |
|                   advance (wave->pcm_buf, wave->rpos << hw->info.shift),
 | |
|                   conv);
 | |
| 
 | |
|         wave->rpos = (wave->rpos + conv) % hw->samples;
 | |
|         hw->wpos = (hw->wpos + conv) % hw->samples;
 | |
|         decr -= conv;
 | |
|     }
 | |
| 
 | |
|     winwave_add_buffers (wave, ret);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static int winwave_read (SWVoiceIn *sw, void *buf, int size)
 | |
| {
 | |
|     return audio_pcm_sw_read (sw, buf, size);
 | |
| }
 | |
| 
 | |
| static int winwave_ctl_in (HWVoiceIn *hw, int cmd, ...)
 | |
| {
 | |
|     MMRESULT mr;
 | |
|     WaveVoiceIn *wave = (WaveVoiceIn *) hw;
 | |
| 
 | |
|     switch (cmd) {
 | |
|     case VOICE_ENABLE:
 | |
|         {
 | |
|             va_list ap;
 | |
|             int poll_mode;
 | |
| 
 | |
|             va_start (ap, cmd);
 | |
|             poll_mode = va_arg (ap, int);
 | |
|             va_end (ap);
 | |
| 
 | |
|             if (poll_mode && !wave->event) {
 | |
|                 wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
 | |
|                 if (!wave->event) {
 | |
|                     dolog ("ADC CreateEvent: %lx, poll mode will be disabled\n",
 | |
|                            GetLastError ());
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (wave->event) {
 | |
|                 int ret;
 | |
| 
 | |
|                 ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
 | |
|                 hw->poll_mode = (ret == 0);
 | |
|             }
 | |
|             else {
 | |
|                 hw->poll_mode = 0;
 | |
|             }
 | |
|             if (wave->paused) {
 | |
|                 mr = waveInStart (wave->hwi);
 | |
|                 if (mr != MMSYSERR_NOERROR) {
 | |
|                     winwave_logerr (mr, "waveInStart");
 | |
|                 }
 | |
|                 wave->paused = 0;
 | |
|             }
 | |
|         }
 | |
|         return 0;
 | |
| 
 | |
|     case VOICE_DISABLE:
 | |
|         if (!wave->paused) {
 | |
|             mr = waveInStop (wave->hwi);
 | |
|             if (mr != MMSYSERR_NOERROR) {
 | |
|                 winwave_logerr (mr, "waveInStop");
 | |
|             }
 | |
|             else {
 | |
|                 wave->paused = 1;
 | |
|             }
 | |
|         }
 | |
|         if (wave->event) {
 | |
|             qemu_del_wait_object (wave->event, winwave_poll, wave);
 | |
|         }
 | |
|         return 0;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void *winwave_audio_init (void)
 | |
| {
 | |
|     return &conf;
 | |
| }
 | |
| 
 | |
| static void winwave_audio_fini (void *opaque)
 | |
| {
 | |
|     (void) opaque;
 | |
| }
 | |
| 
 | |
| static struct audio_option winwave_options[] = {
 | |
|     {
 | |
|         .name        = "DAC_HEADERS",
 | |
|         .tag         = AUD_OPT_INT,
 | |
|         .valp        = &conf.dac_headers,
 | |
|         .descr       = "DAC number of headers",
 | |
|     },
 | |
|     {
 | |
|         .name        = "DAC_SAMPLES",
 | |
|         .tag         = AUD_OPT_INT,
 | |
|         .valp        = &conf.dac_samples,
 | |
|         .descr       = "DAC number of samples per header",
 | |
|     },
 | |
|     {
 | |
|         .name        = "ADC_HEADERS",
 | |
|         .tag         = AUD_OPT_INT,
 | |
|         .valp        = &conf.adc_headers,
 | |
|         .descr       = "ADC number of headers",
 | |
|     },
 | |
|     {
 | |
|         .name        = "ADC_SAMPLES",
 | |
|         .tag         = AUD_OPT_INT,
 | |
|         .valp        = &conf.adc_samples,
 | |
|         .descr       = "ADC number of samples per header",
 | |
|     },
 | |
|     { /* End of list */ }
 | |
| };
 | |
| 
 | |
| static struct audio_pcm_ops winwave_pcm_ops = {
 | |
|     .init_out = winwave_init_out,
 | |
|     .fini_out = winwave_fini_out,
 | |
|     .run_out  = winwave_run_out,
 | |
|     .write    = winwave_write,
 | |
|     .ctl_out  = winwave_ctl_out,
 | |
|     .init_in  = winwave_init_in,
 | |
|     .fini_in  = winwave_fini_in,
 | |
|     .run_in   = winwave_run_in,
 | |
|     .read     = winwave_read,
 | |
|     .ctl_in   = winwave_ctl_in
 | |
| };
 | |
| 
 | |
| struct audio_driver winwave_audio_driver = {
 | |
|     .name           = "winwave",
 | |
|     .descr          = "Windows Waveform Audio http://msdn.microsoft.com",
 | |
|     .options        = winwave_options,
 | |
|     .init           = winwave_audio_init,
 | |
|     .fini           = winwave_audio_fini,
 | |
|     .pcm_ops        = &winwave_pcm_ops,
 | |
|     .can_be_default = 1,
 | |
|     .max_voices_out = INT_MAX,
 | |
|     .max_voices_in  = INT_MAX,
 | |
|     .voice_size_out = sizeof (WaveVoiceOut),
 | |
|     .voice_size_in  = sizeof (WaveVoiceIn)
 | |
| };
 |