+RtApiDs :: RtApiDs()\r
+{\r
+ // Dsound will run both-threaded. If CoInitialize fails, then just\r
+ // accept whatever the mainline chose for a threading model.\r
+ coInitialized_ = false;\r
+ HRESULT hr = CoInitialize( NULL );\r
+ if ( !FAILED( hr ) ) coInitialized_ = true;\r
+}\r
+\r
+RtApiDs :: ~RtApiDs()\r
+{\r
+ if ( coInitialized_ ) CoUninitialize(); // balanced call.\r
+ if ( stream_.state != STREAM_CLOSED ) closeStream();\r
+}\r
+\r
+// The DirectSound default output is always the first device.\r
+unsigned int RtApiDs :: getDefaultOutputDevice( void )\r
+{\r
+ return 0;\r
+}\r
+\r
+// The DirectSound default input is always the first input device,\r
+// which is the first capture device enumerated.\r
+unsigned int RtApiDs :: getDefaultInputDevice( void )\r
+{\r
+ return 0;\r
+}\r
+\r
+unsigned int RtApiDs :: getDeviceCount( void )\r
+{\r
+ // Set query flag for previously found devices to false, so that we\r
+ // can check for any devices that have disappeared.\r
+ for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
+ dsDevices[i].found = false;\r
+\r
+ // Query DirectSound devices.\r
+ struct DsProbeData probeInfo;\r
+ probeInfo.isInput = false;\r
+ probeInfo.dsDevices = &dsDevices;\r
+ HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ }\r
+\r
+ // Query DirectSoundCapture devices.\r
+ probeInfo.isInput = true;\r
+ result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ }\r
+\r
+ // Clean out any devices that may have disappeared.\r
+ std::vector< int > indices;\r
+ for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
+ if ( dsDevices[i].found == false ) indices.push_back( i );\r
+ //unsigned int nErased = 0;\r
+ for ( unsigned int i=0; i<indices.size(); i++ )\r
+ dsDevices.erase( dsDevices.begin()+indices[i] );\r
+ //dsDevices.erase( dsDevices.begin()-nErased++ );\r
+\r
+ return static_cast<unsigned int>(dsDevices.size());\r
+}\r
+\r
+RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )\r
+{\r
+ RtAudio::DeviceInfo info;\r
+ info.probed = false;\r
+\r
+ if ( dsDevices.size() == 0 ) {\r
+ // Force a query of all devices\r
+ getDeviceCount();\r
+ if ( dsDevices.size() == 0 ) {\r
+ errorText_ = "RtApiDs::getDeviceInfo: no devices found!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+ }\r
+\r
+ if ( device >= dsDevices.size() ) {\r
+ errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+\r
+ HRESULT result;\r
+ if ( dsDevices[ device ].validId[0] == false ) goto probeInput;\r
+\r
+ LPDIRECTSOUND output;\r
+ DSCAPS outCaps;\r
+ result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto probeInput;\r
+ }\r
+\r
+ outCaps.dwSize = sizeof( outCaps );\r
+ result = output->GetCaps( &outCaps );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto probeInput;\r
+ }\r
+\r
+ // Get output channel information.\r
+ info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;\r
+\r
+ // Get sample rate information.\r
+ info.sampleRates.clear();\r
+ for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
+ if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&\r
+ SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) {\r
+ info.sampleRates.push_back( SAMPLE_RATES[k] );\r
+\r
+ if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
+ info.preferredSampleRate = SAMPLE_RATES[k];\r
+ }\r
+ }\r
+\r
+ // Get format information.\r
+ if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;\r
+\r
+ output->Release();\r
+\r
+ if ( getDefaultOutputDevice() == device )\r
+ info.isDefaultOutput = true;\r
+\r
+ if ( dsDevices[ device ].validId[1] == false ) {\r
+ info.name = dsDevices[ device ].name;\r
+ info.probed = true;\r
+ return info;\r
+ }\r
+\r
+ probeInput:\r
+\r
+ LPDIRECTSOUNDCAPTURE input;\r
+ result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ DSCCAPS inCaps;\r
+ inCaps.dwSize = sizeof( inCaps );\r
+ result = input->GetCaps( &inCaps );\r
+ if ( FAILED( result ) ) {\r
+ input->Release();\r
+ errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Get input channel information.\r
+ info.inputChannels = inCaps.dwChannels;\r
+\r
+ // Get sample rate and format information.\r
+ std::vector<unsigned int> rates;\r
+ if ( inCaps.dwChannels >= 2 ) {\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+\r
+ if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 );\r
+ }\r
+ else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 );\r
+ }\r
+ }\r
+ else if ( inCaps.dwChannels == 1 ) {\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+\r
+ if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 );\r
+ }\r
+ else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 );\r
+ }\r
+ }\r
+ else info.inputChannels = 0; // technically, this would be an error\r
+\r
+ input->Release();\r
+\r
+ if ( info.inputChannels == 0 ) return info;\r
+\r
+ // Copy the supported rates to the info structure but avoid duplication.\r
+ bool found;\r
+ for ( unsigned int i=0; i<rates.size(); i++ ) {\r
+ found = false;\r
+ for ( unsigned int j=0; j<info.sampleRates.size(); j++ ) {\r
+ if ( rates[i] == info.sampleRates[j] ) {\r
+ found = true;\r
+ break;\r
+ }\r
+ }\r
+ if ( found == false ) info.sampleRates.push_back( rates[i] );\r
+ }\r
+ std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
+\r
+ // If device opens for both playback and capture, we determine the channels.\r
+ if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
+ info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
+\r
+ if ( device == 0 ) info.isDefaultInput = true;\r
+\r
+ // Copy name and return.\r
+ info.name = dsDevices[ device ].name;\r
+ info.probed = true;\r
+ return info;\r
+}\r
+\r
+bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
+ unsigned int firstChannel, unsigned int sampleRate,\r
+ RtAudioFormat format, unsigned int *bufferSize,\r
+ RtAudio::StreamOptions *options )\r
+{\r
+ if ( channels + firstChannel > 2 ) {\r
+ errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";\r
+ return FAILURE;\r
+ }\r
+\r
+ size_t nDevices = dsDevices.size();\r
+ if ( nDevices == 0 ) {\r
+ // This should not happen because a check is made before this function is called.\r
+ errorText_ = "RtApiDs::probeDeviceOpen: no devices found!";\r
+ return FAILURE;\r
+ }\r
+\r
+ if ( device >= nDevices ) {\r
+ // This should not happen because a check is made before this function is called.\r
+ errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!";\r
+ return FAILURE;\r
+ }\r
+\r
+ if ( mode == OUTPUT ) {\r
+ if ( dsDevices[ device ].validId[0] == false ) {\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ }\r
+ else { // mode == INPUT\r
+ if ( dsDevices[ device ].validId[1] == false ) {\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ }\r
+\r
+ // According to a note in PortAudio, using GetDesktopWindow()\r
+ // instead of GetForegroundWindow() is supposed to avoid problems\r
+ // that occur when the application's window is not the foreground\r
+ // window. Also, if the application window closes before the\r
+ // DirectSound buffer, DirectSound can crash. In the past, I had\r
+ // problems when using GetDesktopWindow() but it seems fine now\r
+ // (January 2010). I'll leave it commented here.\r
+ // HWND hWnd = GetForegroundWindow();\r
+ HWND hWnd = GetDesktopWindow();\r
+\r
+ // Check the numberOfBuffers parameter and limit the lowest value to\r
+ // two. This is a judgement call and a value of two is probably too\r
+ // low for capture, but it should work for playback.\r
+ int nBuffers = 0;\r
+ if ( options ) nBuffers = options->numberOfBuffers;\r
+ if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;\r
+ if ( nBuffers < 2 ) nBuffers = 3;\r
+\r
+ // Check the lower range of the user-specified buffer size and set\r
+ // (arbitrarily) to a lower bound of 32.\r
+ if ( *bufferSize < 32 ) *bufferSize = 32;\r
+\r
+ // Create the wave format structure. The data format setting will\r
+ // be determined later.\r
+ WAVEFORMATEX waveFormat;\r
+ ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );\r
+ waveFormat.wFormatTag = WAVE_FORMAT_PCM;\r
+ waveFormat.nChannels = channels + firstChannel;\r
+ waveFormat.nSamplesPerSec = (unsigned long) sampleRate;\r
+\r
+ // Determine the device buffer size. By default, we'll use the value\r
+ // defined above (32K), but we will grow it to make allowances for\r
+ // very large software buffer sizes.\r
+ DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;\r
+ DWORD dsPointerLeadTime = 0;\r
+\r
+ void *ohandle = 0, *bhandle = 0;\r
+ HRESULT result;\r
+ if ( mode == OUTPUT ) {\r
+\r
+ LPDIRECTSOUND output;\r
+ result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ DSCAPS outCaps;\r
+ outCaps.dwSize = sizeof( outCaps );\r
+ result = output->GetCaps( &outCaps );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Check channel information.\r
+ if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {\r
+ errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Check format information. Use 16-bit format unless not\r
+ // supported or user requests 8-bit.\r
+ if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&\r
+ !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {\r
+ waveFormat.wBitsPerSample = 16;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
+ }\r
+ else {\r
+ waveFormat.wBitsPerSample = 8;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
+ }\r
+ stream_.userFormat = format;\r
+\r
+ // Update wave format structure and buffer information.\r
+ waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
+ waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
+ dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
+\r
+ // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
+ while ( dsPointerLeadTime * 2U > dsBufferSize )\r
+ dsBufferSize *= 2;\r
+\r
+ // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.\r
+ // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );\r
+ // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.\r
+ result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Even though we will write to the secondary buffer, we need to\r
+ // access the primary buffer to set the correct output format\r
+ // (since the default is 8-bit, 22 kHz!). Setup the DS primary\r
+ // buffer description.\r
+ DSBUFFERDESC bufferDescription;\r
+ ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
+ bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
+ bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;\r
+\r
+ // Obtain the primary buffer\r
+ LPDIRECTSOUNDBUFFER buffer;\r
+ result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Set the primary DS buffer sound format.\r
+ result = buffer->SetFormat( &waveFormat );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Setup the secondary DS buffer description.\r
+ ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
+ bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
+ bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
+ DSBCAPS_GLOBALFOCUS |\r
+ DSBCAPS_GETCURRENTPOSITION2 |\r
+ DSBCAPS_LOCHARDWARE ); // Force hardware mixing\r
+ bufferDescription.dwBufferBytes = dsBufferSize;\r
+ bufferDescription.lpwfxFormat = &waveFormat;\r
+\r
+ // Try to create the secondary DS buffer. If that doesn't work,\r
+ // try to use software mixing. Otherwise, there's a problem.\r
+ result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
+ if ( FAILED( result ) ) {\r
+ bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
+ DSBCAPS_GLOBALFOCUS |\r
+ DSBCAPS_GETCURRENTPOSITION2 |\r
+ DSBCAPS_LOCSOFTWARE ); // Force software mixing\r
+ result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ }\r
+\r
+ // Get the buffer size ... might be different from what we specified.\r
+ DSBCAPS dsbcaps;\r
+ dsbcaps.dwSize = sizeof( DSBCAPS );\r
+ result = buffer->GetCaps( &dsbcaps );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ buffer->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ dsBufferSize = dsbcaps.dwBufferBytes;\r
+\r
+ // Lock the DS buffer\r
+ LPVOID audioPtr;\r
+ DWORD dataLen;\r
+ result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ buffer->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Zero the DS buffer\r
+ ZeroMemory( audioPtr, dataLen );\r
+\r
+ // Unlock the DS buffer\r
+ result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ buffer->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ ohandle = (void *) output;\r
+ bhandle = (void *) buffer;\r
+ }\r
+\r
+ if ( mode == INPUT ) {\r
+\r
+ LPDIRECTSOUNDCAPTURE input;\r
+ result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ DSCCAPS inCaps;\r
+ inCaps.dwSize = sizeof( inCaps );\r
+ result = input->GetCaps( &inCaps );\r
+ if ( FAILED( result ) ) {\r
+ input->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Check channel information.\r
+ if ( inCaps.dwChannels < channels + firstChannel ) {\r
+ errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";\r
+ return FAILURE;\r
+ }\r
+\r
+ // Check format information. Use 16-bit format unless user\r
+ // requests 8-bit.\r
+ DWORD deviceFormats;\r
+ if ( channels + firstChannel == 2 ) {\r
+ deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;\r
+ if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
+ waveFormat.wBitsPerSample = 8;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
+ }\r
+ else { // assume 16-bit is supported\r
+ waveFormat.wBitsPerSample = 16;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
+ }\r
+ }\r
+ else { // channel == 1\r
+ deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;\r
+ if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
+ waveFormat.wBitsPerSample = 8;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
+ }\r
+ else { // assume 16-bit is supported\r
+ waveFormat.wBitsPerSample = 16;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
+ }\r
+ }\r
+ stream_.userFormat = format;\r
+\r
+ // Update wave format structure and buffer information.\r
+ waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
+ waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
+ dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
+\r
+ // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
+ while ( dsPointerLeadTime * 2U > dsBufferSize )\r
+ dsBufferSize *= 2;\r
+\r
+ // Setup the secondary DS buffer description.\r
+ DSCBUFFERDESC bufferDescription;\r
+ ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );\r
+ bufferDescription.dwSize = sizeof( DSCBUFFERDESC );\r
+ bufferDescription.dwFlags = 0;\r
+ bufferDescription.dwReserved = 0;\r
+ bufferDescription.dwBufferBytes = dsBufferSize;\r
+ bufferDescription.lpwfxFormat = &waveFormat;\r
+\r
+ // Create the capture buffer.\r
+ LPDIRECTSOUNDCAPTUREBUFFER buffer;\r
+ result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );\r
+ if ( FAILED( result ) ) {\r
+ input->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Get the buffer size ... might be different from what we specified.\r
+ DSCBCAPS dscbcaps;\r
+ dscbcaps.dwSize = sizeof( DSCBCAPS );\r
+ result = buffer->GetCaps( &dscbcaps );\r
+ if ( FAILED( result ) ) {\r
+ input->Release();\r
+ buffer->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ dsBufferSize = dscbcaps.dwBufferBytes;\r
+\r
+ // NOTE: We could have a problem here if this is a duplex stream\r
+ // and the play and capture hardware buffer sizes are different\r
+ // (I'm actually not sure if that is a problem or not).\r
+ // Currently, we are not verifying that.\r
+\r
+ // Lock the capture buffer\r
+ LPVOID audioPtr;\r
+ DWORD dataLen;\r
+ result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ input->Release();\r
+ buffer->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Zero the buffer\r
+ ZeroMemory( audioPtr, dataLen );\r
+\r
+ // Unlock the buffer\r
+ result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ input->Release();\r
+ buffer->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ ohandle = (void *) input;\r
+ bhandle = (void *) buffer;\r
+ }\r
+\r
+ // Set various stream parameters\r
+ DsHandle *handle = 0;\r
+ stream_.nDeviceChannels[mode] = channels + firstChannel;\r
+ stream_.nUserChannels[mode] = channels;\r
+ stream_.bufferSize = *bufferSize;\r
+ stream_.channelOffset[mode] = firstChannel;\r
+ stream_.deviceInterleaved[mode] = true;\r
+ if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
+ else stream_.userInterleaved = true;\r
+\r
+ // Set flag for buffer conversion\r
+ stream_.doConvertBuffer[mode] = false;\r
+ if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])\r
+ stream_.doConvertBuffer[mode] = true;\r
+ if (stream_.userFormat != stream_.deviceFormat[mode])\r
+ stream_.doConvertBuffer[mode] = true;\r
+ if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
+ stream_.nUserChannels[mode] > 1 )\r
+ stream_.doConvertBuffer[mode] = true;\r
+\r
+ // Allocate necessary internal buffers\r
+ long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
+ stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.userBuffer[mode] == NULL ) {\r
+ errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";\r
+ goto error;\r
+ }\r
+\r
+ if ( stream_.doConvertBuffer[mode] ) {\r
+\r
+ bool makeBuffer = true;\r
+ bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
+ if ( mode == INPUT ) {\r
+ if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
+ unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
+ if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;\r
+ }\r
+ }\r
+\r
+ if ( makeBuffer ) {\r
+ bufferBytes *= *bufferSize;\r
+ if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.deviceBuffer == NULL ) {\r
+ errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";\r
+ goto error;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Allocate our DsHandle structures for the stream.\r
+ if ( stream_.apiHandle == 0 ) {\r
+ try {\r
+ handle = new DsHandle;\r
+ }\r
+ catch ( std::bad_alloc& ) {\r
+ errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";\r
+ goto error;\r
+ }\r
+\r
+ // Create a manual-reset event.\r
+ handle->condition = CreateEvent( NULL, // no security\r
+ TRUE, // manual-reset\r
+ FALSE, // non-signaled initially\r
+ NULL ); // unnamed\r
+ stream_.apiHandle = (void *) handle;\r
+ }\r
+ else\r
+ handle = (DsHandle *) stream_.apiHandle;\r
+ handle->id[mode] = ohandle;\r
+ handle->buffer[mode] = bhandle;\r
+ handle->dsBufferSize[mode] = dsBufferSize;\r
+ handle->dsPointerLeadTime[mode] = dsPointerLeadTime;\r
+\r
+ stream_.device[mode] = device;\r
+ stream_.state = STREAM_STOPPED;\r
+ if ( stream_.mode == OUTPUT && mode == INPUT )\r
+ // We had already set up an output stream.\r
+ stream_.mode = DUPLEX;\r
+ else\r
+ stream_.mode = mode;\r
+ stream_.nBuffers = nBuffers;\r
+ stream_.sampleRate = sampleRate;\r
+\r
+ // Setup the buffer conversion information structure.\r
+ if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
+\r
+ // Setup the callback thread.\r
+ if ( stream_.callbackInfo.isRunning == false ) {\r
+ unsigned threadId;\r
+ stream_.callbackInfo.isRunning = true;\r
+ stream_.callbackInfo.object = (void *) this;\r
+ stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,\r
+ &stream_.callbackInfo, 0, &threadId );\r
+ if ( stream_.callbackInfo.thread == 0 ) {\r
+ errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";\r
+ goto error;\r
+ }\r
+\r
+ // Boost DS thread priority\r
+ SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );\r
+ }\r
+ return SUCCESS;\r
+\r
+ error:\r
+ if ( handle ) {\r
+ if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
+ LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
+ LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ if ( buffer ) buffer->Release();\r
+ object->Release();\r
+ }\r
+ if ( handle->buffer[1] ) {\r
+ LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
+ LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
+ if ( buffer ) buffer->Release();\r
+ object->Release();\r
+ }\r
+ CloseHandle( handle->condition );\r
+ delete handle;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ stream_.state = STREAM_CLOSED;\r
+ return FAILURE;\r
+}\r
+\r
+void RtApiDs :: closeStream()\r
+{\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiDs::closeStream(): no open stream to close!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ // Stop the callback thread.\r
+ stream_.callbackInfo.isRunning = false;\r
+ WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );\r
+ CloseHandle( (HANDLE) stream_.callbackInfo.thread );\r
+\r
+ DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
+ if ( handle ) {\r
+ if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
+ LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
+ LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ if ( buffer ) {\r
+ buffer->Stop();\r
+ buffer->Release();\r
+ }\r
+ object->Release();\r
+ }\r
+ if ( handle->buffer[1] ) {\r
+ LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
+ LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
+ if ( buffer ) {\r
+ buffer->Stop();\r
+ buffer->Release();\r
+ }\r
+ object->Release();\r
+ }\r
+ CloseHandle( handle->condition );\r
+ delete handle;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ stream_.mode = UNINITIALIZED;\r
+ stream_.state = STREAM_CLOSED;\r
+}\r
+\r
+void RtApiDs :: startStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_RUNNING ) {\r
+ errorText_ = "RtApiDs::startStream(): the stream is already running!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
+\r
+ // Increase scheduler frequency on lesser windows (a side-effect of\r
+ // increasing timer accuracy). On greater windows (Win2K or later),\r
+ // this is already in effect.\r
+ timeBeginPeriod( 1 ); \r
+\r
+ buffersRolling = false;\r
+ duplexPrerollBytes = 0;\r
+\r
+ if ( stream_.mode == DUPLEX ) {\r
+ // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.\r
+ duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );\r
+ }\r
+\r
+ HRESULT result = 0;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+\r
+ LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ result = buffer->Play( 0, 0, DSBPLAY_LOOPING );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
+\r
+ LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
+ result = buffer->Start( DSCBSTART_LOOPING );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ handle->drainCounter = 0;\r
+ handle->internalDrain = false;\r
+ ResetEvent( handle->condition );\r
+ stream_.state = STREAM_RUNNING;\r
+\r
+ unlock:\r
+ if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiDs :: stopStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ HRESULT result = 0;\r
+ LPVOID audioPtr;\r
+ DWORD dataLen;\r
+ DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+ if ( handle->drainCounter == 0 ) {\r
+ handle->drainCounter = 2;\r
+ WaitForSingleObject( handle->condition, INFINITE ); // block until signaled\r
+ }\r
+\r
+ stream_.state = STREAM_STOPPED;\r
+\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ // Stop the buffer and clear memory\r
+ LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ result = buffer->Stop();\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+\r
+ // Lock the buffer and clear it so that if we start to play again,\r
+ // we won't have old data playing.\r
+ result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+\r
+ // Zero the DS buffer\r
+ ZeroMemory( audioPtr, dataLen );\r
+\r
+ // Unlock the DS buffer\r
+ result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+\r
+ // If we start playing again, we must begin at beginning of buffer.\r
+ handle->bufferPointer[0] = 0;\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
+ LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
+ audioPtr = NULL;\r
+ dataLen = 0;\r
+\r
+ stream_.state = STREAM_STOPPED;\r
+\r
+ if ( stream_.mode != DUPLEX )\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ result = buffer->Stop();\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+\r
+ // Lock the buffer and clear it so that if we start to play again,\r
+ // we won't have old data playing.\r
+ result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+\r
+ // Zero the DS buffer\r
+ ZeroMemory( audioPtr, dataLen );\r
+\r
+ // Unlock the DS buffer\r
+ result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+\r
+ // If we start recording again, we must begin at beginning of buffer.\r
+ handle->bufferPointer[1] = 0;\r
+ }\r
+\r
+ unlock:\r
+ timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+\r
+ if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiDs :: abortStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
+ handle->drainCounter = 2;\r
+\r
+ stopStream();\r
+}\r
+\r
+void RtApiDs :: callbackEvent()\r
+{\r
+ if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) {\r
+ Sleep( 50 ); // sleep 50 milliseconds\r
+ return;\r
+ }\r
+\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
+ DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
+\r
+ // Check if we were draining the stream and signal is finished.\r
+ if ( handle->drainCounter > stream_.nBuffers + 2 ) {\r
+\r
+ stream_.state = STREAM_STOPPING;\r
+ if ( handle->internalDrain == false )\r
+ SetEvent( handle->condition );\r
+ else\r
+ stopStream();\r
+ return;\r
+ }\r
+\r
+ // Invoke user callback to get fresh output data UNLESS we are\r
+ // draining stream.\r
+ if ( handle->drainCounter == 0 ) {\r
+ RtAudioCallback callback = (RtAudioCallback) info->callback;\r
+ double streamTime = getStreamTime();\r
+ RtAudioStreamStatus status = 0;\r
+ if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
+ status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
+ handle->xrun[0] = false;\r
+ }\r
+ if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
+ status |= RTAUDIO_INPUT_OVERFLOW;\r
+ handle->xrun[1] = false;\r
+ }\r
+ int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
+ stream_.bufferSize, streamTime, status, info->userData );\r
+ if ( cbReturnValue == 2 ) {\r
+ stream_.state = STREAM_STOPPING;\r
+ handle->drainCounter = 2;\r
+ abortStream();\r
+ return;\r
+ }\r
+ else if ( cbReturnValue == 1 ) {\r
+ handle->drainCounter = 1;\r
+ handle->internalDrain = true;\r
+ }\r
+ }\r
+\r
+ HRESULT result;\r
+ DWORD currentWritePointer, safeWritePointer;\r
+ DWORD currentReadPointer, safeReadPointer;\r
+ UINT nextWritePointer;\r
+\r
+ LPVOID buffer1 = NULL;\r
+ LPVOID buffer2 = NULL;\r
+ DWORD bufferSize1 = 0;\r
+ DWORD bufferSize2 = 0;\r
+\r
+ char *buffer;\r
+ long bufferBytes;\r
+\r
+ MUTEX_LOCK( &stream_.mutex );\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ return;\r
+ }\r
+\r
+ if ( buffersRolling == false ) {\r
+ if ( stream_.mode == DUPLEX ) {\r
+ //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
+\r
+ // It takes a while for the devices to get rolling. As a result,\r
+ // there's no guarantee that the capture and write device pointers\r
+ // will move in lockstep. Wait here for both devices to start\r
+ // rolling, and then set our buffer pointers accordingly.\r
+ // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600\r
+ // bytes later than the write buffer.\r
+\r
+ // Stub: a serious risk of having a pre-emptive scheduling round\r
+ // take place between the two GetCurrentPosition calls... but I'm\r
+ // really not sure how to solve the problem. Temporarily boost to\r
+ // Realtime priority, maybe; but I'm not sure what priority the\r
+ // DirectSound service threads run at. We *should* be roughly\r
+ // within a ms or so of correct.\r
+\r
+ LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
+\r
+ DWORD startSafeWritePointer, startSafeReadPointer;\r
+\r
+ result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ while ( true ) {\r
+ result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break;\r
+ Sleep( 1 );\r
+ }\r
+\r
+ //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
+\r
+ handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
+ if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
+ handle->bufferPointer[1] = safeReadPointer;\r
+ }\r
+ else if ( stream_.mode == OUTPUT ) {\r
+\r
+ // Set the proper nextWritePosition after initial startup.\r
+ LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ result = dsWriteBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer );\r