| 248 | } |
| 249 | |
| 250 | void OutputStream::open() |
| 251 | { |
| 252 | mBytesPerSample = sizeof( float ); |
| 253 | mBytesPerFrame = mNumChannels * mBytesPerSample; |
| 254 | mBytesPerBuffer = mFramesPerBlock * mBytesPerFrame; |
| 255 | |
| 256 | pa_sample_spec sampleSpec; |
| 257 | sampleSpec.format = PA_SAMPLE_FLOAT32LE; |
| 258 | sampleSpec.rate = mSampleRate; |
| 259 | sampleSpec.channels = mNumChannels; |
| 260 | CI_ASSERT( pa_sample_spec_valid( &sampleSpec ) ); |
| 261 | |
| 262 | // default channel map for now |
| 263 | pa_channel_map* channelMap = nullptr; |
| 264 | |
| 265 | { |
| 266 | pulse::ScopedPropertyList propList; |
| 267 | //pa_proplist_sets( propList.get(), PA_PROP_APPLICATION_ICON_NAME, "cinder-pulseaudio-output" ); |
| 268 | |
| 269 | mPaStream = pa_stream_new_with_proplist( mContext->mPaContext, "Playback", &sampleSpec, channelMap, propList.get() ); |
| 270 | if( ! mPaStream ) { |
| 271 | int errorNum = pa_context_errno( mContext->mPaContext ); |
| 272 | std::string errorText = pa_strerror( errorNum ); |
| 273 | throw AudioContextExc( "Could not create PulseAudio output stream" + errorText ); |
| 274 | } |
| 275 | |
| 276 | pa_stream_set_state_callback( mPaStream, &Stream::notifyCallback, static_cast<void*>( this ) ); |
| 277 | |
| 278 | // Even though we start the stream corked above, PulseAudio will issue one stream request |
| 279 | // after setup. OutputDeviceNodePulseAudioImpl::playerCallback() must fulfill the write. |
| 280 | pa_stream_set_write_callback( mPaStream, &OutputStream::writeCallback, static_cast<void*>( this ) ); |
| 281 | |
| 282 | pa_buffer_attr bufferAttr; |
| 283 | bufferAttr.maxlength = uint32_t( mFramesPerBlock * mBytesPerFrame ); |
| 284 | bufferAttr.minreq = UINT32_MAX; |
| 285 | bufferAttr.prebuf = 0; |
| 286 | bufferAttr.tlength = bufferAttr.maxlength; |
| 287 | bufferAttr.fragsize = UINT32_MAX; |
| 288 | |
| 289 | pa_stream_flags_t streamFlags = pa_stream_flags_t( |
| 290 | PA_STREAM_START_CORKED | |
| 291 | PA_STREAM_AUTO_TIMING_UPDATE | |
| 292 | PA_STREAM_INTERPOLATE_TIMING | |
| 293 | PA_STREAM_ADJUST_LATENCY ); |
| 294 | |
| 295 | int status = pa_stream_connect_playback( mPaStream, mDeviceName.c_str(), &bufferAttr, streamFlags, nullptr, nullptr ); |
| 296 | if( status ) { |
| 297 | throw AudioContextExc( "Could not connect PulseAudio output stream playback" ); |
| 298 | } |
| 299 | |
| 300 | // Wait for the stream to be ready. |
| 301 | while( true ) { |
| 302 | pa_stream_state_t state = pa_stream_get_state( mPaStream ); |
| 303 | if( ! PA_STREAM_IS_GOOD( state ) ) { |
| 304 | throw AudioContextExc( "OutputStream connect failed" ); |
| 305 | } |
| 306 | |
| 307 | if( PA_STREAM_READY == state ) { |
no test coverage detected