Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* 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
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue