Initial community commit

This commit is contained in:
Jef 2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit fc06254474
16440 changed files with 4239995 additions and 2 deletions

View file

@ -0,0 +1,169 @@
# clang-format 14
Language: Cpp
Standard: c++20
AccessModifierOffset: -2 #?
AlignAfterOpenBracket: AlwaysBreak
AlignArrayOfStructures: Left
AlignConsecutiveAssignments: false
AlignConsecutiveBitFields: false
AlignConsecutiveDeclarations: false
AlignConsecutiveMacros: true
AlignEscapedNewlines: DontAlign
AlignOperands: AlignAfterOperator
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLambdasOnASingleLine: Inline
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros: []
BinPackArguments: true
BinPackParameters: false
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: true
AfterClass: false
AfterControlStatement: MultiLine
AfterEnum: false
AfterFunction: false
AfterNamespace: false
#AfterObjCDeclaration
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: true
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: false
SplitEmptyNamespace: true
#BreakAfterJavaFieldAnnotations
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BreakBeforeConceptDeclarations: true
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeComma
BreakStringLiterals: false
ColumnLimit: 0
CommentPragmas: '' #?
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 2 #?
ContinuationIndentWidth: 2 #?
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
EmptyLineAfterAccessModifier: Leave
EmptyLineBeforeAccessModifier: Leave
FixNamespaceComments: true
ForEachMacros: []
IfMacros: ['MPT_MAYBE_CONSTANT_IF']
IncludeBlocks: Preserve
IncludeCategories: [] #?
IncludeIsMainRegex: '' #?
IncludeIsMainSourceRegex: '' #?
IndentAccessModifiers: false
IndentCaseBlocks: true
IndentCaseLabels: true
IndentExternBlock: NoIndent
IndentGotoLabels: false
IndentPPDirectives: None
#IndentRequiresClause: true
#BeforeHash
IndentWidth: 2
IndentWrappedFunctionNames: true
InsertTrailingCommas: None
#JavaImportGroups
#JavaScriptQuotes
#JavaScriptWrapImports
KeepEmptyLinesAtTheStartOfBlocks: true
LambdaBodyIndentation: OuterScope
MacroBlockBegin: '' #?
MacroBlockEnd: '' #?
MaxEmptyLinesToKeep: 3
NamespaceIndentation: None
NamespaceMacros: [] #?
#ObjCBinPackProtocolList
#ObjCBlockIndentWidth
#ObjCBreakBeforeNestedBlockParam
#ObjCSpaceAfterProperty
#ObjCSpaceBeforeProtocolList
PackConstructorInitializers: Never
#PenaltyBreakAssignment
#PenaltyBreakBeforeFirstCallParameter
#PenaltyBreakComment
#PenaltyBreakFirstLessLess
#PenaltyBreakOpenParenthesis
#PenaltyBreakString
#PenaltyBreakTemplateDeclaration
#PenaltyExcessCharacter
#PenaltyIndentedWhitespace
#PenaltyReturnTypeOnItsOwnLine
PointerAlignment: Middle
PPIndentWidth: -1
#RawStringFormats
QualifierAlignment: Leave
#QualifierOrder: ['static', 'inline', 'constexpr', 'volatile', 'const', 'restrict', 'type']
ReferenceAlignment: Pointer
ReflowComments: false
RemoveBracesLLVM: false
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: false
#SortJavaStaticImport
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDeclarationName: false
AfterFunctionDefinitionName: false
AfterIfMacros: true
AfterOverloadedOperator: false
#AfterRequiresInClause: false
#AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInConditionalStatement: true
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: true
SpacesInSquareBrackets: false
StatementAttributeLikeMacros: []
StatementMacros: [ 'MPT_WARNING', 'MPT_TEST_GROUP_INLINE_IDENTIFIER', 'MPT_TEST_GROUP_INLINE', 'MPT_TEST_GROUP_STATIC' ] #?
TabWidth: 2
TypenameMacros: [] #?
UseCRLF: false
UseTab: ForContinuationAndIndentation
WhitespaceSensitiveMacros:
- MPT_PP_STRINGIFY

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
[Name]
openmpt123 - command line module music player based on libopenmpt

View file

@ -0,0 +1,726 @@
/*
* openmpt123.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_HPP
#define OPENMPT123_HPP
#include "openmpt123_config.hpp"
#include "mpt/base/compiletime_warning.hpp"
#include "mpt/base/floatingpoint.hpp"
#include "mpt/base/preprocessor.hpp"
#include "mpt/string_transcode/transcode.hpp"
#include <string>
namespace openmpt123 {
struct exception : public openmpt::exception {
exception( const std::string & text ) : openmpt::exception(text) { }
};
struct show_help_exception {
std::string message;
bool longhelp;
show_help_exception( const std::string & msg = "", bool longhelp_ = true ) : message(msg), longhelp(longhelp_) { }
};
struct args_error_exception {
args_error_exception() { }
};
struct show_help_keyboard_exception { };
#if defined(WIN32)
bool IsConsole( DWORD stdHandle );
#endif
bool IsTerminal( int fd );
struct field {
std::string key;
std::string val;
field( const std::string & key )
: key(key)
{
return;
}
};
class textout : public std::ostringstream {
public:
textout() {
return;
}
virtual ~textout() {
return;
}
protected:
std::string pop() {
std::string text = str();
str(std::string());
return text;
}
public:
virtual void writeout() = 0;
virtual void cursor_up( std::size_t lines ) {
static_cast<void>( lines );
}
};
class textout_dummy : public textout {
public:
textout_dummy() {
return;
}
virtual ~textout_dummy() {
return;
}
public:
void writeout() override {
static_cast<void>( pop() );
}
};
class textout_ostream : public textout {
private:
std::ostream & s;
#if defined(__DJGPP__)
mpt::common_encoding codepage;
#endif
public:
textout_ostream( std::ostream & s_ )
: s(s_)
#if defined(__DJGPP__)
, codepage(mpt::common_encoding::cp437)
#endif
{
#if defined(__DJGPP__)
codepage = mpt::djgpp_get_locale_encoding();
#endif
return;
}
virtual ~textout_ostream() {
writeout_impl();
}
private:
void writeout_impl() {
std::string text = pop();
if ( text.length() > 0 ) {
#if defined(__DJGPP__)
s << mpt::transcode<std::string>( codepage, mpt::common_encoding::utf8, text );
#elif defined(__EMSCRIPTEN__)
s << text;
#else
s << mpt::transcode<std::string>( mpt::logical_encoding::locale, mpt::common_encoding::utf8, text );
#endif
s.flush();
}
}
public:
void writeout() override {
writeout_impl();
}
void cursor_up( std::size_t lines ) override {
s.flush();
for ( std::size_t line = 0; line < lines; ++line ) {
*this << "\x1b[1A";
}
}
};
#if defined(WIN32)
class textout_ostream_console : public textout {
private:
#if defined(UNICODE)
std::wostream & s;
#else
std::ostream & s;
#endif
HANDLE handle;
bool console;
public:
#if defined(UNICODE)
textout_ostream_console( std::wostream & s_, DWORD stdHandle_ )
#else
textout_ostream_console( std::ostream & s_, DWORD stdHandle_ )
#endif
: s(s_)
, handle(GetStdHandle( stdHandle_ ))
, console(IsConsole( stdHandle_ ))
{
return;
}
virtual ~textout_ostream_console() {
writeout_impl();
}
private:
void writeout_impl() {
std::string text = pop();
if ( text.length() > 0 ) {
if ( console ) {
#if defined(UNICODE)
std::wstring wtext = mpt::transcode<std::wstring>( mpt::common_encoding::utf8, text );
WriteConsole( handle, wtext.data(), static_cast<DWORD>( wtext.size() ), NULL, NULL );
#else
std::string ltext = mpt::transcode<std::string>( mpt::logical_encoding::locale, mpt::common_encoding::utf8, text );
WriteConsole( handle, ltext.data(), static_cast<DWORD>( ltext.size() ), NULL, NULL );
#endif
} else {
#if defined(UNICODE)
s << mpt::transcode<std::wstring>( mpt::common_encoding::utf8, text );
#else
s << mpt::transcode<std::string>( mpt::logical_encoding::locale, mpt::common_encoding::utf8, text );
#endif
s.flush();
}
}
}
public:
void writeout() override {
writeout_impl();
}
void cursor_up( std::size_t lines ) override {
if ( console ) {
s.flush();
CONSOLE_SCREEN_BUFFER_INFO csbi;
ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) );
COORD coord_cursor = COORD();
if ( GetConsoleScreenBufferInfo( handle, &csbi ) != FALSE ) {
coord_cursor = csbi.dwCursorPosition;
coord_cursor.X = 1;
coord_cursor.Y -= static_cast<SHORT>( lines );
SetConsoleCursorPosition( handle, coord_cursor );
}
}
}
};
#endif // WIN32
static inline float mpt_round( float val ) {
if ( val >= 0.0f ) {
return std::floor( val + 0.5f );
} else {
return std::ceil( val - 0.5f );
}
}
static inline long mpt_lround( float val ) {
return static_cast< long >( mpt_round( val ) );
}
static inline std::string append_software_tag( std::string software ) {
std::string openmpt123 = std::string() + "openmpt123 " + OPENMPT123_VERSION_STRING + " (libopenmpt " + openmpt::string::get( "library_version" ) + ", OpenMPT " + openmpt::string::get( "core_version" ) + ")";
if ( software.empty() ) {
software = openmpt123;
} else {
software += " (via " + openmpt123 + ")";
}
return software;
}
static inline std::string get_encoder_tag() {
return std::string() + "openmpt123 " + OPENMPT123_VERSION_STRING + " (libopenmpt " + openmpt::string::get( "library_version" ) + ", OpenMPT " + openmpt::string::get( "core_version" ) + ")";
}
static inline std::string get_extension( std::string filename ) {
if ( filename.find_last_of( "." ) != std::string::npos ) {
return filename.substr( filename.find_last_of( "." ) + 1 );
}
return "";
}
enum class Mode {
None,
Probe,
Info,
UI,
Batch,
Render
};
static inline std::string mode_to_string( Mode mode ) {
switch ( mode ) {
case Mode::None: return "none"; break;
case Mode::Probe: return "probe"; break;
case Mode::Info: return "info"; break;
case Mode::UI: return "ui"; break;
case Mode::Batch: return "batch"; break;
case Mode::Render: return "render"; break;
}
return "";
}
static const std::int32_t default_low = -2;
static const std::int32_t default_high = -1;
struct commandlineflags {
Mode mode;
bool canUI;
std::int32_t ui_redraw_interval;
bool canProgress;
std::string driver;
std::string device;
std::int32_t buffer;
std::int32_t period;
std::int32_t samplerate;
std::int32_t channels;
std::int32_t gain;
std::int32_t separation;
std::int32_t filtertaps;
std::int32_t ramping; // ramping strength : -1:default 0:off 1 2 3 4 5 // roughly milliseconds
std::int32_t tempo;
std::int32_t pitch;
std::int32_t dither;
std::int32_t repeatcount;
std::int32_t subsong;
std::map<std::string, std::string> ctls;
double seek_target;
double end_time;
bool quiet;
bool verbose;
int terminal_width;
int terminal_height;
bool show_details;
bool show_message;
bool show_ui;
bool show_progress;
bool show_meters;
bool show_channel_meters;
bool show_pattern;
bool use_float;
bool use_stdout;
bool randomize;
bool shuffle;
bool restart;
std::size_t playlist_index;
std::vector<std::string> filenames;
std::string output_filename;
std::string output_extension;
bool force_overwrite;
bool paused;
std::string warnings;
void apply_default_buffer_sizes() {
if ( ui_redraw_interval == default_high ) {
ui_redraw_interval = 50;
} else if ( ui_redraw_interval == default_low ) {
ui_redraw_interval = 10;
}
if ( buffer == default_high ) {
buffer = 250;
} else if ( buffer == default_low ) {
buffer = 50;
}
if ( period == default_high ) {
period = 50;
} else if ( period == default_low ) {
period = 10;
}
}
commandlineflags() {
mode = Mode::UI;
ui_redraw_interval = default_high;
driver = "";
device = "";
buffer = default_high;
period = default_high;
#if defined(__DJGPP__)
samplerate = 44100;
channels = 2;
use_float = false;
#else
samplerate = 48000;
channels = 2;
use_float = mpt::float_traits<float>::is_hard && mpt::float_traits<float>::is_ieee754_binary;
#endif
gain = 0;
separation = 100;
filtertaps = 8;
ramping = -1;
tempo = 0;
pitch = 0;
dither = 1;
repeatcount = 0;
subsong = -1;
seek_target = 0.0;
end_time = 0.0;
quiet = false;
verbose = false;
#if defined(__DJGPP__)
terminal_width = 80;
terminal_height = 25;
#else
terminal_width = 72;
terminal_height = 23;
#endif
#if defined(WIN32)
terminal_width = 72;
terminal_height = 23;
HANDLE hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE );
if ( ( hStdOutput != NULL ) && ( hStdOutput != INVALID_HANDLE_VALUE ) ) {
CONSOLE_SCREEN_BUFFER_INFO csbi;
ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) );
if ( GetConsoleScreenBufferInfo( hStdOutput, &csbi ) != FALSE ) {
terminal_width = std::min( static_cast<int>( 1 + csbi.srWindow.Right - csbi.srWindow.Left ), static_cast<int>( csbi.dwSize.X ) );
terminal_height = std::min( static_cast<int>( 1 + csbi.srWindow.Bottom - csbi.srWindow.Top ), static_cast<int>( csbi.dwSize.Y ) );
}
}
#else // WIN32
if ( isatty( STDERR_FILENO ) ) {
const char * env_columns = std::getenv( "COLUMNS" );
if ( env_columns ) {
std::istringstream istr( env_columns );
int tmp = 0;
istr >> tmp;
if ( tmp > 0 ) {
terminal_width = tmp;
}
}
const char * env_rows = std::getenv( "ROWS" );
if ( env_rows ) {
std::istringstream istr( env_rows );
int tmp = 0;
istr >> tmp;
if ( tmp > 0 ) {
terminal_height = tmp;
}
}
#if defined(TIOCGWINSZ)
struct winsize ts;
if ( ioctl( STDERR_FILENO, TIOCGWINSZ, &ts ) >= 0 ) {
terminal_width = ts.ws_col;
terminal_height = ts.ws_row;
}
#elif defined(TIOCGSIZE)
struct ttysize ts;
if ( ioctl( STDERR_FILENO, TIOCGSIZE, &ts ) >= 0 ) {
terminal_width = ts.ts_cols;
terminal_height = ts.ts_rows;
}
#endif
}
#endif
show_details = true;
show_message = false;
#if defined(WIN32)
canUI = IsTerminal( 0 ) ? true : false;
canProgress = IsTerminal( 2 ) ? true : false;
#else // !WIN32
canUI = isatty( STDIN_FILENO ) ? true : false;
canProgress = isatty( STDERR_FILENO ) ? true : false;
#endif // WIN32
show_ui = canUI;
show_progress = canProgress;
show_meters = canUI && canProgress;
show_channel_meters = false;
show_pattern = false;
use_stdout = false;
randomize = false;
shuffle = false;
restart = false;
playlist_index = 0;
output_extension = "auto";
force_overwrite = false;
paused = false;
}
void check_and_sanitize() {
if ( filenames.size() == 0 ) {
throw args_error_exception();
}
if ( use_stdout && ( device != commandlineflags().device || !output_filename.empty() ) ) {
throw args_error_exception();
}
if ( !output_filename.empty() && ( device != commandlineflags().device || use_stdout ) ) {
throw args_error_exception();
}
for ( const auto & filename : filenames ) {
if ( filename == "-" ) {
canUI = false;
}
}
show_ui = canUI;
if ( mode == Mode::None ) {
if ( canUI ) {
mode = Mode::UI;
} else {
mode = Mode::Batch;
}
}
if ( mode == Mode::UI && !canUI ) {
throw args_error_exception();
}
if ( show_progress && !canProgress ) {
throw args_error_exception();
}
switch ( mode ) {
case Mode::None:
throw args_error_exception();
break;
case Mode::Probe:
show_ui = false;
show_progress = false;
show_meters = false;
show_channel_meters = false;
show_pattern = false;
break;
case Mode::Info:
show_ui = false;
show_progress = false;
show_meters = false;
show_channel_meters = false;
show_pattern = false;
break;
case Mode::UI:
break;
case Mode::Batch:
show_meters = false;
show_channel_meters = false;
show_pattern = false;
break;
case Mode::Render:
show_meters = false;
show_channel_meters = false;
show_pattern = false;
show_ui = false;
break;
}
if ( quiet ) {
verbose = false;
show_ui = false;
show_details = false;
show_progress = false;
show_channel_meters = false;
}
if ( verbose ) {
show_details = true;
}
if ( channels != 1 && channels != 2 && channels != 4 ) {
channels = commandlineflags().channels;
}
if ( samplerate < 0 ) {
samplerate = commandlineflags().samplerate;
}
if ( output_extension == "auto" ) {
output_extension = "";
}
if ( mode != Mode::Render && !output_extension.empty() ) {
throw args_error_exception();
}
if ( mode == Mode::Render && !output_filename.empty() ) {
throw args_error_exception();
}
if ( mode != Mode::Render && !output_filename.empty() ) {
output_extension = get_extension( output_filename );
}
if ( output_extension.empty() ) {
output_extension = "wav";
}
}
};
template < typename Tsample > Tsample convert_sample_to( float val );
template < > float convert_sample_to( float val ) {
return val;
}
template < > std::int16_t convert_sample_to( float val ) {
std::int32_t tmp = static_cast<std::int32_t>( val * 32768.0f );
tmp = std::min( tmp, std::int32_t( 32767 ) );
tmp = std::max( tmp, std::int32_t( -32768 ) );
return static_cast<std::int16_t>( tmp );
}
class write_buffers_interface {
protected:
virtual ~write_buffers_interface() {
return;
}
public:
virtual void write_metadata( std::map<std::string,std::string> metadata ) {
(void)metadata;
return;
}
virtual void write_updated_metadata( std::map<std::string,std::string> metadata ) {
(void)metadata;
return;
}
virtual void write( const std::vector<float*> buffers, std::size_t frames ) = 0;
virtual void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) = 0;
virtual bool pause() {
return false;
}
virtual bool unpause() {
return false;
}
virtual bool sleep( int /*ms*/ ) {
return false;
}
virtual bool is_dummy() const {
return false;
}
};
class write_buffers_polling_wrapper : public write_buffers_interface {
protected:
std::size_t channels;
std::size_t sampleQueueMaxFrames;
std::deque<float> sampleQueue;
protected:
virtual ~write_buffers_polling_wrapper() {
return;
}
protected:
write_buffers_polling_wrapper( const commandlineflags & flags )
: channels(flags.channels)
, sampleQueueMaxFrames(0)
{
return;
}
void set_queue_size_frames( std::size_t frames ) {
sampleQueueMaxFrames = frames;
}
template < typename Tsample >
Tsample pop_queue() {
float val = 0.0f;
if ( !sampleQueue.empty() ) {
val = sampleQueue.front();
sampleQueue.pop_front();
}
return convert_sample_to<Tsample>( val );
}
public:
void write( const std::vector<float*> buffers, std::size_t frames ) override {
for ( std::size_t frame = 0; frame < frames; ++frame ) {
for ( std::size_t channel = 0; channel < channels; ++channel ) {
sampleQueue.push_back( buffers[channel][frame] );
}
while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) {
while ( !forward_queue() ) {
sleep( 1 );
}
}
}
}
void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
for ( std::size_t frame = 0; frame < frames; ++frame ) {
for ( std::size_t channel = 0; channel < channels; ++channel ) {
sampleQueue.push_back( buffers[channel][frame] * (1.0f/32768.0f) );
}
while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) {
while ( !forward_queue() ) {
sleep( 1 );
}
}
}
}
virtual bool forward_queue() = 0;
bool sleep( int ms ) override = 0;
};
class write_buffers_polling_wrapper_int : public write_buffers_interface {
protected:
std::size_t channels;
std::size_t sampleQueueMaxFrames;
std::deque<std::int16_t> sampleQueue;
protected:
virtual ~write_buffers_polling_wrapper_int() {
return;
}
protected:
write_buffers_polling_wrapper_int( const commandlineflags & flags )
: channels(flags.channels)
, sampleQueueMaxFrames(0)
{
return;
}
void set_queue_size_frames( std::size_t frames ) {
sampleQueueMaxFrames = frames;
}
std::int16_t pop_queue() {
std::int16_t val = 0;
if ( !sampleQueue.empty() ) {
val = sampleQueue.front();
sampleQueue.pop_front();
}
return val;
}
public:
void write( const std::vector<float*> buffers, std::size_t frames ) override {
for ( std::size_t frame = 0; frame < frames; ++frame ) {
for ( std::size_t channel = 0; channel < channels; ++channel ) {
sampleQueue.push_back( convert_sample_to<std::int16_t>( buffers[channel][frame] ) );
}
while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) {
while ( !forward_queue() ) {
sleep( 1 );
}
}
}
}
void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
for ( std::size_t frame = 0; frame < frames; ++frame ) {
for ( std::size_t channel = 0; channel < channels; ++channel ) {
sampleQueue.push_back( buffers[channel][frame] );
}
while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) {
while ( !forward_queue() ) {
sleep( 1 );
}
}
}
}
virtual bool forward_queue() = 0;
bool sleep( int ms ) override = 0;
};
class void_audio_stream : public write_buffers_interface {
public:
virtual ~void_audio_stream() {
return;
}
public:
void write( const std::vector<float*> buffers, std::size_t frames ) override {
(void)buffers;
(void)frames;
}
void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
(void)buffers;
(void)frames;
}
bool is_dummy() const override {
return true;
}
};
class file_audio_stream_base : public write_buffers_interface {
protected:
file_audio_stream_base() {
return;
}
public:
void write_metadata( std::map<std::string,std::string> metadata ) override {
(void)metadata;
return;
}
void write_updated_metadata( std::map<std::string,std::string> metadata ) override {
(void)metadata;
return;
}
void write( const std::vector<float*> buffers, std::size_t frames ) override = 0;
void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override = 0;
virtual ~file_audio_stream_base() {
return;
}
};
} // namespace openmpt123
#endif // OPENMPT123_HPP

View file

@ -0,0 +1,185 @@
/*
* openmpt123_allegro42.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_ALLEGRO42_HPP
#define OPENMPT123_ALLEGRO42_HPP
#include "openmpt123_config.hpp"
#include "openmpt123.hpp"
#if defined(MPT_WITH_ALLEGRO42)
#if defined(__GNUC__) && !defined(__clang__) && !defined(_MSC_VER)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#pragma GCC diagnostic ignored "-Wfloat-conversion"
#endif
#include <allegro.h>
#if defined(__GNUC__) && !defined(__clang__) && !defined(_MSC_VER)
#pragma GCC diagnostic pop
#endif
namespace openmpt123 {
struct allegro42_exception : public exception {
static std::string error_to_string() {
try {
return std::string( allegro_error );
} catch ( const std::bad_alloc & ) {
return std::string();
}
}
allegro42_exception()
: exception( error_to_string() )
{
}
};
class allegro42_raii {
public:
allegro42_raii() {
if ( allegro_init() != 0 ) {
throw allegro42_exception();
}
}
~allegro42_raii() {
allegro_exit();
}
};
class allegro42_sound_raii {
public:
allegro42_sound_raii() {
if ( install_sound( DIGI_AUTODETECT, MIDI_NONE, NULL ) != 0 ) {
throw allegro42_exception();
}
if ( digi_card == DIGI_NONE ) {
remove_sound();
throw exception( "no audio device found" );
}
}
~allegro42_sound_raii() {
remove_sound();
}
};
class allegro42_stream_raii : public write_buffers_polling_wrapper_int {
private:
allegro42_raii allegro;
allegro42_sound_raii allegro_sound;
AUDIOSTREAM * stream;
std::size_t bits;
std::size_t channels;
std::uint32_t period_frames;
private:
std::uint32_t round_up_power2(std::uint32_t x)
{
std::uint32_t result = 1;
while ( result < x ) {
result *= 2;
}
return result;
}
public:
allegro42_stream_raii( commandlineflags & flags, std::ostream & log )
: write_buffers_polling_wrapper_int(flags)
, stream(NULL)
, bits(16)
, channels(flags.channels)
, period_frames(1024)
{
if ( flags.use_float ) {
throw exception( "floating point is unsupported" );
}
if ( ( flags.channels != 1 ) && ( flags.channels != 2 ) ) {
throw exception( "only mono or stereo supported" );
}
if ( flags.buffer == default_high ) {
flags.buffer = 1024 * 2 * 1000 / flags.samplerate;
} else if ( flags.buffer == default_low ) {
flags.buffer = 512 * 2 * 1000 / flags.samplerate;
}
if ( flags.period == default_high ) {
flags.period = 1024 / 2 * 1000 / flags.samplerate;
} else if ( flags.period == default_low ) {
flags.period = 512 / 2 * 1000 / flags.samplerate;
}
flags.apply_default_buffer_sizes();
period_frames = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) );
set_queue_size_frames( period_frames );
if ( flags.verbose ) {
log << "Allegro-4.2:" << std::endl;
log << " allegro samplerate: " << get_mixer_frequency() << std::endl;
log << " latency: " << flags.buffer << std::endl;
log << " period: " << flags.period << std::endl;
log << " frames per buffer: " << period_frames << std::endl;
log << " ui redraw: " << flags.ui_redraw_interval << std::endl;
}
stream = play_audio_stream( period_frames, 16, ( flags.channels > 1 ) ? TRUE : FALSE, flags.samplerate, 255, 128 );
if ( !stream ) {
bits = 8;
stream = play_audio_stream( period_frames, 8, ( flags.channels > 1 ) ? TRUE : FALSE, flags.samplerate, 255, 128 );
if ( !stream ) {
throw allegro42_exception();
}
}
}
~allegro42_stream_raii() {
if ( stream ) {
stop_audio_stream( stream );
stream = NULL;
}
}
public:
bool forward_queue() override {
void * p = get_audio_stream_buffer( stream );
if ( !p ) {
return false;
}
for ( std::size_t frame = 0; frame < period_frames; ++frame ) {
for ( std::size_t channel = 0; channel < channels; ++channel ) {
std::int16_t sample = pop_queue();
if ( bits == 8 ) {
std::uint8_t u8sample = ( static_cast<std::uint16_t>( sample ) + 0x8000u ) >> 8;
std::memcpy( reinterpret_cast<unsigned char *>( p ) + ( ( ( frame * channels ) + channel ) * sizeof( std::uint8_t ) ), &u8sample, sizeof( std::uint8_t ) );
} else {
std::uint16_t u16sample = static_cast<std::uint16_t>( sample ) + 0x8000u;
std::memcpy( reinterpret_cast<unsigned char *>( p ) + ( ( ( frame * channels ) + channel ) * sizeof( std::uint16_t ) ), &u16sample, sizeof( std::uint16_t ) );
}
}
}
free_audio_stream_buffer( stream );
return true;
}
bool unpause() override {
voice_start( stream->voice );
return true;
}
bool pause() override {
voice_stop( stream->voice );
return true;
}
bool sleep( int ms ) override {
rest( ms );
return true;
}
};
static std::string show_allegro42_devices( std::ostream & /* log */ ) {
std::ostringstream devices;
devices << " allegro42:" << std::endl;
devices << " " << "0" << ": Default Device" << std::endl;
return devices.str();
}
} // namespace openmpt123
#endif // MPT_WITH_ALLEGRO42
#endif // OPENMPT123_ALLEGRO42_HPP

View file

@ -0,0 +1,64 @@
/*
* openmpt123_config.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_CONFIG_HPP
#define OPENMPT123_CONFIG_HPP
#define MPT_INLINE_NS mpt_openmpt123
#if defined(_WIN32)
#ifndef WIN32
#define WIN32
#endif
#endif // _WIN32
#if defined(WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifdef _UNICODE
#ifndef UNICODE
#define UNICODE
#endif
#endif
#ifdef UNICODE
#ifndef _UNICODE
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#endif
#define _UNICODE
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif
#endif
#endif // WIN32
#if defined(WIN32)
#define MPT_WITH_MMIO
#endif // WIN32
#if defined(MPT_BUILD_MSVC)
#define MPT_WITH_FLAC
#define MPT_WITH_PORTAUDIO
#if defined(MPT_BUILD_MSVC_STATIC)
#define FLAC__NO_DLL
#endif
#endif // MPT_BUILD_MSVC
#define OPENMPT123_VERSION_STRING OPENMPT_API_VERSION_STRING
#endif // OPENMPT123_CONFIG_HPP

View file

@ -0,0 +1,142 @@
/*
* openmpt123_flac.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_FLAC_HPP
#define OPENMPT123_FLAC_HPP
#include "openmpt123_config.hpp"
#include "openmpt123.hpp"
#if defined(MPT_WITH_FLAC)
#if defined(_MSC_VER) && defined(__clang__) && defined(__c2__)
#include <sys/types.h>
#if __STDC__
typedef _off_t off_t;
#endif
#endif
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#endif
#include <FLAC/metadata.h>
#include <FLAC/format.h>
#include <FLAC/stream_encoder.h>
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
namespace openmpt123 {
class flac_stream_raii : public file_audio_stream_base {
private:
commandlineflags flags;
std::string filename;
bool called_init;
std::vector< std::pair< std::string, std::string > > tags;
FLAC__StreamMetadata * flac_metadata[1];
FLAC__StreamEncoder * encoder;
std::vector<FLAC__int32> interleaved_buffer;
void add_vorbiscomment_field( FLAC__StreamMetadata * vorbiscomment, const std::string & field, const std::string & value ) {
if ( !value.empty() ) {
FLAC__StreamMetadata_VorbisComment_Entry entry;
FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair( &entry, field.c_str(), value.c_str() );
FLAC__metadata_object_vorbiscomment_append_comment( vorbiscomment, entry, false );
}
}
public:
flac_stream_raii( const std::string & filename_, const commandlineflags & flags_, std::ostream & /*log*/ ) : flags(flags_), filename(filename_), called_init(false), encoder(0) {
flac_metadata[0] = 0;
encoder = FLAC__stream_encoder_new();
if ( !encoder ) {
throw exception( "error creating flac encoder" );
}
FLAC__stream_encoder_set_channels( encoder, flags.channels );
FLAC__stream_encoder_set_bits_per_sample( encoder, flags.use_float ? 24 : 16 );
FLAC__stream_encoder_set_sample_rate( encoder, flags.samplerate );
FLAC__stream_encoder_set_compression_level( encoder, 8 );
}
~flac_stream_raii() {
if ( encoder ) {
FLAC__stream_encoder_finish( encoder );
FLAC__stream_encoder_delete( encoder );
encoder = 0;
}
if ( flac_metadata[0] ) {
FLAC__metadata_object_delete( flac_metadata[0] );
flac_metadata[0] = 0;
}
}
void write_metadata( std::map<std::string,std::string> metadata ) override {
if ( called_init ) {
return;
}
tags.clear();
tags.push_back( std::make_pair( "TITLE", metadata[ "title" ] ) );
tags.push_back( std::make_pair( "ARTIST", metadata[ "artist" ] ) );
tags.push_back( std::make_pair( "DATE", metadata[ "date" ] ) );
tags.push_back( std::make_pair( "COMMENT", metadata[ "message" ] ) );
if ( !metadata[ "type" ].empty() && !metadata[ "tracker" ].empty() ) {
tags.push_back( std::make_pair( "SOURCEMEDIA", std::string() + "'" + metadata[ "type" ] + "' tracked music file, made with '" + metadata[ "tracker" ] + "', rendered with '" + get_encoder_tag() + "'" ) );
} else if ( !metadata[ "type_long" ].empty() ) {
tags.push_back( std::make_pair( "SOURCEMEDIA", std::string() + "'" + metadata[ "type" ] + "' tracked music file, rendered with '" + get_encoder_tag() + "'" ) );
} else if ( !metadata[ "tracker" ].empty() ) {
tags.push_back( std::make_pair( "SOURCEMEDIA", std::string() + "tracked music file, made with '" + metadata[ "tracker" ] + "', rendered with '" + get_encoder_tag() + "'" ) );
} else {
tags.push_back( std::make_pair( "SOURCEMEDIA", std::string() + "tracked music file, rendered with '" + get_encoder_tag() + "'" ) );
}
tags.push_back( std::make_pair( "ENCODER", get_encoder_tag() ) );
flac_metadata[0] = FLAC__metadata_object_new( FLAC__METADATA_TYPE_VORBIS_COMMENT );
for ( std::vector< std::pair< std::string, std::string > >::iterator tag = tags.begin(); tag != tags.end(); ++tag ) {
add_vorbiscomment_field( flac_metadata[0], tag->first, tag->second );
}
FLAC__stream_encoder_set_metadata( encoder, flac_metadata, 1 );
}
void write( const std::vector<float*> buffers, std::size_t frames ) override {
if ( !called_init ) {
FLAC__stream_encoder_init_file( encoder, filename.c_str(), NULL, 0 );
called_init = true;
}
interleaved_buffer.clear();
for ( std::size_t frame = 0; frame < frames; frame++ ) {
for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
float in = buffers[channel][frame];
if ( in <= -1.0f ) {
in = -1.0f;
} else if ( in >= 1.0f ) {
in = 1.0f;
}
FLAC__int32 out = mpt_lround( in * (1<<23) );
out = std::max( 0 - (1<<23), out );
out = std::min( out, 0 + (1<<23) - 1 );
interleaved_buffer.push_back( out );
}
}
FLAC__stream_encoder_process_interleaved( encoder, interleaved_buffer.data(), static_cast<unsigned int>( frames ) );
}
void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
if ( !called_init ) {
FLAC__stream_encoder_init_file( encoder, filename.c_str(), NULL, 0 );
called_init = true;
}
interleaved_buffer.clear();
for ( std::size_t frame = 0; frame < frames; frame++ ) {
for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
interleaved_buffer.push_back( buffers[channel][frame] );
}
}
FLAC__stream_encoder_process_interleaved( encoder, interleaved_buffer.data(), static_cast<unsigned int>( frames ) );
}
};
} // namespace openmpt123
#endif // MPT_WITH_FLAC
#endif // OPENMPT123_FLAC_HPP

View file

@ -0,0 +1,133 @@
/*
* openmpt123_mmio.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_MMIO_HPP
#define OPENMPT123_MMIO_HPP
#include "openmpt123_config.hpp"
#include "openmpt123.hpp"
#if defined(MPT_WITH_MMIO)
namespace openmpt123 {
class mmio_stream_raii : public file_audio_stream_base {
private:
std::ostream & log;
commandlineflags flags;
WAVEFORMATEX waveformatex;
HMMIO mmio;
MMCKINFO WAVE_chunk;
MMCKINFO fmt__chunk;
MMCKINFO data_chunk;
MMIOINFO data_info;
private:
void CHECKED( HRESULT err ) {
if ( err != 0 ) {
throw exception( "error writing wave file" );
}
}
void UNCHECKED( HRESULT err ) {
if ( err != 0 ) {
log << "error writing wave file" << std::endl;
}
}
public:
mmio_stream_raii( const std::string & filename, const commandlineflags & flags_, std::ostream & log_ ) : log(log_), flags(flags_), mmio(NULL) {
ZeroMemory( &waveformatex, sizeof( WAVEFORMATEX ) );
waveformatex.cbSize = 0;
waveformatex.wFormatTag = flags.use_float ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
waveformatex.nChannels = static_cast<WORD>( flags.channels );
waveformatex.nSamplesPerSec = flags.samplerate;
waveformatex.wBitsPerSample = flags.use_float ? 32 : 16;
waveformatex.nBlockAlign = static_cast<WORD>( flags.channels * ( waveformatex.wBitsPerSample / 8 ) );
waveformatex.nAvgBytesPerSec = waveformatex.nSamplesPerSec * waveformatex.nBlockAlign;
#if defined(WIN32) && defined(UNICODE)
wchar_t * tmp = _wcsdup( mpt::transcode<std::wstring>( mpt::common_encoding::utf8, filename ).c_str() );
mmio = mmioOpen( tmp, NULL, MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE );
free( tmp );
tmp = 0;
#else
char * tmp = strdup( filename.c_str() );
mmio = mmioOpen( tmp, NULL, MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE );
free( tmp );
tmp = 0;
#endif
ZeroMemory( &WAVE_chunk, sizeof( MMCKINFO ) );
WAVE_chunk.fccType = mmioFOURCC('W', 'A', 'V', 'E');
CHECKED(mmioCreateChunk( mmio, &WAVE_chunk, MMIO_CREATERIFF ));
ZeroMemory( &fmt__chunk, sizeof( MMCKINFO ) );
fmt__chunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
fmt__chunk.cksize = sizeof( WAVEFORMATEX );
CHECKED(mmioCreateChunk( mmio, &fmt__chunk, 0 ));
mmioWrite( mmio, (const char*)&waveformatex, sizeof( WAVEFORMATEX ) );
CHECKED(mmioAscend( mmio, &fmt__chunk, 0 ));
ZeroMemory( &data_chunk, sizeof( MMCKINFO ) );
data_chunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
data_chunk.cksize = 0;
CHECKED(mmioCreateChunk( mmio, &data_chunk, 0 ));
ZeroMemory( &data_info, sizeof( MMIOINFO ) );
CHECKED(mmioGetInfo( mmio, &data_info, 0 ));
}
void write( const std::vector<float*> buffers, std::size_t frames ) override {
for ( std::size_t frame = 0; frame < frames; frame++ ) {
for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
if ( data_info.pchEndWrite - data_info.pchNext < static_cast<long>( sizeof( float ) ) ) {
data_info.dwFlags |= MMIO_DIRTY;
CHECKED(mmioAdvance( mmio, &data_info, MMIO_WRITE ));
}
std::memcpy( data_info.pchNext, &( buffers[channel][frame] ), sizeof( float ) );
data_info.pchNext += sizeof( float );
}
}
}
void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
for ( std::size_t frame = 0; frame < frames; frame++ ) {
for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
if ( data_info.pchEndWrite - data_info.pchNext < static_cast<long>( sizeof( std::int16_t ) ) ) {
data_info.dwFlags |= MMIO_DIRTY;
CHECKED(mmioAdvance( mmio, &data_info, MMIO_WRITE ));
}
std::memcpy( data_info.pchNext, &( buffers[channel][frame] ), sizeof( std::int16_t ) );
data_info.pchNext += sizeof( std::int16_t );
}
}
}
~mmio_stream_raii() {
data_info.dwFlags |= MMIO_DIRTY;
UNCHECKED(mmioSetInfo( mmio, &data_info, 0 ));
UNCHECKED(mmioAscend( mmio, &data_chunk, 0 ));
UNCHECKED(mmioAscend( mmio, &WAVE_chunk, 0 ));
UNCHECKED(mmioClose( mmio, 0 ));
mmio = NULL;
}
};
} // namespace openmpt123
#endif // MPT_WITH_MMIO
#endif // OPENMPT123_MMIO_HPP

View file

@ -0,0 +1,288 @@
/*
* 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

View file

@ -0,0 +1,166 @@
/*
* openmpt123_pulseaudio.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_PULSEAUDIO_HPP
#define OPENMPT123_PULSEAUDIO_HPP
#include "openmpt123_config.hpp"
#include "openmpt123.hpp"
#if defined(MPT_WITH_PULSEAUDIO)
#include <pulse/pulseaudio.h>
#include <pulse/simple.h>
namespace openmpt123 {
struct pulseaudio_exception : public exception {
static std::string error_to_string( int error ) {
try {
if ( error == 0 ) {
return std::string();
}
std::ostringstream e;
const char * str = pa_strerror( error );
if ( !str ) {
e << "error=" << error;
return e.str();
}
if ( std::strlen(str) == 0 ) {
e << "error=" << error;
return e.str();
}
e << str << " (error=" << error << ")";
return e.str();
} catch ( const std::bad_alloc & ) {
return std::string();
}
}
pulseaudio_exception( int error ) : exception( error_to_string( error ) ) { }
};
class pulseaudio_stream_raii : public write_buffers_interface {
private:
pa_simple * stream;
std::size_t channels;
std::size_t sampleSize;
std::vector<float> sampleBufFloat;
std::vector<std::int16_t> sampleBufInt;
public:
pulseaudio_stream_raii( commandlineflags & flags, std::ostream & /* log */ )
: stream(NULL)
, channels(flags.channels)
, sampleSize(flags.use_float ? sizeof( float ) : sizeof( std::int16_t ))
{
int error = 0;
pa_sample_spec ss;
std::memset( &ss, 0, sizeof( pa_sample_spec ) );
ss.format = ( flags.use_float ? PA_SAMPLE_FLOAT32 : PA_SAMPLE_S16NE );
ss.rate = flags.samplerate;
ss.channels = flags.channels;
pa_buffer_attr ba;
std::memset( &ba, 0, sizeof( pa_buffer_attr ) );
bool use_ba = false;
if ( flags.buffer != default_high && flags.buffer != default_low ) {
use_ba = true;
ba.maxlength = channels * sampleSize * ( flags.buffer * flags.samplerate / 1000 );
} else {
ba.maxlength = static_cast<std::uint32_t>(-1);
}
if ( flags.period != default_high && flags.period != default_low ) {
use_ba = true;
ba.minreq = channels * sampleSize * ( flags.period * flags.samplerate / 1000 );
if ( ba.maxlength != static_cast<std::uint32_t>(-1) ) {
ba.tlength = ba.maxlength - ba.minreq;
ba.prebuf = ba.tlength;
} else {
ba.tlength = static_cast<std::uint32_t>(-1);
ba.prebuf = static_cast<std::uint32_t>(-1);
}
} else {
ba.minreq = static_cast<std::uint32_t>(-1);
ba.tlength = static_cast<std::uint32_t>(-1);
ba.prebuf = static_cast<std::uint32_t>(-1);
}
ba.fragsize = 0;
flags.apply_default_buffer_sizes();
sampleBufFloat.resize( channels * ( flags.ui_redraw_interval * flags.samplerate / 1000 ) );
sampleBufInt.resize( channels * ( flags.ui_redraw_interval * flags.samplerate / 1000 ) );
stream = pa_simple_new( NULL, "openmpt123", PA_STREAM_PLAYBACK, NULL, "openmpt123", &ss, NULL, ( use_ba ? &ba : NULL ), &error );
if ( !stream ) {
throw pulseaudio_exception( error );
}
}
~pulseaudio_stream_raii() {
int error = 0;
if ( stream ) {
error = 0;
if ( pa_simple_drain( stream, &error ) < 0 ) {
// throw pulseaudio_exception( error );
}
pa_simple_free( stream );
stream = NULL;
}
}
private:
template<typename Tsample>
void write_frames( const Tsample * buffer, std::size_t frames ) {
int error = 0;
if ( pa_simple_write( stream, buffer, frames * channels * sampleSize, &error ) < 0 ) {
throw pulseaudio_exception( error );
}
}
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 unpause() override {
return true;
}
bool pause() override {
int error = 0;
error = 0;
if ( pa_simple_drain( stream, &error ) < 0 ) {
throw pulseaudio_exception( error );
}
return true;
}
bool sleep( int ms ) override {
pa_msleep( ms );
return true;
}
};
static std::string show_pulseaudio_devices( std::ostream & /* log */ ) {
std::ostringstream devices;
devices << " pulseaudio:" << std::endl;
devices << " " << "0" << ": Default Device" << std::endl;
return devices.str();
}
} // namespace openmpt123
#endif // MPT_WITH_PULSEAUDIO
#endif // OPENMPT123_PULSEAUDIO_HPP

View file

@ -0,0 +1,58 @@
/*
* openmpt123_raw.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_RAW_HPP
#define OPENMPT123_RAW_HPP
#include "openmpt123_config.hpp"
#include "openmpt123.hpp"
#include <fstream>
namespace openmpt123 {
class raw_stream_raii : public file_audio_stream_base {
private:
commandlineflags flags;
std::ofstream file;
std::vector<float> interleaved_float_buffer;
std::vector<std::int16_t> interleaved_int_buffer;
public:
raw_stream_raii( const std::string & filename, const commandlineflags & flags_, std::ostream & /*log*/ ) : flags(flags_), file(filename.c_str(), std::ios::binary) {
return;
}
~raw_stream_raii() {
return;
}
void write_metadata( std::map<std::string,std::string> /* metadata */ ) override {
return;
}
void write( const std::vector<float*> buffers, std::size_t frames ) override {
interleaved_float_buffer.clear();
for ( std::size_t frame = 0; frame < frames; frame++ ) {
for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
interleaved_float_buffer.push_back( buffers[channel][frame] );
}
}
file.write( reinterpret_cast<const char *>( interleaved_float_buffer.data() ), frames * buffers.size() * sizeof( float ) );
}
void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
interleaved_int_buffer.clear();
for ( std::size_t frame = 0; frame < frames; frame++ ) {
for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
interleaved_int_buffer.push_back( buffers[channel][frame] );
}
}
file.write( reinterpret_cast<const char *>( interleaved_int_buffer.data() ), frames * buffers.size() * sizeof( std::int16_t ) );
}
};
} // namespace openmpt123
#endif // OPENMPT123_RAW_HPP

View file

@ -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

View file

@ -0,0 +1,208 @@
/*
* openmpt123_sndfile.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_SNDFILE_HPP
#define OPENMPT123_SNDFILE_HPP
#include "openmpt123_config.hpp"
#include "openmpt123.hpp"
#if defined(MPT_WITH_SNDFILE)
#include <sndfile.h>
namespace openmpt123 {
class sndfile_stream_raii : public file_audio_stream_base {
private:
commandlineflags flags;
std::ostream & log;
SNDFILE * sndfile;
std::vector<float> interleaved_float_buffer;
std::vector<std::int16_t> interleaved_int_buffer;
private:
enum match_mode_enum {
match_print,
match_recurse,
match_exact,
match_better,
match_any
};
std::string match_mode_to_string( match_mode_enum match_mode ) {
switch ( match_mode ) {
case match_print : return "print" ; break;
case match_recurse: return "recurse"; break;
case match_exact : return "exact" ; break;
case match_better : return "better" ; break;
case match_any : return "any" ; break;
}
return "";
}
int matched_result( const SF_FORMAT_INFO & format_info, const SF_FORMAT_INFO & subformat_info, match_mode_enum match_mode ) {
if ( flags.verbose ) {
log << "sndfile: using format '"
<< format_info.name << " (" << format_info.extension << ")" << " / " << subformat_info.name
<< "', "
<< "match: " << match_mode_to_string( match_mode )
<< std::endl;
}
return ( format_info.format & SF_FORMAT_TYPEMASK ) | subformat_info.format;
}
int find_format( const std::string & extension, match_mode_enum match_mode ) {
if ( match_mode == match_recurse ) {
int result = 0;
result = find_format( extension, match_exact );
if ( result ) {
return result;
}
result = find_format( extension, match_better );
if ( result ) {
return result;
}
result = find_format( extension, match_any );
if ( result ) {
return result;
}
if ( result ) {
return result;
}
return 0;
}
int format = 0;
int major_count;
sf_command( 0, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof( int ) );
for ( int m = 0; m < major_count; m++ ) {
SF_FORMAT_INFO format_info;
format_info.format = m;
sf_command( 0, SFC_GET_FORMAT_MAJOR, &format_info, sizeof( SF_FORMAT_INFO ) );
format = format_info.format;
int subtype_count;
sf_command( 0, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count, sizeof( int ) );
for ( int s = 0; s < subtype_count; s++ ) {
SF_FORMAT_INFO subformat_info;
subformat_info.format = s;
sf_command( 0, SFC_GET_FORMAT_SUBTYPE, &subformat_info, sizeof( SF_FORMAT_INFO ) );
format = ( format & SF_FORMAT_TYPEMASK ) | subformat_info.format;
SF_INFO sfinfo;
std::memset( &sfinfo, 0, sizeof( SF_INFO ) );
sfinfo.channels = flags.channels;
sfinfo.format = format;
if ( sf_format_check( &sfinfo ) ) {
switch ( match_mode ) {
case match_print:
log << "sndfile: "
<< ( format_info.name ? format_info.name : "" ) << " (" << ( format_info.extension ? format_info.extension : "" ) << ")"
<< " / "
<< ( subformat_info.name ? subformat_info.name : "" )
<< " ["
<< std::setbase(16) << std::setw(8) << std::setfill('0') << format_info.format
<< "|"
<< std::setbase(16) << std::setw(8) << std::setfill('0') << subformat_info.format
<< "]"
<< std::endl;
break;
case match_recurse:
break;
case match_exact:
if ( extension == format_info.extension ) {
if ( flags.use_float && ( subformat_info.format == SF_FORMAT_FLOAT ) ) {
return matched_result( format_info, subformat_info, match_mode );
} else if ( !flags.use_float && ( subformat_info.format == SF_FORMAT_PCM_16 ) ) {
return matched_result( format_info, subformat_info, match_mode );
}
}
break;
case match_better:
if ( extension == format_info.extension ) {
if ( flags.use_float && ( subformat_info.format == SF_FORMAT_FLOAT || subformat_info.format == SF_FORMAT_DOUBLE ) ) {
return matched_result( format_info, subformat_info, match_mode );
} else if ( !flags.use_float && ( subformat_info.format & ( subformat_info.format == SF_FORMAT_PCM_16 || subformat_info.format == SF_FORMAT_PCM_24 || subformat_info.format == SF_FORMAT_PCM_32 ) ) ) {
return matched_result( format_info, subformat_info, match_mode );
}
}
break;
case match_any:
if ( extension == format_info.extension ) {
return matched_result( format_info, subformat_info, match_mode );
}
break;
}
}
}
}
return 0;
}
void write_metadata_field( int str_type, const std::string & str ) {
if ( !str.empty() ) {
sf_set_string( sndfile, str_type, str.c_str() );
}
}
public:
sndfile_stream_raii( const std::string & filename, const commandlineflags & flags_, std::ostream & log_ ) : flags(flags_), log(log_), sndfile(0) {
if ( flags.verbose ) {
find_format( "", match_print );
log << std::endl;
}
int format = find_format( flags.output_extension, match_recurse );
if ( !format ) {
throw exception( "unknown file type" );
}
SF_INFO info;
std::memset( &info, 0, sizeof( SF_INFO ) );
info.samplerate = flags.samplerate;
info.channels = flags.channels;
info.format = format;
sndfile = sf_open( filename.c_str(), SFM_WRITE, &info );
}
~sndfile_stream_raii() {
sf_close( sndfile );
sndfile = 0;
}
void write_metadata( std::map<std::string,std::string> metadata ) override {
write_metadata_field( SF_STR_TITLE, metadata[ "title" ] );
write_metadata_field( SF_STR_ARTIST, metadata[ "artist" ] );
write_metadata_field( SF_STR_DATE, metadata[ "date" ] );
write_metadata_field( SF_STR_COMMENT, metadata[ "message" ] );
write_metadata_field( SF_STR_SOFTWARE, append_software_tag( metadata[ "tracker" ] ) );
}
void write( const std::vector<float*> buffers, std::size_t frames ) override {
interleaved_float_buffer.clear();
for ( std::size_t frame = 0; frame < frames; frame++ ) {
for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
interleaved_float_buffer.push_back( buffers[channel][frame] );
}
}
sf_writef_float( sndfile, interleaved_float_buffer.data(), frames );
}
void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
interleaved_int_buffer.clear();
for ( std::size_t frame = 0; frame < frames; frame++ ) {
for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
interleaved_int_buffer.push_back( buffers[channel][frame] );
}
}
sf_writef_short( sndfile, interleaved_int_buffer.data(), frames );
}
};
} // namespace openmpt123
#endif // MPT_WITH_SNDFILE
#endif // OPENMPT123_SNDFILE_HPP

View file

@ -0,0 +1,49 @@
/*
* openmpt123_stdout.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_STDOUT_HPP
#define OPENMPT123_STDOUT_HPP
#include "openmpt123_config.hpp"
#include "openmpt123.hpp"
namespace openmpt123 {
class stdout_stream_raii : public write_buffers_interface {
private:
std::vector<float> interleaved_float_buffer;
std::vector<std::int16_t> interleaved_int_buffer;
public:
stdout_stream_raii() {
return;
}
public:
void write( const std::vector<float*> buffers, std::size_t frames ) override {
interleaved_float_buffer.clear();
for ( std::size_t frame = 0; frame < frames; frame++ ) {
for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
interleaved_float_buffer.push_back( buffers[channel][frame] );
}
}
std::cout.write( reinterpret_cast<const char *>( interleaved_float_buffer.data() ), interleaved_float_buffer.size() * sizeof( float ) );
}
void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
interleaved_int_buffer.clear();
for ( std::size_t frame = 0; frame < frames; frame++ ) {
for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
interleaved_int_buffer.push_back( buffers[channel][frame] );
}
}
std::cout.write( reinterpret_cast<const char *>( interleaved_int_buffer.data() ), interleaved_int_buffer.size() * sizeof( std::int16_t ) );
}
};
} // namespace openmpt123
#endif // OPENMPT123_STDOUT_HPP

View file

@ -0,0 +1,198 @@
/*
* openmpt123_waveout.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_WAVEOUT_HPP
#define OPENMPT123_WAVEOUT_HPP
#include "openmpt123_config.hpp"
#include "openmpt123.hpp"
#if defined(WIN32)
namespace openmpt123 {
struct waveout_exception : public exception {
waveout_exception() : exception( "waveout" ) { }
};
class waveout_stream_raii : public write_buffers_interface {
private:
HWAVEOUT waveout;
std::size_t num_channels;
std::size_t num_chunks;
std::size_t frames_per_chunk;
std::size_t bytes_per_chunk;
std::vector<WAVEHDR> waveheaders;
std::vector<std::vector<char> > wavebuffers;
std::deque<char> byte_queue;
public:
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 = static_cast<WORD>( 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;
std::istringstream device_string( flags.device );
int device = -1;
device_string >> device;
waveOutOpen( &waveout, device == -1 ? WAVE_MAPPER : 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 ) );
}
}
~waveout_stream_raii() {
if ( waveout ) {
write_or_wait( true );
drain();
waveOutReset( waveout );
for ( std::size_t i = 0; i < num_chunks; ++i ) {
waveheaders[i].dwBufferLength = static_cast<DWORD>( wavebuffers[i].size() );
waveOutUnprepareHeader( waveout, &waveheaders[i], sizeof( WAVEHDR ) );
}
wavebuffers.clear();
waveheaders.clear();
frames_per_chunk = 0;
num_chunks = 0;
waveOutClose( waveout );
waveout = NULL;
}
}
private:
void drain() {
std::size_t empty_chunks = 0;
while ( empty_chunks != num_chunks ) {
empty_chunks = 0;
for ( std::size_t chunk = 0; chunk < num_chunks; ++chunk ) {
DWORD flags = waveheaders[chunk].dwFlags;
if ( !(flags & WHDR_INQUEUE) || (flags & WHDR_DONE) ) {
empty_chunks++;
}
}
if ( empty_chunks != num_chunks ) {
Sleep( 1 );
}
}
}
std::size_t wait_for_empty_chunk() {
while ( true ) {
for ( std::size_t chunk = 0; chunk < num_chunks; ++chunk ) {
DWORD flags = waveheaders[chunk].dwFlags;
if ( !(flags & WHDR_INQUEUE) || (flags & WHDR_DONE) ) {
return chunk;
}
}
Sleep( 1 );
}
}
void write_chunk() {
std::size_t chunk = wait_for_empty_chunk();
std::size_t chunk_bytes = std::min( byte_queue.size(), bytes_per_chunk );
waveheaders[chunk].dwBufferLength = static_cast<DWORD>( chunk_bytes );
for ( std::size_t byte = 0; byte < chunk_bytes; ++byte ) {
wavebuffers[chunk][byte] = byte_queue.front();
byte_queue.pop_front();
}
waveOutWrite( waveout, &waveheaders[chunk], sizeof( WAVEHDR ) );
}
void write_or_wait( bool flush = false ) {
while ( byte_queue.size() >= bytes_per_chunk ) {
write_chunk();
}
if ( flush && !byte_queue.empty() ) {
write_chunk();
}
}
template < typename Tsample >
void write_buffers( const std::vector<Tsample*> buffers, std::size_t frames ) {
for ( std::size_t frame = 0; frame < frames; ++frame ) {
for ( std::size_t channel = 0; channel < buffers.size(); ++channel ) {
Tsample val = buffers[channel][frame];
char buf[ sizeof( Tsample ) ];
std::memcpy( buf, &val, sizeof( Tsample ) );
std::copy( buf, buf + sizeof( Tsample ), std::back_inserter( byte_queue ) );
}
}
write_or_wait();
}
public:
void write( const std::vector<float*> buffers, std::size_t frames ) override {
write_buffers( buffers, frames );
}
void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
write_buffers( buffers, frames );
}
bool pause() override {
waveOutPause( waveout );
return true;
}
bool unpause() override {
waveOutRestart( waveout );
return true;
}
bool sleep( int ms ) override {
Sleep( ms );
return true;
}
};
static std::string show_waveout_devices( std::ostream & /*log*/ ) {
std::ostringstream devices;
devices << " waveout:" << std::endl;
for ( UINT i = 0; i < waveOutGetNumDevs(); ++i ) {
devices << " " << i << ": ";
WAVEOUTCAPSW caps;
ZeroMemory( &caps, sizeof( caps ) );
waveOutGetDevCapsW( i, &caps, sizeof( caps ) );
devices << mpt::transcode<std::string>( mpt::common_encoding::utf8, caps.szPname );
devices << std::endl;
}
return devices.str();
}
} // namespace openmpt123
#endif // WIN32
#endif // OPENMPT123_WAVEOUT_HPP