waveout_stream_raii( commandlineflags & flags ) : waveout(NULL) , num_channels(0) , num_chunks(0) , frames_per_chunk(0) , bytes_per_chunk(0) { if ( flags.buffer == default_high ) { flags.buffer = 150; } else if ( flags.buffer == default_low ) { flags.buffer = 50; } if ( flags.period == default_high ) { flags.period = 30; } else if ( flags.period == default_low ) { flags.period = 10; } flags.apply_default_buffer_sizes(); WAVEFORMATEX wfx; ZeroMemory( &wfx, sizeof( wfx ) ); wfx.wFormatTag = flags.use_float ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM; wfx.nChannels = flags.channels; wfx.nSamplesPerSec = flags.samplerate; wfx.wBitsPerSample = flags.use_float ? 32 : 16; wfx.nBlockAlign = ( wfx.wBitsPerSample / 8 ) * wfx.nChannels; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; wfx.cbSize = 0; waveOutOpen( &waveout, flags.device == -1 ? WAVE_MAPPER : flags.device, &wfx, 0, 0, CALLBACK_NULL ); num_channels = flags.channels; std::size_t frames_per_buffer = flags.samplerate * flags.buffer / 1000; num_chunks = ( flags.buffer + flags.period - 1 ) / flags.period; if ( num_chunks < 2 ) { num_chunks = 2; } frames_per_chunk = ( frames_per_buffer + num_chunks - 1 ) / num_chunks; bytes_per_chunk = wfx.nBlockAlign * frames_per_chunk; waveheaders.resize( num_chunks ); wavebuffers.resize( num_chunks ); for ( std::size_t i = 0; i < num_chunks; ++i ) { wavebuffers[i].resize( bytes_per_chunk ); waveheaders[i] = WAVEHDR(); waveheaders[i].lpData = wavebuffers[i].data(); waveheaders[i].dwBufferLength = static_cast<DWORD>( wavebuffers[i].size() ); waveheaders[i].dwFlags = 0; waveOutPrepareHeader( waveout, &waveheaders[i], sizeof( WAVEHDR ) ); } }
portaudio_stream_blocking_raii( commandlineflags & flags, std::ostream & log ) : portaudio_raii(flags.verbose, log) , stream(NULL) , interleaved(false) , channels(flags.channels) { PaStreamParameters streamparameters; std::memset( &streamparameters, 0, sizeof(PaStreamParameters) ); std::istringstream device_string( flags.device ); int device = -1; device_string >> device; streamparameters.device = ( device == -1 ) ? Pa_GetDefaultOutputDevice() : device; streamparameters.channelCount = flags.channels; streamparameters.sampleFormat = ( flags.use_float ? paFloat32 : paInt16 ) | paNonInterleaved; if ( flags.buffer == default_high ) { streamparameters.suggestedLatency = Pa_GetDeviceInfo( streamparameters.device )->defaultHighOutputLatency; flags.buffer = static_cast<std::int32_t>( Pa_GetDeviceInfo( streamparameters.device )->defaultHighOutputLatency * 1000.0 ); } else if ( flags.buffer == default_low ) { streamparameters.suggestedLatency = Pa_GetDeviceInfo( streamparameters.device )->defaultLowOutputLatency; flags.buffer = static_cast<std::int32_t>( Pa_GetDeviceInfo( streamparameters.device )->defaultLowOutputLatency * 1000.0 ); } else { streamparameters.suggestedLatency = flags.buffer * 0.001; } unsigned long framesperbuffer = 0; if ( flags.mode != ModeUI ) { framesperbuffer = paFramesPerBufferUnspecified; flags.period = 50; flags.period = std::min<std::int32_t>( flags.period, flags.buffer / 3 ); } else if ( flags.period == default_high ) { framesperbuffer = paFramesPerBufferUnspecified; flags.period = 50; flags.period = std::min<std::int32_t>( flags.period, flags.buffer / 3 ); } else if ( flags.period == default_low ) { framesperbuffer = paFramesPerBufferUnspecified; flags.period = 10; flags.period = std::min<std::int32_t>( flags.period, flags.buffer / 3 ); } else { framesperbuffer = flags.period * flags.samplerate / 1000; } if ( flags.period <= 0 ) { flags.period = 1; } flags.apply_default_buffer_sizes(); if ( flags.verbose ) { log << "PortAudio:" << std::endl; log << " device: " << streamparameters.device << " [ " << Pa_GetHostApiInfo( Pa_GetDeviceInfo( streamparameters.device )->hostApi )->name << " / " << Pa_GetDeviceInfo( streamparameters.device )->name << " ] " << std::endl; log << " low latency: " << Pa_GetDeviceInfo( streamparameters.device )->defaultLowOutputLatency << std::endl; log << " high latency: " << Pa_GetDeviceInfo( streamparameters.device )->defaultHighOutputLatency << std::endl; log << " suggested latency: " << streamparameters.suggestedLatency << std::endl; log << " frames per buffer: " << framesperbuffer << std::endl; log << " ui redraw: " << flags.period << std::endl; } PaError e = PaError(); e = Pa_OpenStream( &stream, NULL, &streamparameters, flags.samplerate, framesperbuffer, ( flags.dither > 0 ) ? paNoFlag : paDitherOff, NULL, NULL ); if ( e != paNoError ) { // Non-interleaved failed, try interleaved next. // This might help broken portaudio on MacOS X. streamparameters.sampleFormat &= ~paNonInterleaved; e = Pa_OpenStream( &stream, NULL, &streamparameters, flags.samplerate, framesperbuffer, ( flags.dither > 0 ) ? paNoFlag : paDitherOff, NULL, NULL ); if ( e == paNoError ) { interleaved = true; } check_portaudio_error( e ); } check_portaudio_error( Pa_StartStream( stream ) ); if ( flags.verbose ) { log << " channels: " << streamparameters.channelCount << std::endl; log << " sampleformat: " << ( ( ( streamparameters.sampleFormat & ~paNonInterleaved ) == paFloat32 ) ? "paFloat32" : "paInt16" ) << std::endl; log << " latency: " << Pa_GetStreamInfo( stream )->outputLatency << std::endl; log << " samplerate: " << Pa_GetStreamInfo( stream )->sampleRate << std::endl; log << std::endl; } }