#include #ifdef QUISK_HAVE_DIRECTX #include #include #include #include "quisk.h" #include "dsound.h" //#include #include //#include //#include #include // This module provides sound card access using Direct Sound HRESULT errFound, errOpen; extern HWND quisk_mainwin_handle; static GUID IEEE = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; static GUID PCMM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; static PyObject * MakePyUnicode(LPCTSTR txt) { // return a Python Unicode object PyObject * py_unicode; int bytes_txt; #ifdef UNICODE size_t wstr_size = wcslen(txt); py_unicode = PyUnicode_DecodeUTF16((const char *)txt, wstr_size * sizeof(WCHAR), "replace", NULL); #else bytes_txt = strlen(txt); int wstr_size = MultiByteToWideChar(CP_ACP, 0, txt, bytes_txt, NULL, 0); LPWSTR wstr = (LPWSTR)malloc(sizeof(WCHAR) * (wstr_size + 1)); MultiByteToWideChar(CP_ACP, 0, txt, bytes_txt, wstr, wstr_size + 1); py_unicode = PyUnicode_DecodeUTF16((const char *)wstr, wstr_size * sizeof(WCHAR), "replace", NULL); free(wstr); #endif return py_unicode; } static int match_name(LPCTSTR lpszDesc, const char * name) { int found; PyObject * py_unicode; PyObject * py_substring; Py_ssize_t length, index; py_unicode = MakePyUnicode(lpszDesc); if ( ! py_unicode) return 0; py_substring = PyUnicode_DecodeUTF8(name, strlen(name), "replace"); if ( ! py_substring) { Py_DECREF(py_unicode); return 0; } length = PyUnicode_GetLength(py_unicode); if (length <= 0) { Py_DECREF(py_unicode); Py_DECREF(py_substring); return 0; } index = PyUnicode_Find(py_unicode, py_substring, 0, length, 1); if (index >= 0) found = 1; else if (index == -1) found = 0; else { // error PyErr_Clear(); found = 0; } Py_DECREF(py_unicode); Py_DECREF(py_substring); return found; } static BOOL CALLBACK DSEnumNames(LPGUID lpGUID, LPCTSTR lpszDesc, LPCTSTR lpszDrvName, LPVOID pyseq) { //char * buf = (char *)malloc(2000); //strcpy (buf, "\xc9vir\xf6n"); //strcat(buf, (char *)lpszDesc); //PyObject * py_string = py_str_utf8((LPCTSTR)buf); PyObject * py_unicode = MakePyUnicode(lpszDesc); PyList_Append((PyObject *)pyseq, py_unicode); //free(buf); Py_DECREF(py_unicode); return TRUE; } static BOOL CALLBACK DsEnumPlay(LPGUID lpGUID, LPCTSTR lpszDesc, LPCTSTR lpszDrvName, LPVOID dev) { // Open the play device if the name is found in the description LPDIRECTSOUND8 DsDev; if (strcmp(lpszDrvName, ((struct sound_dev *)dev)->device_name) == 0 || match_name(lpszDesc, ((struct sound_dev *)dev)->name)) { errFound = DS_OK; errOpen = DirectSoundCreate8(lpGUID, &DsDev, NULL); if (errOpen == DS_OK) { ((struct sound_dev *)dev)->handle = DsDev; } return FALSE; // Stop iteration } else { return TRUE; } } static BOOL CALLBACK DsEnumCapture(LPGUID lpGUID, LPCTSTR lpszDesc, LPCTSTR lpszDrvName, LPVOID dev) { // Open the capture device if the name is found in the description LPDIRECTSOUNDCAPTURE8 DsDev; //char * buf = (char *)malloc(2000); //strcpy (buf, "\xc9vir\xf6n"); //strcat(buf, (char *)lpszDesc); //PyObject * py_string = py_str_utf8((LPCTSTR)buf); //QuiskPrintf("EnumCapture .%s. .%s.\n", lpszDesc, lpszDrvName); //QuiskPrintf(" Capture .%s. .%s.\n", ((struct sound_dev *)dev)->name, ((struct sound_dev *)dev)->device_name); if (strcmp(lpszDrvName, ((struct sound_dev *)dev)->device_name) == 0 || match_name(lpszDesc, ((struct sound_dev *)dev)->name)) { errFound = DS_OK; errOpen = DirectSoundCaptureCreate8(lpGUID, &DsDev, NULL); if (errOpen == DS_OK) ((struct sound_dev *)dev)->handle = DsDev; return FALSE; // Stop iteration } else { return TRUE; } } static void MakeWFext(int use_new, int use_float, struct sound_dev * dev, WAVEFORMATEXTENSIBLE * pwfex) { // fill in a WAVEFORMATEXTENSIBLE structure if (use_float) { dev->sample_bytes = 4; dev->sound_format = Float32; } if (use_new) { pwfex->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; pwfex->Format.cbSize = 22; pwfex->Samples.wValidBitsPerSample = dev->sample_bytes * 8; if (dev->num_channels == 1) pwfex->dwChannelMask = SPEAKER_FRONT_LEFT; else pwfex->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; if (use_float) { pwfex->SubFormat = IEEE; dev->use_float = 1; } else { pwfex->SubFormat = PCMM; dev->use_float = 0; } } else { pwfex->Format.cbSize = 0; if (use_float) { pwfex->Format.wFormatTag = 0x03; //WAVE_FORMAT_IEEE; dev->use_float = 1; } else { pwfex->Format.wFormatTag = WAVE_FORMAT_PCM; dev->use_float = 0; } } pwfex->Format.nChannels = dev->num_channels; pwfex->Format.nSamplesPerSec = dev->sample_rate; pwfex->Format.nAvgBytesPerSec = dev->num_channels * dev->sample_rate * dev->sample_bytes; dev->play_buf_bytes = pwfex->Format.nAvgBytesPerSec * quisk_sound_state.latency_millisecs / 1000 * 2; dev->play_buf_size = dev->play_buf_bytes / (dev->num_channels * dev->sample_bytes); pwfex->Format.nBlockAlign = dev->num_channels * dev->sample_bytes; pwfex->Format.wBitsPerSample = dev->sample_bytes * 8; } static int quisk_open_capture(struct sound_dev * dev) { // Open the soundcard for capture. Return non-zero for error. LPDIRECTSOUNDCAPTUREBUFFER ptBuf; DSCBUFFERDESC dscbd; HRESULT hr; WAVEFORMATEXTENSIBLE wfex; if (quisk_sound_state.verbose_sound) QuiskPrintf("Opening capture device %s\n Name %s\n Device name %s\n", dev->stream_description, dev->name, dev->device_name); dev->handle = NULL; dev->buffer = NULL; dev->started = 0; dev->dataPos = 0; dev->portaudio_index = -1; if ( ! dev->name[0]) // Check for null play name; not an error return 0; errFound = ~DS_OK; DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)DsEnumCapture, dev); if (errFound != DS_OK) { snprintf (dev->dev_errmsg, QUISK_SC_SIZE, "DirectSound capture device name %.200s not found", dev->name); if (quisk_sound_state.verbose_sound) QuiskPrintf("%s\n", dev->dev_errmsg); return 1; } if (errOpen != DS_OK) { snprintf (dev->dev_errmsg, QUISK_SC_SIZE, "DirectSound capture device %.200s open failed", dev->name); if (quisk_sound_state.verbose_sound) QuiskPrintf("%s\n", dev->dev_errmsg); return 1; } dev->sample_bytes = 4; dev->sound_format = Int32; MakeWFext (1, 0, dev, &wfex); // fill in wfex memset(&dscbd, 0, sizeof(DSCBUFFERDESC)); dscbd.dwSize = sizeof(DSCBUFFERDESC); dscbd.dwFlags = 0; dscbd.dwBufferBytes = dev->play_buf_bytes; dscbd.lpwfxFormat = (WAVEFORMATEX *)&wfex; hr = IDirectSoundCapture_CreateCaptureBuffer( (LPDIRECTSOUNDCAPTURE8)dev->handle, &dscbd, &ptBuf, NULL); if (hr == DS_OK) { dev->buffer = ptBuf; } else { snprintf (dev->dev_errmsg, QUISK_SC_SIZE, "DirectSound capture device %.200s buffer create failed (0x%lX)", dev->name, hr); return 1; } ptBuf = (LPDIRECTSOUNDCAPTUREBUFFER)dev->buffer; hr = IDirectSoundCaptureBuffer8_Start(ptBuf, DSCBSTART_LOOPING); if (hr != DS_OK) { if (quisk_sound_state.verbose_sound) QuiskPrintf("Capture start error 0x%lX", hr); snprintf (dev->dev_errmsg, QUISK_SC_SIZE, "DirectSound capture device %.200s capture start failed", dev->name); return 1; } if (quisk_sound_state.verbose_sound) { QuiskPrintf(" Sample rate %d\n Channel_I %d\n Channel_Q %d\n", dev->sample_rate, dev->channel_I, dev->channel_Q); QuiskPrintf(" Number of channels %d, bytes per sample %d\n", dev->num_channels, dev->sample_bytes); QuiskPrintf("Created capture buffer size %d bytes for device %s, descr %s\n", dev->play_buf_bytes, dev->name, dev->stream_description); } return 0; } static int quisk_open_playback(struct sound_dev * dev) { // Open the soundcard for playback. Return non-zero for error. LPDIRECTSOUNDBUFFER ptBuf; WAVEFORMATEXTENSIBLE wfex; DSBUFFERDESC dsbdesc; HRESULT hr; if (quisk_sound_state.verbose_sound) QuiskPrintf("Opening playback device %s\n Name %s\n Device name %s\n", dev->stream_description, dev->name, dev->device_name); dev->handle = NULL; dev->buffer = NULL; dev->started = 0; dev->dataPos = 0; dev->portaudio_index = -1; dev->sample_bytes = 2; dev->sound_format = Int16; if ( ! dev->name[0]) // Check for null play name; not an error return 0; errFound = ~DS_OK; DirectSoundEnumerate((LPDSENUMCALLBACK)DsEnumPlay, dev); if (errFound != DS_OK) { snprintf (dev->dev_errmsg, QUISK_SC_SIZE, "DirectSound play device name %.200s not found", dev->name); return 1; } if (errOpen != DS_OK) { snprintf (dev->dev_errmsg, QUISK_SC_SIZE, "DirectSound play device %.200s open failed", dev->name); return 1; } hr = IDirectSound_SetCooperativeLevel ((LPDIRECTSOUND8)dev->handle, quisk_mainwin_handle, DSSCL_PRIORITY); if (hr != DS_OK) { snprintf (dev->dev_errmsg, QUISK_SC_SIZE, "DirectSound play device %.200s cooperative level failed", dev->name); return 1; } dev->sample_bytes = 4; dev->sound_format = Int32; MakeWFext (1, 0, dev, &wfex); // fill in wfex memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS; dsbdesc.dwBufferBytes = dev->play_buf_bytes; dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&wfex; hr = IDirectSound_CreateSoundBuffer( (LPDIRECTSOUND8)dev->handle, &dsbdesc, &ptBuf, NULL); if (hr == DS_OK) { dev->buffer = ptBuf; } else { snprintf (dev->dev_errmsg, QUISK_SC_SIZE, "DirectSound play device %.180s buffer create failed (0x%X)", dev->name, (unsigned int)hr); return 1; } if (quisk_sound_state.verbose_sound) { QuiskPrintf(" Sample rate %d\n Channel_I %d\n Channel_Q %d\n", dev->sample_rate, dev->channel_I, dev->channel_Q); QuiskPrintf(" Number of channels %d, bytes per sample %d\n", dev->num_channels, dev->sample_bytes); QuiskPrintf("Created play buffer size %d bytes for device %s, descr %s\n", dev->play_buf_bytes, dev->name, dev->stream_description); } return 0; } PyObject * quisk_directx_sound_devices(PyObject * self, PyObject * args) { // Return a list of DirectSound device names PyObject * pylist, * pycapt, * pyplay; if (!PyArg_ParseTuple (args, "")) return NULL; // Each pycapt and pyplay is a device name pylist = PyList_New(0); // list [pycapt, pyplay] pycapt = PyList_New(0); // list of capture devices pyplay = PyList_New(0); // list of play devices PyList_Append(pylist, pycapt); PyList_Append(pylist, pyplay); DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)DSEnumNames, pycapt); DirectSoundEnumerate((LPDSENUMCALLBACK)DSEnumNames, pyplay); return pylist; } void quisk_start_sound_directx (struct sound_dev ** pCapture, struct sound_dev ** pPlayback) { struct sound_dev * pDev; // DirectX must open the playback device before the (same) capture device while (1) { pDev = *pPlayback++; if ( ! pDev) break; if ( ! pDev->dev_errmsg[0] && pDev->driver == DEV_DRIVER_DIRECTX) quisk_open_playback(pDev); } while (1) { pDev = *pCapture++; if ( ! pDev) break; if ( ! pDev->dev_errmsg[0] && pDev->driver == DEV_DRIVER_DIRECTX) quisk_open_capture(pDev); } } void quisk_close_sound_directx(struct sound_dev ** pCapture, struct sound_dev ** pPlayback) { struct sound_dev * pDev; while (*pPlayback) { pDev = *pPlayback; if (pDev->buffer) IDirectSoundBuffer8_Stop((LPDIRECTSOUNDBUFFER)pDev->buffer); pDev->handle = NULL; pPlayback++; } while (*pCapture) { pDev = *pCapture; if (pDev->buffer) IDirectSoundCaptureBuffer_Stop((LPDIRECTSOUNDCAPTUREBUFFER)pDev->buffer); pDev->handle = NULL; pCapture++; } } int quisk_read_directx(struct sound_dev * dev, complex double * cSamples) { // cSamples can be NULL to discard samples LPDIRECTSOUNDCAPTUREBUFFER ptBuf = (LPDIRECTSOUNDCAPTUREBUFFER)dev->buffer; HRESULT hr; DWORD readPos, captPos; LPVOID pt1, pt2; DWORD n1, n2; int16_t si, sq, * pts; float fi, fq, * ptf; int32_t li, lq, * ptl; int ii, qq, nSamples; int bytes, frames, bytes_per_frame; if ( ! dev->handle || ! dev->buffer) return 0; bytes_per_frame = dev->num_channels * dev->sample_bytes; hr = IDirectSoundCaptureBuffer8_GetCurrentPosition(ptBuf, &captPos, &readPos); if (hr != DS_OK) { #if DEBUG_IO QuiskPrintf ("Get CurrentPosition error 0x%lX\n", hr); #endif dev->dev_error++; return 0; } if ( ! dev->started) { dev->started = 1; dev->dataPos = readPos; } if (readPos >= dev->dataPos) bytes = readPos - dev->dataPos; else bytes = readPos - dev->dataPos + dev->play_buf_bytes; frames = bytes / bytes_per_frame; // frames available to read dev->dev_latency = frames; //if (dev->dev_index == t_Capture) QuiskPrintf("Read %d frames\n", frames); if ( ! bytes) return 0; if (IDirectSoundCaptureBuffer8_Lock(ptBuf, dev->dataPos, bytes, &pt1, &n1, &pt2, &n2, 0) != DS_OK) { dev->dev_error++; #if DEBUG_IO QuiskPrintf ("DirecctX capture lock error bytes %d\n", bytes); #endif return 0; } //QuiskPrintf ("%d %d %d %d\n", dev->channel_I, dev->channel_Q, bytes_per_frame, dev->num_channels); #if DEBUG_IO QuiskPrintf("%s read %4d bytes %4d frames from %9d to (%9lu %9lu) diff %9lu\n", dev->name, bytes, frames, dev->dataPos, readPos, captPos, captPos - readPos); #endif #if DEBUG_IO if (bytes != n1 + n2) QuiskPrintf ("Lock not equal to bytes\n"); #endif dev->dataPos += bytes; if (dev->dataPos >= dev->play_buf_bytes) dev->dataPos -= dev->play_buf_bytes; nSamples = 0; switch (dev->sound_format) { case Int16: pts = (short *)pt1; frames = (n1 + n2) / bytes_per_frame; bytes = 0; while (frames) { si = pts[dev->channel_I]; sq = pts[dev->channel_Q]; pts += dev->num_channels; if (si >= CLIP16 || si <= -CLIP16) dev->overrange++; // assume overrange returns max int if (sq >= CLIP16 || sq <= -CLIP16) dev->overrange++; ii = si << 16; qq = sq << 16; if (nSamples < SAMP_BUFFER_SIZE * 8 / 10) { if (cSamples) cSamples[nSamples] = ii + I * qq; nSamples++; } bytes += bytes_per_frame; frames--; if (bytes == n1) pts = (short *)pt2; } break; case Int32: ptl = (int *)pt1; frames = (n1 + n2) / bytes_per_frame; bytes = 0; while (frames) { li = ptl[dev->channel_I]; lq = ptl[dev->channel_Q]; ptl += dev->num_channels; if (li >= CLIP32 || li <= -CLIP32) dev->overrange++; // assume overrange returns max int if (lq >= CLIP32 || lq <= -CLIP32) dev->overrange++; if (nSamples < SAMP_BUFFER_SIZE * 8 / 10) { if (cSamples) cSamples[nSamples] = li + I * lq; nSamples++; } bytes += bytes_per_frame; frames--; if (bytes == n1) ptl = (int *)pt2; } break; case Float32: // use IEEE float ptf = (float *)pt1; frames = (n1 + n2) / bytes_per_frame; bytes = 0; while (frames) { fi = ptf[dev->channel_I]; fq = ptf[dev->channel_Q]; ptf += dev->num_channels; if (fabsf(fi) >= 1.0 || fabsf(fq) >= 1.0) dev->overrange++; // assume overrange returns maximum if (nSamples < SAMP_BUFFER_SIZE * 8 / 10) { if (cSamples) cSamples[nSamples] = (fi + I * fq) * 16777215; nSamples++; } bytes += bytes_per_frame; frames--; if (bytes == n1) { ptf = (float *)pt2; } } break; case Int24: break; } IDirectSoundCaptureBuffer8_Unlock(ptBuf, pt1, n1, pt2, n2); return nSamples; } void quisk_play_directx(struct sound_dev * dev, int nSamples, complex double * cSamples, int report_latency, double volume) { LPDIRECTSOUNDBUFFER ptBuf = (LPDIRECTSOUNDBUFFER)dev->buffer; DWORD playPos, writePos; // hardware index into buffer LPVOID pt1, pt2; DWORD n1, n2; short * pts; float * ptf; int * ptl; // int must be 32 bits int n, unavail, frames, bytes, pass, bytes_per_frame; float buffer_fill; if ( ! dev->handle || ! dev->buffer) return; bytes_per_frame = dev->num_channels * dev->sample_bytes; // Note: writePos moves ahead with playPos; it is not associated with write activity if (IDirectSoundBuffer8_GetCurrentPosition(ptBuf, &playPos, &writePos) != DS_OK) { if (quisk_sound_state.verbose_sound) QuiskPrintf ("Bad GetCurrentPosition for %s\n", dev->stream_description); quisk_sound_state.write_error++; dev->dev_error++; playPos = writePos = 0; } unavail = (int)writePos - (int)playPos; // Must not write to this region if (unavail < 0) unavail += dev->play_buf_bytes; dev->play_delay = dev->dataPos - playPos; // includes unavail if (dev->play_delay < 0) dev->play_delay += dev->play_buf_bytes; dev->cr_average_fill += ((double)(dev->play_delay - unavail) / bytes_per_frame + nSamples / 2) / (2 * dev->latency_frames); dev->cr_average_count++; // buffer_fill is (the writable play samples plus the current samples) / (2 * latency_frames) // so 0.5 filled represents the target level of dev->latency_frames buffer_fill = ((float)(dev->play_delay - unavail) / bytes_per_frame + nSamples) / (2 * dev->latency_frames); switch(dev->started) { case 0: // Starting state; wait for buffer to fill before starting play if (buffer_fill >= 0.5) { IDirectSoundBuffer8_Play (ptBuf, 0, 0, DSBPLAY_LOOPING); dev->started = 1; if (quisk_sound_state.verbose_sound) QuiskPrintf ("Start DirectX play at dev->latency_frames %d\n", dev->latency_frames); } break; case 1: // Normal run state // Measure the space available to write samples frames = (dev->play_buf_bytes - dev->play_delay - unavail) / bytes_per_frame; // Check for underrun n = unavail / bytes_per_frame + dev->latency_frames * 2 / 10 - nSamples; // minimum frames if (dev->dev_latency < n) { quisk_sound_state.underrun_error++; dev->dev_underrun++; if (quisk_sound_state.verbose_sound) QuiskPrintf ("Underrun error for %s\n", dev->stream_description); n += dev->latency_frames * 2 / 10; while (n-- > 0) cSamples[nSamples++] = 0; // add zero samples } // Check if play buffer is too full else if (dev->dev_latency > dev->latency_frames * 18 / 10 || nSamples >= frames) { quisk_sound_state.write_error++; dev->dev_error++; if (quisk_sound_state.verbose_sound) QuiskPrintf ("Buffer too full for %s\n", dev->stream_description); nSamples = 0; dev->started = 2; } break; case 2: // Buffer is too full; wait for it to drain if (buffer_fill <= 0.5) { dev->started = 1; if (quisk_sound_state.verbose_sound) QuiskPrintf("Resume adding samples for %s\n", dev->stream_description); } else { nSamples = 0; } break; } bytes = nSamples * bytes_per_frame; if (bytes <= 0) return; // write our data bytes at our data position dataPos if (IDirectSoundBuffer8_Lock(ptBuf, dev->dataPos, bytes, &pt1, &n1, &pt2, &n2, 0) != DS_OK) { if (quisk_sound_state.verbose_sound) QuiskPrintf ("DirectX play lock error for %s\n", dev->stream_description); quisk_sound_state.write_error++; dev->dev_error++; return; } pass = 0; n = 0; //if (dev->dev_index == t_Playback) QuiskPrintf("Buffer fill %.3f%% bytes %d for %s\n", buffer_fill * 100, bytes, dev->stream_description); switch (dev->sound_format) { case Int16: pts = (short *)pt1; // Start writing at pt1 frames = n1 / bytes_per_frame; for (n = 0; n < nSamples && pass < 2; n++) { pts[dev->channel_I] = (short)(volume * creal(cSamples[n]) / 65536); pts[dev->channel_Q] = (short)(volume * cimag(cSamples[n]) / 65536); pts += dev->num_channels; if (--frames <= 0) { pass++; // change to pt2 pts = (short *)pt2; frames = n2 / bytes_per_frame; } } break; case Int32: ptl = (int *)pt1; // Start writing at pt1 frames = n1 / bytes_per_frame; for (n = 0; n < nSamples && pass < 2; n++) { ptl[dev->channel_I] = (int)(volume * creal(cSamples[n])); ptl[dev->channel_Q] = (int)(volume * cimag(cSamples[n])); ptl += dev->num_channels; if (--frames <= 0) { pass++; // change to pt2 ptl = (int *)pt2; frames = n2 / bytes_per_frame; } } break; case Float32: // use IEEE float ptf = (float *)pt1; // Start writing at pt1 frames = n1 / bytes_per_frame; for (n = 0; n < nSamples && pass < 2; n++) { ptf[dev->channel_I] = (volume * creal(cSamples[n]) / CLIP32); ptf[dev->channel_Q] = (volume * cimag(cSamples[n]) / CLIP32); ptf += dev->num_channels; if (--frames <= 0) { pass++; // change to pt2 ptf = (float *)pt2; frames = n2 / bytes_per_frame; } } break; case Int24: break; } IDirectSoundBuffer8_Unlock(ptBuf, pt1, n1, pt2, n2); dev->dataPos += bytes; // update data write position if (dev->dataPos >= dev->play_buf_bytes) dev->dataPos -= dev->play_buf_bytes; dev->dev_latency = dev->play_delay / bytes_per_frame; // frames in buffer available to play if (report_latency) // Report latency for main playback device quisk_sound_state.latencyPlay = dev->dev_latency; dev->play_delay += bytes; // bytes available to play dev->old_play_delay = dev->play_delay; } #else // No directx available #include #include #include "quisk.h" PyObject * quisk_directx_sound_devices(PyObject * self, PyObject * args) { return quisk_dummy_sound_devices(self, args); } void quisk_start_sound_directx (struct sound_dev ** pCapture, struct sound_dev ** pPlayback) { struct sound_dev * pDev; const char * msg = "No driver support for this device"; while (*pCapture) { pDev = *pCapture++; if (pDev->driver == DEV_DRIVER_DIRECTX) { strMcpy(pDev->dev_errmsg, msg, QUISK_SC_SIZE); if (quisk_sound_state.verbose_sound) QuiskPrintf("%s\n", msg); } } while (*pPlayback) { pDev = *pPlayback++; if (pDev->driver == DEV_DRIVER_DIRECTX) { strMcpy(pDev->dev_errmsg, msg, QUISK_SC_SIZE); if (quisk_sound_state.verbose_sound) QuiskPrintf("%s\n", msg); } } } int quisk_read_directx(struct sound_dev * dev, complex double * cSamples) { return 0; } void quisk_play_directx(struct sound_dev * dev, int nSamples, complex double * cSamples, int report_latency, double volume) {} void quisk_close_sound_directx(struct sound_dev ** pCapture, struct sound_dev ** pPlayback) {} #endif