123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- /*
- * openmpt123_portaudio.hpp
- * ------------------------
- * Purpose: libopenmpt command line player
- * Notes : (currently none)
- * Authors: OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #ifndef OPENMPT123_PORTAUDIO_HPP
- #define OPENMPT123_PORTAUDIO_HPP
- #include "openmpt123_config.hpp"
- #include "openmpt123.hpp"
- #if defined(MPT_WITH_PORTAUDIO)
- #include <portaudio.h>
- namespace openmpt123 {
- struct portaudio_exception : public exception {
- portaudio_exception( PaError code ) : exception( Pa_GetErrorText( code ) ) { }
- };
- typedef void (*PaUtilLogCallback ) (const char *log);
- #ifdef _MSC_VER
- extern "C" void PaUtil_SetDebugPrintFunction(PaUtilLogCallback cb);
- #endif
- class portaudio_raii {
- private:
- std::ostream & log;
- bool log_set;
- bool portaudio_initialized;
- static std::ostream * portaudio_log_stream;
- private:
- static void portaudio_log_function( const char * log ) {
- if ( portaudio_log_stream ) {
- *portaudio_log_stream << "PortAudio: " << log;
- }
- }
- protected:
- void check_portaudio_error( PaError e ) {
- if ( e > 0 ) {
- return;
- }
- if ( e == paNoError ) {
- return;
- }
- if ( e == paOutputUnderflowed ) {
- log << "PortAudio warning: " << Pa_GetErrorText( e ) << std::endl;
- return;
- }
- throw portaudio_exception( e );
- }
- public:
- portaudio_raii( bool verbose, std::ostream & log ) : log(log), log_set(false), portaudio_initialized(false) {
- if ( verbose ) {
- portaudio_log_stream = &log;
- } else {
- portaudio_log_stream = 0;
- }
- #ifdef _MSC_VER
- PaUtil_SetDebugPrintFunction( portaudio_log_function );
- log_set = true;
- #endif
- check_portaudio_error( Pa_Initialize() );
- portaudio_initialized = true;
- if ( verbose ) {
- *portaudio_log_stream << std::endl;
- }
- }
- ~portaudio_raii() {
- if ( portaudio_initialized ) {
- check_portaudio_error( Pa_Terminate() );
- portaudio_initialized = false;
- }
- if ( log_set ) {
- #ifdef _MSC_VER
- PaUtil_SetDebugPrintFunction( NULL );
- log_set = false;
- #endif
- }
- portaudio_log_stream = 0;
- }
- };
- std::ostream * portaudio_raii::portaudio_log_stream = 0;
- class portaudio_stream_blocking_raii : public portaudio_raii, public write_buffers_interface {
- private:
- PaStream * stream;
- bool interleaved;
- std::size_t channels;
- std::vector<float> sampleBufFloat;
- std::vector<std::int16_t> sampleBufInt;
- public:
- 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 != Mode::UI ) {
- framesperbuffer = paFramesPerBufferUnspecified;
- flags.period = 50;
- flags.period = std::min( flags.period, flags.buffer / 3 );
- } else if ( flags.period == default_high ) {
- framesperbuffer = paFramesPerBufferUnspecified;
- flags.period = 50;
- flags.period = std::min( flags.period, flags.buffer / 3 );
- } else if ( flags.period == default_low ) {
- framesperbuffer = paFramesPerBufferUnspecified;
- flags.period = 10;
- flags.period = std::min( 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;
- }
- }
- ~portaudio_stream_blocking_raii() {
- if ( stream ) {
- PaError stopped = Pa_IsStreamStopped( stream );
- check_portaudio_error( stopped );
- if ( !stopped ) {
- check_portaudio_error( Pa_StopStream( stream ) );
- }
- check_portaudio_error( Pa_CloseStream( stream ) );
- stream = NULL;
- }
- }
- private:
- template<typename Tsample>
- void write_frames( const Tsample * buffer, std::size_t frames ) {
- while ( frames > 0 ) {
- unsigned long chunk_frames = static_cast<unsigned long>( std::min( static_cast<std::uint64_t>( frames ), static_cast<std::uint64_t>( std::numeric_limits<unsigned long>::max() ) ) );
- check_portaudio_error( Pa_WriteStream( stream, buffer, chunk_frames ) );
- buffer += chunk_frames * channels;
- frames -= chunk_frames;
- }
- }
- template<typename Tsample>
- void write_frames( std::vector<Tsample*> buffers, std::size_t frames ) {
- while ( frames > 0 ) {
- unsigned long chunk_frames = static_cast<unsigned long>( std::min( static_cast<std::uint64_t>( frames ), static_cast<std::uint64_t>( std::numeric_limits<unsigned long>::max() ) ) );
- check_portaudio_error( Pa_WriteStream( stream, buffers.data(), chunk_frames ) );
- for ( std::size_t channel = 0; channel < channels; ++channel ) {
- buffers[channel] += chunk_frames;
- }
- frames -= chunk_frames;
- }
- }
- public:
- void write( const std::vector<float*> buffers, std::size_t frames ) override {
- if ( interleaved ) {
- sampleBufFloat.clear();
- for ( std::size_t frame = 0; frame < frames; ++frame ) {
- for ( std::size_t channel = 0; channel < channels; ++channel ) {
- sampleBufFloat.push_back( buffers[channel][frame] );
- }
- }
- write_frames( sampleBufFloat.data(), frames );
- } else {
- write_frames( buffers, frames );
- }
- }
- void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
- if ( interleaved ) {
- sampleBufInt.clear();
- for ( std::size_t frame = 0; frame < frames; ++frame ) {
- for ( std::size_t channel = 0; channel < channels; ++channel ) {
- sampleBufInt.push_back( buffers[channel][frame] );
- }
- }
- write_frames( sampleBufInt.data(), frames );
- } else {
- write_frames( buffers, frames );
- }
- }
- bool unpause() override {
- check_portaudio_error( Pa_StartStream( stream ) );
- return true;
- }
- bool pause() override {
- check_portaudio_error( Pa_StopStream( stream ) );
- return true;
- }
- bool sleep( int ms ) override {
- Pa_Sleep( ms );
- return true;
- }
- };
- #define portaudio_stream_raii portaudio_stream_blocking_raii
- static std::string show_portaudio_devices( std::ostream & log ) {
- std::ostringstream devices;
- devices << " portaudio:" << std::endl;
- portaudio_raii portaudio( false, log );
- for ( PaDeviceIndex i = 0; i < Pa_GetDeviceCount(); ++i ) {
- if ( Pa_GetDeviceInfo( i ) && Pa_GetDeviceInfo( i )->maxOutputChannels > 0 ) {
- devices << " " << i << ": ";
- if ( Pa_GetHostApiInfo( Pa_GetDeviceInfo( i )->hostApi ) && Pa_GetHostApiInfo( Pa_GetDeviceInfo( i )->hostApi )->name ) {
- devices << Pa_GetHostApiInfo( Pa_GetDeviceInfo( i )->hostApi )->name;
- } else {
- devices << "Host API " << Pa_GetDeviceInfo( i )->hostApi;
- }
- if ( Pa_GetHostApiInfo( Pa_GetDeviceInfo( i )->hostApi ) ) {
- if ( i == Pa_GetHostApiInfo( Pa_GetDeviceInfo( i )->hostApi )->defaultOutputDevice ) {
- devices << " (default)";
- }
- }
- devices << " - ";
- if ( Pa_GetDeviceInfo( i )->name ) {
- devices << Pa_GetDeviceInfo( i )->name;
- } else {
- devices << "Device " << i;
- }
- devices << " (";
- devices << "high latency: " << Pa_GetDeviceInfo( i )->defaultHighOutputLatency;
- devices << ", ";
- devices << "low latency: " << Pa_GetDeviceInfo( i )->defaultLowOutputLatency;
- devices << ")";
- devices << std::endl;
- }
- }
- return devices.str();
- }
- } // namespace openmpt123
- #endif // MPT_WITH_PORTAUDIO
- #endif // OPENMPT123_PORTAUDIO_HPP
|