123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734 |
- /*
- * QEMU DirectSound audio driver
- *
- * Copyright (c) 2005 Vassili Karpov (malc)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
- /*
- * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
- */
- #include "qemu/osdep.h"
- #include "audio.h"
- #define AUDIO_CAP "dsound"
- #include "audio_int.h"
- #include "qemu/host-utils.h"
- #include "qemu/module.h"
- #include <windows.h>
- #include <mmsystem.h>
- #include <objbase.h>
- #include <dsound.h>
- #include "audio_win_int.h"
- /* #define DEBUG_DSOUND */
- typedef struct {
- LPDIRECTSOUND dsound;
- LPDIRECTSOUNDCAPTURE dsound_capture;
- struct audsettings settings;
- Audiodev *dev;
- } dsound;
- typedef struct {
- HWVoiceOut hw;
- LPDIRECTSOUNDBUFFER dsound_buffer;
- bool first_time;
- dsound *s;
- } DSoundVoiceOut;
- typedef struct {
- HWVoiceIn hw;
- LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
- bool first_time;
- dsound *s;
- } DSoundVoiceIn;
- static void dsound_log_hresult (HRESULT hr)
- {
- const char *str = "BUG";
- switch (hr) {
- case DS_OK:
- str = "The method succeeded";
- break;
- #ifdef DS_NO_VIRTUALIZATION
- case DS_NO_VIRTUALIZATION:
- str = "The buffer was created, but another 3D algorithm was substituted";
- break;
- #endif
- #ifdef DS_INCOMPLETE
- case DS_INCOMPLETE:
- str = "The method succeeded, but not all the optional effects were obtained";
- break;
- #endif
- #ifdef DSERR_ACCESSDENIED
- case DSERR_ACCESSDENIED:
- str = "The request failed because access was denied";
- break;
- #endif
- #ifdef DSERR_ALLOCATED
- case DSERR_ALLOCATED:
- str = "The request failed because resources, "
- "such as a priority level, were already in use "
- "by another caller";
- break;
- #endif
- #ifdef DSERR_ALREADYINITIALIZED
- case DSERR_ALREADYINITIALIZED:
- str = "The object is already initialized";
- break;
- #endif
- #ifdef DSERR_BADFORMAT
- case DSERR_BADFORMAT:
- str = "The specified wave format is not supported";
- break;
- #endif
- #ifdef DSERR_BADSENDBUFFERGUID
- case DSERR_BADSENDBUFFERGUID:
- str = "The GUID specified in an audiopath file "
- "does not match a valid mix-in buffer";
- break;
- #endif
- #ifdef DSERR_BUFFERLOST
- case DSERR_BUFFERLOST:
- str = "The buffer memory has been lost and must be restored";
- break;
- #endif
- #ifdef DSERR_BUFFERTOOSMALL
- case DSERR_BUFFERTOOSMALL:
- str = "The buffer size is not great enough to "
- "enable effects processing";
- break;
- #endif
- #ifdef DSERR_CONTROLUNAVAIL
- case DSERR_CONTROLUNAVAIL:
- str = "The buffer control (volume, pan, and so on) "
- "requested by the caller is not available. "
- "Controls must be specified when the buffer is created, "
- "using the dwFlags member of DSBUFFERDESC";
- break;
- #endif
- #ifdef DSERR_DS8_REQUIRED
- case DSERR_DS8_REQUIRED:
- str = "A DirectSound object of class CLSID_DirectSound8 or later "
- "is required for the requested functionality. "
- "For more information, see IDirectSound8 Interface";
- break;
- #endif
- #ifdef DSERR_FXUNAVAILABLE
- case DSERR_FXUNAVAILABLE:
- str = "The effects requested could not be found on the system, "
- "or they are in the wrong order or in the wrong location; "
- "for example, an effect expected in hardware "
- "was found in software";
- break;
- #endif
- #ifdef DSERR_GENERIC
- case DSERR_GENERIC:
- str = "An undetermined error occurred inside the DirectSound subsystem";
- break;
- #endif
- #ifdef DSERR_INVALIDCALL
- case DSERR_INVALIDCALL:
- str = "This function is not valid for the current state of this object";
- break;
- #endif
- #ifdef DSERR_INVALIDPARAM
- case DSERR_INVALIDPARAM:
- str = "An invalid parameter was passed to the returning function";
- break;
- #endif
- #ifdef DSERR_NOAGGREGATION
- case DSERR_NOAGGREGATION:
- str = "The object does not support aggregation";
- break;
- #endif
- #ifdef DSERR_NODRIVER
- case DSERR_NODRIVER:
- str = "No sound driver is available for use, "
- "or the given GUID is not a valid DirectSound device ID";
- break;
- #endif
- #ifdef DSERR_NOINTERFACE
- case DSERR_NOINTERFACE:
- str = "The requested COM interface is not available";
- break;
- #endif
- #ifdef DSERR_OBJECTNOTFOUND
- case DSERR_OBJECTNOTFOUND:
- str = "The requested object was not found";
- break;
- #endif
- #ifdef DSERR_OTHERAPPHASPRIO
- case DSERR_OTHERAPPHASPRIO:
- str = "Another application has a higher priority level, "
- "preventing this call from succeeding";
- break;
- #endif
- #ifdef DSERR_OUTOFMEMORY
- case DSERR_OUTOFMEMORY:
- str = "The DirectSound subsystem could not allocate "
- "sufficient memory to complete the caller's request";
- break;
- #endif
- #ifdef DSERR_PRIOLEVELNEEDED
- case DSERR_PRIOLEVELNEEDED:
- str = "A cooperative level of DSSCL_PRIORITY or higher is required";
- break;
- #endif
- #ifdef DSERR_SENDLOOP
- case DSERR_SENDLOOP:
- str = "A circular loop of send effects was detected";
- break;
- #endif
- #ifdef DSERR_UNINITIALIZED
- case DSERR_UNINITIALIZED:
- str = "The Initialize method has not been called "
- "or has not been called successfully "
- "before other methods were called";
- break;
- #endif
- #ifdef DSERR_UNSUPPORTED
- case DSERR_UNSUPPORTED:
- str = "The function called is not supported at this time";
- break;
- #endif
- default:
- AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT 0x%lx)\n", hr);
- return;
- }
- AUD_log (AUDIO_CAP, "Reason: %s\n", str);
- }
- static void G_GNUC_PRINTF (2, 3) dsound_logerr (
- HRESULT hr,
- const char *fmt,
- ...
- )
- {
- va_list ap;
- va_start (ap, fmt);
- AUD_vlog (AUDIO_CAP, fmt, ap);
- va_end (ap);
- dsound_log_hresult (hr);
- }
- static void G_GNUC_PRINTF (3, 4) dsound_logerr2 (
- HRESULT hr,
- const char *typ,
- const char *fmt,
- ...
- )
- {
- va_list ap;
- AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
- va_start (ap, fmt);
- AUD_vlog (AUDIO_CAP, fmt, ap);
- va_end (ap);
- dsound_log_hresult (hr);
- }
- #ifdef DEBUG_DSOUND
- static void print_wave_format (WAVEFORMATEX *wfx)
- {
- dolog ("tag = %d\n", wfx->wFormatTag);
- dolog ("nChannels = %d\n", wfx->nChannels);
- dolog ("nSamplesPerSec = %ld\n", wfx->nSamplesPerSec);
- dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec);
- dolog ("nBlockAlign = %d\n", wfx->nBlockAlign);
- dolog ("wBitsPerSample = %d\n", wfx->wBitsPerSample);
- dolog ("cbSize = %d\n", wfx->cbSize);
- }
- #endif
- static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s)
- {
- HRESULT hr;
- hr = IDirectSoundBuffer_Restore (dsb);
- if (hr != DS_OK) {
- dsound_logerr (hr, "Could not restore playback buffer\n");
- return -1;
- }
- return 0;
- }
- #include "dsound_template.h"
- #define DSBTYPE_IN
- #include "dsound_template.h"
- #undef DSBTYPE_IN
- static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp,
- dsound *s)
- {
- HRESULT hr;
- hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not get playback buffer status\n");
- return -1;
- }
- if (*statusp & DSBSTATUS_BUFFERLOST) {
- dsound_restore_out(dsb, s);
- return -1;
- }
- return 0;
- }
- static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
- DWORD *statusp)
- {
- HRESULT hr;
- hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not get capture buffer status\n");
- return -1;
- }
- return 0;
- }
- static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
- dsound *s)
- {
- int err;
- LPVOID p1, p2;
- DWORD blen1, blen2, len1, len2;
- err = dsound_lock_out (
- dsb,
- &hw->info,
- 0,
- hw->size_emul,
- &p1, &p2,
- &blen1, &blen2,
- 1,
- s
- );
- if (err) {
- return;
- }
- len1 = blen1 / hw->info.bytes_per_frame;
- len2 = blen2 / hw->info.bytes_per_frame;
- #ifdef DEBUG_DSOUND
- dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
- p1, blen1, len1,
- p2, blen2, len2);
- #endif
- if (p1 && len1) {
- audio_pcm_info_clear_buf (&hw->info, p1, len1);
- }
- if (p2 && len2) {
- audio_pcm_info_clear_buf (&hw->info, p2, len2);
- }
- dsound_unlock_out (dsb, p1, p2, blen1, blen2);
- }
- static int dsound_set_cooperative_level(dsound *s)
- {
- HRESULT hr;
- HWND hwnd;
- hwnd = GetDesktopWindow();
- hr = IDirectSound_SetCooperativeLevel (
- s->dsound,
- hwnd,
- DSSCL_PRIORITY
- );
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not set cooperative level for window %p\n",
- hwnd);
- return -1;
- }
- return 0;
- }
- static void dsound_enable_out(HWVoiceOut *hw, bool enable)
- {
- HRESULT hr;
- DWORD status;
- DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
- LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
- dsound *s = ds->s;
- if (!dsb) {
- dolog ("Attempt to control voice without a buffer\n");
- return;
- }
- if (enable) {
- if (dsound_get_status_out (dsb, &status, s)) {
- return;
- }
- if (status & DSBSTATUS_PLAYING) {
- dolog ("warning: Voice is already playing\n");
- return;
- }
- dsound_clear_sample (hw, dsb, s);
- hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not start playing buffer\n");
- return;
- }
- } else {
- if (dsound_get_status_out (dsb, &status, s)) {
- return;
- }
- if (status & DSBSTATUS_PLAYING) {
- hr = IDirectSoundBuffer_Stop (dsb);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not stop playing buffer\n");
- return;
- }
- } else {
- dolog ("warning: Voice is not playing\n");
- }
- }
- }
- static size_t dsound_buffer_get_free(HWVoiceOut *hw)
- {
- DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
- LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
- HRESULT hr;
- DWORD ppos, wpos;
- hr = IDirectSoundBuffer_GetCurrentPosition(
- dsb, &ppos, ds->first_time ? &wpos : NULL);
- if (FAILED(hr)) {
- dsound_logerr(hr, "Could not get playback buffer position\n");
- return 0;
- }
- if (ds->first_time) {
- hw->pos_emul = wpos;
- ds->first_time = false;
- }
- return audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
- }
- static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
- {
- DSoundVoiceOut *ds = (DSoundVoiceOut *)hw;
- LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
- DWORD act_size;
- size_t req_size;
- int err;
- void *ret;
- req_size = MIN(*size, hw->size_emul - hw->pos_emul);
- assert(req_size > 0);
- err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
- &act_size, NULL, false, ds->s);
- if (err) {
- dolog("Failed to lock buffer\n");
- *size = 0;
- return NULL;
- }
- *size = act_size;
- return ret;
- }
- static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len)
- {
- DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
- LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
- int err = dsound_unlock_out(dsb, buf, NULL, len, 0);
- if (err) {
- dolog("Failed to unlock buffer!!\n");
- return 0;
- }
- hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
- return len;
- }
- static void dsound_enable_in(HWVoiceIn *hw, bool enable)
- {
- HRESULT hr;
- DWORD status;
- DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
- LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
- if (!dscb) {
- dolog ("Attempt to control capture voice without a buffer\n");
- return;
- }
- if (enable) {
- if (dsound_get_status_in (dscb, &status)) {
- return;
- }
- if (status & DSCBSTATUS_CAPTURING) {
- dolog ("warning: Voice is already capturing\n");
- return;
- }
- /* clear ?? */
- hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not start capturing\n");
- return;
- }
- } else {
- if (dsound_get_status_in (dscb, &status)) {
- return;
- }
- if (status & DSCBSTATUS_CAPTURING) {
- hr = IDirectSoundCaptureBuffer_Stop (dscb);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not stop capturing\n");
- return;
- }
- } else {
- dolog ("warning: Voice is not capturing\n");
- }
- }
- }
- static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
- {
- DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
- LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
- HRESULT hr;
- DWORD rpos, act_size;
- size_t req_size;
- int err;
- void *ret;
- hr = IDirectSoundCaptureBuffer_GetCurrentPosition(dscb, NULL, &rpos);
- if (FAILED(hr)) {
- dsound_logerr(hr, "Could not get capture buffer position\n");
- *size = 0;
- return NULL;
- }
- if (ds->first_time) {
- hw->pos_emul = rpos;
- ds->first_time = false;
- }
- req_size = audio_ring_dist(rpos, hw->pos_emul, hw->size_emul);
- req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul));
- if (req_size == 0) {
- *size = 0;
- return NULL;
- }
- err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
- &act_size, NULL, false, ds->s);
- if (err) {
- dolog("Failed to lock buffer\n");
- *size = 0;
- return NULL;
- }
- *size = act_size;
- return ret;
- }
- static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len)
- {
- DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
- LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
- int err = dsound_unlock_in(dscb, buf, NULL, len, 0);
- if (err) {
- dolog("Failed to unlock buffer!!\n");
- return;
- }
- hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
- }
- static void dsound_audio_fini (void *opaque)
- {
- HRESULT hr;
- dsound *s = opaque;
- if (!s->dsound) {
- g_free(s);
- return;
- }
- hr = IDirectSound_Release (s->dsound);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not release DirectSound\n");
- }
- s->dsound = NULL;
- if (!s->dsound_capture) {
- g_free(s);
- return;
- }
- hr = IDirectSoundCapture_Release (s->dsound_capture);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not release DirectSoundCapture\n");
- }
- s->dsound_capture = NULL;
- g_free(s);
- }
- static void *dsound_audio_init(Audiodev *dev, Error **errp)
- {
- int err;
- HRESULT hr;
- dsound *s = g_new0(dsound, 1);
- AudiodevDsoundOptions *dso;
- assert(dev->driver == AUDIODEV_DRIVER_DSOUND);
- s->dev = dev;
- dso = &dev->u.dsound;
- if (!dso->has_latency) {
- dso->has_latency = true;
- dso->latency = 10000; /* 10 ms */
- }
- hr = CoInitialize (NULL);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not initialize COM\n");
- g_free(s);
- return NULL;
- }
- hr = CoCreateInstance (
- &CLSID_DirectSound,
- NULL,
- CLSCTX_ALL,
- &IID_IDirectSound,
- (void **) &s->dsound
- );
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not create DirectSound instance\n");
- g_free(s);
- return NULL;
- }
- hr = IDirectSound_Initialize (s->dsound, NULL);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not initialize DirectSound\n");
- hr = IDirectSound_Release (s->dsound);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not release DirectSound\n");
- }
- g_free(s);
- return NULL;
- }
- hr = CoCreateInstance (
- &CLSID_DirectSoundCapture,
- NULL,
- CLSCTX_ALL,
- &IID_IDirectSoundCapture,
- (void **) &s->dsound_capture
- );
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
- } else {
- hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
- hr = IDirectSoundCapture_Release (s->dsound_capture);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not release DirectSoundCapture\n");
- }
- s->dsound_capture = NULL;
- }
- }
- err = dsound_set_cooperative_level(s);
- if (err) {
- dsound_audio_fini (s);
- return NULL;
- }
- return s;
- }
- static struct audio_pcm_ops dsound_pcm_ops = {
- .init_out = dsound_init_out,
- .fini_out = dsound_fini_out,
- .write = audio_generic_write,
- .buffer_get_free = dsound_buffer_get_free,
- .get_buffer_out = dsound_get_buffer_out,
- .put_buffer_out = dsound_put_buffer_out,
- .enable_out = dsound_enable_out,
- .init_in = dsound_init_in,
- .fini_in = dsound_fini_in,
- .read = audio_generic_read,
- .get_buffer_in = dsound_get_buffer_in,
- .put_buffer_in = dsound_put_buffer_in,
- .enable_in = dsound_enable_in,
- };
- static struct audio_driver dsound_audio_driver = {
- .name = "dsound",
- .descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
- .init = dsound_audio_init,
- .fini = dsound_audio_fini,
- .pcm_ops = &dsound_pcm_ops,
- .max_voices_out = INT_MAX,
- .max_voices_in = 1,
- .voice_size_out = sizeof (DSoundVoiceOut),
- .voice_size_in = sizeof (DSoundVoiceIn)
- };
- static void register_audio_dsound(void)
- {
- audio_driver_register(&dsound_audio_driver);
- }
- type_init(register_audio_dsound);
|