winamp/Src/external_dependencies/openmpt-trunk/openmpt123/openmpt123_sdl2.hpp
2024-09-24 14:54:57 +02:00

227 lines
6.4 KiB
C++

/*
* openmpt123_sdl2.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_SDL2_HPP
#define OPENMPT123_SDL2_HPP
#include "openmpt123_config.hpp"
#include "openmpt123.hpp"
#if defined(MPT_WITH_SDL2)
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#endif // __clang__
#include <SDL.h>
#if defined(__clang__)
#pragma clang diagnostic pop
#endif // __clang__
#ifdef main
#undef main
#endif
#ifdef SDL_main
#undef SDL_main
#endif
#if (SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 4))
MPT_WARNING("Support for SDL2 < 2.0.4 has been deprecated and will be removed in a future openmpt123 version.")
#endif
namespace openmpt123 {
struct sdl2_exception : public exception {
private:
static std::string text_from_code( int code ) {
std::ostringstream s;
s << code;
return s.str();
}
public:
sdl2_exception( int code, const char * error ) : exception( text_from_code( code ) + " (" + ( error ? std::string(error) : std::string("NULL") ) + ")" ) { }
};
static void check_sdl2_error( int e ) {
if ( e < 0 ) {
throw sdl2_exception( e, SDL_GetError() );
}
}
class sdl2_raii {
public:
sdl2_raii( Uint32 flags ) {
check_sdl2_error( SDL_Init( flags ) );
}
~sdl2_raii() {
SDL_Quit();
}
};
class sdl2_stream_raii : public write_buffers_interface {
private:
std::ostream & log;
sdl2_raii sdl2;
int dev;
std::size_t channels;
bool use_float;
std::size_t sampleQueueMaxFrames;
std::vector<float> sampleBufFloat;
std::vector<std::int16_t> sampleBufInt;
protected:
std::uint32_t round_up_power2(std::uint32_t x)
{
std::uint32_t result = 1;
while ( result < x ) {
result *= 2;
}
return result;
}
public:
sdl2_stream_raii( commandlineflags & flags, std::ostream & log_ )
: log(log_)
, sdl2( SDL_INIT_NOPARACHUTE | SDL_INIT_TIMER | SDL_INIT_AUDIO )
, dev(-1)
, channels(flags.channels)
, use_float(flags.use_float)
, sampleQueueMaxFrames(0)
{
if ( flags.buffer == default_high ) {
flags.buffer = 160;
} else if ( flags.buffer == default_low ) {
flags.buffer = 80;
}
if ( flags.period == default_high ) {
flags.period = 20;
} else if ( flags.period == default_low ) {
flags.period = 10;
}
flags.apply_default_buffer_sizes();
SDL_AudioSpec audiospec;
std::memset( &audiospec, 0, sizeof( SDL_AudioSpec ) );
audiospec.freq = flags.samplerate;
audiospec.format = ( flags.use_float ? AUDIO_F32SYS : AUDIO_S16SYS );
audiospec.channels = flags.channels;
audiospec.silence = 0;
audiospec.samples = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) );
audiospec.size = audiospec.samples * audiospec.channels * ( flags.use_float ? sizeof( float ) : sizeof( std::int16_t ) );
audiospec.callback = NULL;
audiospec.userdata = NULL;
if ( flags.verbose ) {
log << "SDL2:" << std::endl;
log << " latency: " << ( audiospec.samples * 2.0 / flags.samplerate ) << " (2 * " << audiospec.samples << ")" << std::endl;
log << std::endl;
}
sampleQueueMaxFrames = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) );
SDL_AudioSpec audiospec_obtained;
std::memset( &audiospec_obtained, 0, sizeof( SDL_AudioSpec ) );
std::memcpy( &audiospec_obtained, &audiospec, sizeof( SDL_AudioSpec ) );
dev = SDL_OpenAudioDevice( NULL, 0, &audiospec, &audiospec_obtained, 0 );
if ( dev < 0 ) {
check_sdl2_error( dev );
} else if ( dev == 0 ) {
check_sdl2_error( -1 );
}
SDL_PauseAudioDevice( dev, 0 );
}
~sdl2_stream_raii() {
SDL_PauseAudioDevice( dev, 1 );
SDL_CloseAudioDevice( dev );
}
private:
std::size_t get_num_writeable_frames() {
std::size_t num_queued_frames = SDL_GetQueuedAudioSize( dev ) / ( use_float ? sizeof( float ) : sizeof( std::int16_t ) ) / channels;
if ( num_queued_frames > sampleQueueMaxFrames ) {
return 0;
}
return sampleQueueMaxFrames - num_queued_frames;
}
template<typename Tsample>
void write_frames( const Tsample * buffer, std::size_t frames ) {
while ( frames > 0 ) {
std::size_t chunk_frames = std::min( frames, get_num_writeable_frames() );
if ( chunk_frames > 0 ) {
check_sdl2_error( SDL_QueueAudio( dev, buffer, chunk_frames * channels * ( use_float ? sizeof( float ) : sizeof( std::int16_t ) ) ) );
frames -= chunk_frames;
buffer += chunk_frames * channels;
} else {
SDL_Delay( 1 );
}
}
}
public:
void write( const std::vector<float*> buffers, std::size_t frames ) override {
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 );
}
void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
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 );
}
bool pause() override {
SDL_PauseAudioDevice( dev, 1 );
return true;
}
bool unpause() override {
SDL_PauseAudioDevice( dev, 0 );
return true;
}
bool sleep( int ms ) override {
SDL_Delay( ms );
return true;
}
};
static std::string show_sdl2_devices( std::ostream & /* log */ ) {
std::ostringstream devices;
std::size_t device_index = 0;
devices << " SDL2:" << std::endl;
sdl2_raii sdl2( SDL_INIT_NOPARACHUTE | SDL_INIT_AUDIO );
for ( int driver = 0; driver < SDL_GetNumAudioDrivers(); ++driver ) {
const char * driver_name = SDL_GetAudioDriver( driver );
if ( !driver_name ) {
continue;
}
if ( std::string( driver_name ).empty() ) {
continue;
}
if ( SDL_AudioInit( driver_name ) < 0 ) {
continue;
}
for ( int device = 0; device < SDL_GetNumAudioDevices( 0 ); ++device ) {
const char * device_name = SDL_GetAudioDeviceName( device, 0 );
if ( !device_name ) {
continue;
}
if ( std::string( device_name ).empty() ) {
continue;
}
devices << " " << device_index << ": " << driver_name << " - " << device_name << std::endl;
device_index++;
}
SDL_AudioQuit();
}
return devices.str();
}
} // namespace openmpt123
#endif // MPT_WITH_SDL2
#endif // OPENMPT123_SDL2_HPP