Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
169
Src/external_dependencies/openmpt-trunk/openmpt123/.clang-format
Normal file
169
Src/external_dependencies/openmpt-trunk/openmpt123/.clang-format
Normal 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
|
2544
Src/external_dependencies/openmpt-trunk/openmpt123/openmpt123.cpp
Normal file
2544
Src/external_dependencies/openmpt-trunk/openmpt123/openmpt123.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,2 @@
|
|||
[Name]
|
||||
openmpt123 - command line module music player based on libopenmpt
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue