1 /*
2  * openmpt123.hpp
3  * --------------
4  * Purpose: libopenmpt command line player
5  * Notes  : (currently none)
6  * Authors: OpenMPT Devs
7  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
8  */
9 
10 #ifndef OPENMPT123_HPP
11 #define OPENMPT123_HPP
12 
13 #include "openmpt123_config.hpp"
14 
15 #include "mpt/base/compiletime_warning.hpp"
16 #include "mpt/base/floatingpoint.hpp"
17 #include "mpt/base/preprocessor.hpp"
18 #include "mpt/string_transcode/transcode.hpp"
19 
20 #include <string>
21 
22 namespace openmpt123 {
23 
24 struct exception : public openmpt::exception {
exceptionopenmpt123::exception25 	exception( const std::string & text ) : openmpt::exception(text) { }
26 };
27 
28 struct show_help_exception {
29 	std::string message;
30 	bool longhelp;
show_help_exceptionopenmpt123::show_help_exception31 	show_help_exception( const std::string & msg = "", bool longhelp_ = true ) : message(msg), longhelp(longhelp_) { }
32 };
33 
34 struct args_error_exception {
args_error_exceptionopenmpt123::args_error_exception35 	args_error_exception() { }
36 };
37 
38 struct show_help_keyboard_exception { };
39 
40 #if defined(WIN32)
41 bool IsConsole( DWORD stdHandle );
42 #endif
43 bool IsTerminal( int fd );
44 
45 
46 
47 struct field {
48 	std::string key;
49 	std::string val;
fieldopenmpt123::field50 	field( const std::string & key )
51 		: key(key)
52 	{
53 		return;
54 	}
55 };
56 
57 class textout : public std::ostringstream {
58 public:
textout()59 	textout() {
60 		return;
61 	}
~textout()62 	virtual ~textout() {
63 		return;
64 	}
65 protected:
pop()66 	std::string pop() {
67 		std::string text = str();
68 		str(std::string());
69 		return text;
70 	}
71 public:
72 	virtual void writeout() = 0;
cursor_up(std::size_t lines)73 	virtual void cursor_up( std::size_t lines ) {
74 		static_cast<void>( lines );
75 	}
76 };
77 
78 class textout_dummy : public textout {
79 public:
textout_dummy()80 	textout_dummy() {
81 		return;
82 	}
~textout_dummy()83 	virtual ~textout_dummy() {
84 		return;
85 	}
86 public:
writeout()87 	void writeout() override {
88 		static_cast<void>( pop() );
89 	}
90 };
91 
92 class textout_ostream : public textout {
93 private:
94 	std::ostream & s;
95 #if defined(__DJGPP__)
96 	mpt::common_encoding codepage;
97 #endif
98 public:
textout_ostream(std::ostream & s_)99 	textout_ostream( std::ostream & s_ )
100 		: s(s_)
101 #if defined(__DJGPP__)
102 		, codepage(mpt::common_encoding::cp437)
103 #endif
104 	{
105 		#if defined(__DJGPP__)
106 			codepage = mpt::djgpp_get_locale_encoding();
107 		#endif
108 		return;
109 	}
~textout_ostream()110 	virtual ~textout_ostream() {
111 		writeout_impl();
112 	}
113 private:
writeout_impl()114 	void writeout_impl() {
115 		std::string text = pop();
116 		if ( text.length() > 0 ) {
117 			#if defined(__DJGPP__)
118 				s << mpt::transcode<std::string>( codepage, mpt::common_encoding::utf8, text );
119 			#elif defined(__EMSCRIPTEN__)
120 				s << text;
121 			#else
122 				s << mpt::transcode<std::string>( mpt::logical_encoding::locale, mpt::common_encoding::utf8, text );
123 		#endif
124 			s.flush();
125 		}
126 	}
127 public:
writeout()128 	void writeout() override {
129 		writeout_impl();
130 	}
cursor_up(std::size_t lines)131 	void cursor_up( std::size_t lines ) override {
132 		s.flush();
133 		for ( std::size_t line = 0; line < lines; ++line ) {
134 			*this << "\x1b[1A";
135 		}
136 	}
137 };
138 
139 #if defined(WIN32)
140 
141 class textout_ostream_console : public textout {
142 private:
143 #if defined(UNICODE)
144 	std::wostream & s;
145 #else
146 	std::ostream & s;
147 #endif
148 	HANDLE handle;
149 	bool console;
150 public:
151 #if defined(UNICODE)
textout_ostream_console(std::wostream & s_,DWORD stdHandle_)152 	textout_ostream_console( std::wostream & s_, DWORD stdHandle_ )
153 #else
154 	textout_ostream_console( std::ostream & s_, DWORD stdHandle_ )
155 #endif
156 		: s(s_)
157 		, handle(GetStdHandle( stdHandle_ ))
158 		, console(IsConsole( stdHandle_ ))
159 	{
160 		return;
161 	}
~textout_ostream_console()162 	virtual ~textout_ostream_console() {
163 		writeout_impl();
164 	}
165 private:
writeout_impl()166 	void writeout_impl() {
167 		std::string text = pop();
168 		if ( text.length() > 0 ) {
169 			if ( console ) {
170 				#if defined(UNICODE)
171 					std::wstring wtext = mpt::transcode<std::wstring>( mpt::common_encoding::utf8, text );
172 					WriteConsole( handle, wtext.data(), static_cast<DWORD>( wtext.size() ), NULL, NULL );
173 				#else
174 					std::string ltext = mpt::transcode<std::string>( mpt::logical_encoding::locale, mpt::common_encoding::utf8, text );
175 					WriteConsole( handle, ltext.data(), static_cast<DWORD>( ltext.size() ), NULL, NULL );
176 				#endif
177 			} else {
178 				#if defined(UNICODE)
179 					s << mpt::transcode<std::wstring>( mpt::common_encoding::utf8, text );
180 				#else
181 					s << mpt::transcode<std::string>( mpt::logical_encoding::locale, mpt::common_encoding::utf8, text );
182 				#endif
183 				s.flush();
184 			}
185 		}
186 	}
187 public:
writeout()188 	void writeout() override {
189 		writeout_impl();
190 	}
cursor_up(std::size_t lines)191 	void cursor_up( std::size_t lines ) override {
192 		if ( console ) {
193 			s.flush();
194 			CONSOLE_SCREEN_BUFFER_INFO csbi;
195 			ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) );
196 			COORD coord_cursor = COORD();
197 			if ( GetConsoleScreenBufferInfo( handle, &csbi ) != FALSE ) {
198 				coord_cursor = csbi.dwCursorPosition;
199 				coord_cursor.X = 1;
200 				coord_cursor.Y -= static_cast<SHORT>( lines );
201 				SetConsoleCursorPosition( handle, coord_cursor );
202 			}
203 		}
204 	}
205 };
206 
207 #endif // WIN32
208 
mpt_round(float val)209 static inline float mpt_round( float val ) {
210 	if ( val >= 0.0f ) {
211 		return std::floor( val + 0.5f );
212 	} else {
213 		return std::ceil( val - 0.5f );
214 	}
215 }
216 
mpt_lround(float val)217 static inline long mpt_lround( float val ) {
218 	return static_cast< long >( mpt_round( val ) );
219 }
220 
append_software_tag(std::string software)221 static inline std::string append_software_tag( std::string software ) {
222 	std::string openmpt123 = std::string() + "openmpt123 " + OPENMPT123_VERSION_STRING + " (libopenmpt " + openmpt::string::get( "library_version" ) + ", OpenMPT " + openmpt::string::get( "core_version" ) + ")";
223 	if ( software.empty() ) {
224 		software = openmpt123;
225 	} else {
226 		software += " (via " + openmpt123 + ")";
227 	}
228 	return software;
229 }
230 
get_encoder_tag()231 static inline std::string get_encoder_tag() {
232 	return std::string() + "openmpt123 " + OPENMPT123_VERSION_STRING + " (libopenmpt " + openmpt::string::get( "library_version" ) + ", OpenMPT " + openmpt::string::get( "core_version" ) + ")";
233 }
234 
get_extension(std::string filename)235 static inline std::string get_extension( std::string filename ) {
236 	if ( filename.find_last_of( "." ) != std::string::npos ) {
237 		return filename.substr( filename.find_last_of( "." ) + 1 );
238 	}
239 	return "";
240 }
241 
242 enum class Mode {
243 	None,
244 	Probe,
245 	Info,
246 	UI,
247 	Batch,
248 	Render
249 };
250 
mode_to_string(Mode mode)251 static inline std::string mode_to_string( Mode mode ) {
252 	switch ( mode ) {
253 		case Mode::None:   return "none"; break;
254 		case Mode::Probe:  return "probe"; break;
255 		case Mode::Info:   return "info"; break;
256 		case Mode::UI:     return "ui"; break;
257 		case Mode::Batch:  return "batch"; break;
258 		case Mode::Render: return "render"; break;
259 	}
260 	return "";
261 }
262 
263 static const std::int32_t default_low = -2;
264 static const std::int32_t default_high = -1;
265 
266 struct commandlineflags {
267 	Mode mode;
268 	bool canUI;
269 	std::int32_t ui_redraw_interval;
270 	bool canProgress;
271 	std::string driver;
272 	std::string device;
273 	std::int32_t buffer;
274 	std::int32_t period;
275 	std::int32_t samplerate;
276 	std::int32_t channels;
277 	std::int32_t gain;
278 	std::int32_t separation;
279 	std::int32_t filtertaps;
280 	std::int32_t ramping; // ramping strength : -1:default 0:off 1 2 3 4 5 // roughly milliseconds
281 	std::int32_t tempo;
282 	std::int32_t pitch;
283 	std::int32_t dither;
284 	std::int32_t repeatcount;
285 	std::int32_t subsong;
286 	std::map<std::string, std::string> ctls;
287 	double seek_target;
288 	double end_time;
289 	bool quiet;
290 	bool verbose;
291 	int terminal_width;
292 	int terminal_height;
293 	bool show_details;
294 	bool show_message;
295 	bool show_ui;
296 	bool show_progress;
297 	bool show_meters;
298 	bool show_channel_meters;
299 	bool show_pattern;
300 	bool use_float;
301 	bool use_stdout;
302 	bool randomize;
303 	bool shuffle;
304 	bool restart;
305 	std::size_t playlist_index;
306 	std::vector<std::string> filenames;
307 	std::string output_filename;
308 	std::string output_extension;
309 	bool force_overwrite;
310 	bool paused;
311 	std::string warnings;
apply_default_buffer_sizesopenmpt123::commandlineflags312 	void apply_default_buffer_sizes() {
313 		if ( ui_redraw_interval == default_high ) {
314 			ui_redraw_interval = 50;
315 		} else if ( ui_redraw_interval == default_low ) {
316 			ui_redraw_interval = 10;
317 		}
318 		if ( buffer == default_high ) {
319 			buffer = 250;
320 		} else if ( buffer == default_low ) {
321 			buffer = 50;
322 		}
323 		if ( period == default_high ) {
324 			period = 50;
325 		} else if ( period == default_low ) {
326 			period = 10;
327 		}
328 	}
commandlineflagsopenmpt123::commandlineflags329 	commandlineflags() {
330 		mode = Mode::UI;
331 		ui_redraw_interval = default_high;
332 		driver = "";
333 		device = "";
334 		buffer = default_high;
335 		period = default_high;
336 #if defined(__DJGPP__)
337 		samplerate = 44100;
338 		channels = 2;
339 		use_float = false;
340 #else
341 		samplerate = 48000;
342 		channels = 2;
343 		use_float = mpt::float_traits<float>::is_hard && mpt::float_traits<float>::is_ieee754_binary;
344 #endif
345 		gain = 0;
346 		separation = 100;
347 		filtertaps = 8;
348 		ramping = -1;
349 		tempo = 0;
350 		pitch = 0;
351 		dither = 1;
352 		repeatcount = 0;
353 		subsong = -1;
354 		seek_target = 0.0;
355 		end_time = 0.0;
356 		quiet = false;
357 		verbose = false;
358 #if defined(__DJGPP__)
359 		terminal_width = 80;
360 		terminal_height = 25;
361 #else
362 		terminal_width = 72;
363 		terminal_height = 23;
364 #endif
365 #if defined(WIN32)
366 		terminal_width = 72;
367 		terminal_height = 23;
368 		HANDLE hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE );
369 		if ( ( hStdOutput != NULL ) && ( hStdOutput != INVALID_HANDLE_VALUE ) ) {
370 			CONSOLE_SCREEN_BUFFER_INFO csbi;
371 			ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) );
372 			if ( GetConsoleScreenBufferInfo( hStdOutput, &csbi ) != FALSE ) {
373 				terminal_width = std::min( static_cast<int>( 1 + csbi.srWindow.Right - csbi.srWindow.Left ), static_cast<int>( csbi.dwSize.X ) );
374 				terminal_height = std::min( static_cast<int>( 1 + csbi.srWindow.Bottom - csbi.srWindow.Top ), static_cast<int>( csbi.dwSize.Y ) );
375 			}
376 		}
377 #else // WIN32
378 		if ( isatty( STDERR_FILENO ) ) {
379 			if ( std::getenv( "COLUMNS" ) ) {
380 				std::istringstream istr( std::getenv( "COLUMNS" ) );
381 				int tmp = 0;
382 				istr >> tmp;
383 				if ( tmp > 0 ) {
384 					terminal_width = tmp;
385 				}
386 			}
387 			if ( std::getenv( "ROWS" ) ) {
388 				std::istringstream istr( std::getenv( "ROWS" ) );
389 				int tmp = 0;
390 				istr >> tmp;
391 				if ( tmp > 0 ) {
392 					terminal_height = tmp;
393 				}
394 			}
395 			#if defined(TIOCGWINSZ)
396 				struct winsize ts;
397 				if ( ioctl( STDERR_FILENO, TIOCGWINSZ, &ts ) >= 0 ) {
398 					terminal_width = ts.ws_col;
399 					terminal_height = ts.ws_row;
400 				}
401 			#elif defined(TIOCGSIZE)
402 				struct ttysize ts;
403 				if ( ioctl( STDERR_FILENO, TIOCGSIZE, &ts ) >= 0 ) {
404 					terminal_width = ts.ts_cols;
405 					terminal_height = ts.ts_rows;
406 				}
407 			#endif
408 		}
409 #endif
410 		show_details = true;
411 		show_message = false;
412 #if defined(WIN32)
413 		canUI = IsTerminal( 0 ) ? true : false;
414 		canProgress = IsTerminal( 2 ) ? true : false;
415 #else // !WIN32
416 		canUI = isatty( STDIN_FILENO ) ? true : false;
417 		canProgress = isatty( STDERR_FILENO ) ? true : false;
418 #endif // WIN32
419 		show_ui = canUI;
420 		show_progress = canProgress;
421 		show_meters = canUI && canProgress;
422 		show_channel_meters = false;
423 		show_pattern = false;
424 		use_stdout = false;
425 		randomize = false;
426 		shuffle = false;
427 		restart = false;
428 		playlist_index = 0;
429 		output_extension = "auto";
430 		force_overwrite = false;
431 		paused = false;
432 	}
check_and_sanitizeopenmpt123::commandlineflags433 	void check_and_sanitize() {
434 		if ( filenames.size() == 0 ) {
435 			throw args_error_exception();
436 		}
437 		if ( use_stdout && ( device != commandlineflags().device || !output_filename.empty() ) ) {
438 			throw args_error_exception();
439 		}
440 		if ( !output_filename.empty() && ( device != commandlineflags().device || use_stdout ) ) {
441 			throw args_error_exception();
442 		}
443 		for ( const auto & filename : filenames ) {
444 			if ( filename == "-" ) {
445 				canUI = false;
446 			}
447 		}
448 		show_ui = canUI;
449 		if ( mode == Mode::None ) {
450 			if ( canUI ) {
451 				mode = Mode::UI;
452 			} else {
453 				mode = Mode::Batch;
454 			}
455 		}
456 		if ( mode == Mode::UI && !canUI ) {
457 			throw args_error_exception();
458 		}
459 		if ( show_progress && !canProgress ) {
460 			throw args_error_exception();
461 		}
462 		switch ( mode ) {
463 			case Mode::None:
464 				throw args_error_exception();
465 			break;
466 			case Mode::Probe:
467 				show_ui = false;
468 				show_progress = false;
469 				show_meters = false;
470 				show_channel_meters = false;
471 				show_pattern = false;
472 			break;
473 			case Mode::Info:
474 				show_ui = false;
475 				show_progress = false;
476 				show_meters = false;
477 				show_channel_meters = false;
478 				show_pattern = false;
479 			break;
480 			case Mode::UI:
481 			break;
482 			case Mode::Batch:
483 				show_meters = false;
484 				show_channel_meters = false;
485 				show_pattern = false;
486 			break;
487 			case Mode::Render:
488 				show_meters = false;
489 				show_channel_meters = false;
490 				show_pattern = false;
491 				show_ui = false;
492 			break;
493 		}
494 		if ( quiet ) {
495 			verbose = false;
496 			show_ui = false;
497 			show_details = false;
498 			show_progress = false;
499 			show_channel_meters = false;
500 		}
501 		if ( verbose ) {
502 			show_details = true;
503 		}
504 		if ( channels != 1 && channels != 2 && channels != 4 ) {
505 			channels = commandlineflags().channels;
506 		}
507 		if ( samplerate < 0 ) {
508 			samplerate = commandlineflags().samplerate;
509 		}
510 		if ( output_extension == "auto" ) {
511 			output_extension = "";
512 		}
513 		if ( mode != Mode::Render && !output_extension.empty() ) {
514 			throw args_error_exception();
515 		}
516 		if ( mode == Mode::Render && !output_filename.empty() ) {
517 			throw args_error_exception();
518 		}
519 		if ( mode != Mode::Render && !output_filename.empty() ) {
520 			output_extension = get_extension( output_filename );
521 		}
522 		if ( output_extension.empty() ) {
523 			output_extension = "wav";
524 		}
525 	}
526 };
527 
528 template < typename Tsample > Tsample convert_sample_to( float val );
convert_sample_to(float val)529 template < > float convert_sample_to( float val ) {
530 	return val;
531 }
convert_sample_to(float val)532 template < > std::int16_t convert_sample_to( float val ) {
533 	std::int32_t tmp = static_cast<std::int32_t>( val * 32768.0f );
534 	tmp = std::min( tmp, std::int32_t( 32767 ) );
535 	tmp = std::max( tmp, std::int32_t( -32768 ) );
536 	return static_cast<std::int16_t>( tmp );
537 }
538 
539 class write_buffers_interface {
540 protected:
~write_buffers_interface()541 	virtual ~write_buffers_interface() {
542 		return;
543 	}
544 public:
write_metadata(std::map<std::string,std::string> metadata)545 	virtual void write_metadata( std::map<std::string,std::string> metadata ) {
546 		(void)metadata;
547 		return;
548 	}
write_updated_metadata(std::map<std::string,std::string> metadata)549 	virtual void write_updated_metadata( std::map<std::string,std::string> metadata ) {
550 		(void)metadata;
551 		return;
552 	}
553 	virtual void write( const std::vector<float*> buffers, std::size_t frames ) = 0;
554 	virtual void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) = 0;
pause()555 	virtual bool pause() {
556 		return false;
557 	}
unpause()558 	virtual bool unpause() {
559 		return false;
560 	}
sleep(int)561 	virtual bool sleep( int /*ms*/ ) {
562 		return false;
563 	}
is_dummy() const564 	virtual bool is_dummy() const {
565 		return false;
566 	}
567 };
568 
569 class write_buffers_polling_wrapper : public write_buffers_interface {
570 protected:
571 	std::size_t channels;
572 	std::size_t sampleQueueMaxFrames;
573 	std::deque<float> sampleQueue;
574 protected:
~write_buffers_polling_wrapper()575 	virtual ~write_buffers_polling_wrapper() {
576 		return;
577 	}
578 protected:
write_buffers_polling_wrapper(const commandlineflags & flags)579 	write_buffers_polling_wrapper( const commandlineflags & flags )
580 		: channels(flags.channels)
581 		, sampleQueueMaxFrames(0)
582 	{
583 		return;
584 	}
set_queue_size_frames(std::size_t frames)585 	void set_queue_size_frames( std::size_t frames ) {
586 		sampleQueueMaxFrames = frames;
587 	}
588 	template < typename Tsample >
pop_queue()589 	Tsample pop_queue() {
590 		float val = 0.0f;
591 		if ( !sampleQueue.empty() ) {
592 			val = sampleQueue.front();
593 			sampleQueue.pop_front();
594 		}
595 		return convert_sample_to<Tsample>( val );
596 	}
597 public:
write(const std::vector<float * > buffers,std::size_t frames)598 	void write( const std::vector<float*> buffers, std::size_t frames ) override {
599 		for ( std::size_t frame = 0; frame < frames; ++frame ) {
600 			for ( std::size_t channel = 0; channel < channels; ++channel ) {
601 				sampleQueue.push_back( buffers[channel][frame] );
602 			}
603 			while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) {
604 				while ( !forward_queue() ) {
605 					sleep( 1 );
606 				}
607 			}
608 		}
609 	}
write(const std::vector<std::int16_t * > buffers,std::size_t frames)610 	void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
611 		for ( std::size_t frame = 0; frame < frames; ++frame ) {
612 			for ( std::size_t channel = 0; channel < channels; ++channel ) {
613 				sampleQueue.push_back( buffers[channel][frame] * (1.0f/32768.0f) );
614 			}
615 			while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) {
616 				while ( !forward_queue() ) {
617 					sleep( 1 );
618 				}
619 			}
620 		}
621 	}
622 	virtual bool forward_queue() = 0;
623 	bool sleep( int ms ) override = 0;
624 };
625 
626 class write_buffers_polling_wrapper_int : public write_buffers_interface {
627 protected:
628 	std::size_t channels;
629 	std::size_t sampleQueueMaxFrames;
630 	std::deque<std::int16_t> sampleQueue;
631 protected:
~write_buffers_polling_wrapper_int()632 	virtual ~write_buffers_polling_wrapper_int() {
633 		return;
634 	}
635 protected:
write_buffers_polling_wrapper_int(const commandlineflags & flags)636 	write_buffers_polling_wrapper_int( const commandlineflags & flags )
637 		: channels(flags.channels)
638 		, sampleQueueMaxFrames(0)
639 	{
640 		return;
641 	}
set_queue_size_frames(std::size_t frames)642 	void set_queue_size_frames( std::size_t frames ) {
643 		sampleQueueMaxFrames = frames;
644 	}
pop_queue()645 	std::int16_t pop_queue() {
646 		std::int16_t val = 0;
647 		if ( !sampleQueue.empty() ) {
648 			val = sampleQueue.front();
649 			sampleQueue.pop_front();
650 		}
651 		return val;
652 	}
653 public:
write(const std::vector<float * > buffers,std::size_t frames)654 	void write( const std::vector<float*> buffers, std::size_t frames ) override {
655 		for ( std::size_t frame = 0; frame < frames; ++frame ) {
656 			for ( std::size_t channel = 0; channel < channels; ++channel ) {
657 				sampleQueue.push_back( convert_sample_to<std::int16_t>( buffers[channel][frame] ) );
658 			}
659 			while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) {
660 				while ( !forward_queue() ) {
661 					sleep( 1 );
662 				}
663 			}
664 		}
665 	}
write(const std::vector<std::int16_t * > buffers,std::size_t frames)666 	void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
667 		for ( std::size_t frame = 0; frame < frames; ++frame ) {
668 			for ( std::size_t channel = 0; channel < channels; ++channel ) {
669 				sampleQueue.push_back( buffers[channel][frame] );
670 			}
671 			while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) {
672 				while ( !forward_queue() ) {
673 					sleep( 1 );
674 				}
675 			}
676 		}
677 	}
678 	virtual bool forward_queue() = 0;
679 	bool sleep( int ms ) override = 0;
680 };
681 
682 class void_audio_stream : public write_buffers_interface {
683 public:
~void_audio_stream()684 	virtual ~void_audio_stream() {
685 		return;
686 	}
687 public:
write(const std::vector<float * > buffers,std::size_t frames)688 	void write( const std::vector<float*> buffers, std::size_t frames ) override {
689 		(void)buffers;
690 		(void)frames;
691 	}
write(const std::vector<std::int16_t * > buffers,std::size_t frames)692 	void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
693 		(void)buffers;
694 		(void)frames;
695 	}
is_dummy() const696 	bool is_dummy() const override {
697 		return true;
698 	}
699 };
700 
701 class file_audio_stream_base : public write_buffers_interface {
702 protected:
file_audio_stream_base()703 	file_audio_stream_base() {
704 		return;
705 	}
706 public:
write_metadata(std::map<std::string,std::string> metadata)707 	void write_metadata( std::map<std::string,std::string> metadata ) override {
708 		(void)metadata;
709 		return;
710 	}
write_updated_metadata(std::map<std::string,std::string> metadata)711 	void write_updated_metadata( std::map<std::string,std::string> metadata ) override {
712 		(void)metadata;
713 		return;
714 	}
715 	void write( const std::vector<float*> buffers, std::size_t frames ) override = 0;
716 	void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override = 0;
~file_audio_stream_base()717 	virtual ~file_audio_stream_base() {
718 		return;
719 	}
720 };
721 
722 } // namespace openmpt123
723 
724 #endif // OPENMPT123_HPP
725