openmpt123_sndfile.hpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * openmpt123_sndfile.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. #ifndef OPENMPT123_SNDFILE_HPP
  10. #define OPENMPT123_SNDFILE_HPP
  11. #include "openmpt123_config.hpp"
  12. #include "openmpt123.hpp"
  13. #if defined(MPT_WITH_SNDFILE)
  14. #include <sndfile.h>
  15. namespace openmpt123 {
  16. class sndfile_stream_raii : public file_audio_stream_base {
  17. private:
  18. commandlineflags flags;
  19. std::ostream & log;
  20. SNDFILE * sndfile;
  21. std::vector<float> interleaved_float_buffer;
  22. std::vector<std::int16_t> interleaved_int_buffer;
  23. private:
  24. enum match_mode_enum {
  25. match_print,
  26. match_recurse,
  27. match_exact,
  28. match_better,
  29. match_any
  30. };
  31. std::string match_mode_to_string( match_mode_enum match_mode ) {
  32. switch ( match_mode ) {
  33. case match_print : return "print" ; break;
  34. case match_recurse: return "recurse"; break;
  35. case match_exact : return "exact" ; break;
  36. case match_better : return "better" ; break;
  37. case match_any : return "any" ; break;
  38. }
  39. return "";
  40. }
  41. int matched_result( const SF_FORMAT_INFO & format_info, const SF_FORMAT_INFO & subformat_info, match_mode_enum match_mode ) {
  42. if ( flags.verbose ) {
  43. log << "sndfile: using format '"
  44. << format_info.name << " (" << format_info.extension << ")" << " / " << subformat_info.name
  45. << "', "
  46. << "match: " << match_mode_to_string( match_mode )
  47. << std::endl;
  48. }
  49. return ( format_info.format & SF_FORMAT_TYPEMASK ) | subformat_info.format;
  50. }
  51. int find_format( const std::string & extension, match_mode_enum match_mode ) {
  52. if ( match_mode == match_recurse ) {
  53. int result = 0;
  54. result = find_format( extension, match_exact );
  55. if ( result ) {
  56. return result;
  57. }
  58. result = find_format( extension, match_better );
  59. if ( result ) {
  60. return result;
  61. }
  62. result = find_format( extension, match_any );
  63. if ( result ) {
  64. return result;
  65. }
  66. if ( result ) {
  67. return result;
  68. }
  69. return 0;
  70. }
  71. int format = 0;
  72. int major_count;
  73. sf_command( 0, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof( int ) );
  74. for ( int m = 0; m < major_count; m++ ) {
  75. SF_FORMAT_INFO format_info;
  76. format_info.format = m;
  77. sf_command( 0, SFC_GET_FORMAT_MAJOR, &format_info, sizeof( SF_FORMAT_INFO ) );
  78. format = format_info.format;
  79. int subtype_count;
  80. sf_command( 0, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count, sizeof( int ) );
  81. for ( int s = 0; s < subtype_count; s++ ) {
  82. SF_FORMAT_INFO subformat_info;
  83. subformat_info.format = s;
  84. sf_command( 0, SFC_GET_FORMAT_SUBTYPE, &subformat_info, sizeof( SF_FORMAT_INFO ) );
  85. format = ( format & SF_FORMAT_TYPEMASK ) | subformat_info.format;
  86. SF_INFO sfinfo;
  87. std::memset( &sfinfo, 0, sizeof( SF_INFO ) );
  88. sfinfo.channels = flags.channels;
  89. sfinfo.format = format;
  90. if ( sf_format_check( &sfinfo ) ) {
  91. switch ( match_mode ) {
  92. case match_print:
  93. log << "sndfile: "
  94. << ( format_info.name ? format_info.name : "" ) << " (" << ( format_info.extension ? format_info.extension : "" ) << ")"
  95. << " / "
  96. << ( subformat_info.name ? subformat_info.name : "" )
  97. << " ["
  98. << std::setbase(16) << std::setw(8) << std::setfill('0') << format_info.format
  99. << "|"
  100. << std::setbase(16) << std::setw(8) << std::setfill('0') << subformat_info.format
  101. << "]"
  102. << std::endl;
  103. break;
  104. case match_recurse:
  105. break;
  106. case match_exact:
  107. if ( extension == format_info.extension ) {
  108. if ( flags.use_float && ( subformat_info.format == SF_FORMAT_FLOAT ) ) {
  109. return matched_result( format_info, subformat_info, match_mode );
  110. } else if ( !flags.use_float && ( subformat_info.format == SF_FORMAT_PCM_16 ) ) {
  111. return matched_result( format_info, subformat_info, match_mode );
  112. }
  113. }
  114. break;
  115. case match_better:
  116. if ( extension == format_info.extension ) {
  117. if ( flags.use_float && ( subformat_info.format == SF_FORMAT_FLOAT || subformat_info.format == SF_FORMAT_DOUBLE ) ) {
  118. return matched_result( format_info, subformat_info, match_mode );
  119. } 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 ) ) ) {
  120. return matched_result( format_info, subformat_info, match_mode );
  121. }
  122. }
  123. break;
  124. case match_any:
  125. if ( extension == format_info.extension ) {
  126. return matched_result( format_info, subformat_info, match_mode );
  127. }
  128. break;
  129. }
  130. }
  131. }
  132. }
  133. return 0;
  134. }
  135. void write_metadata_field( int str_type, const std::string & str ) {
  136. if ( !str.empty() ) {
  137. sf_set_string( sndfile, str_type, str.c_str() );
  138. }
  139. }
  140. public:
  141. sndfile_stream_raii( const std::string & filename, const commandlineflags & flags_, std::ostream & log_ ) : flags(flags_), log(log_), sndfile(0) {
  142. if ( flags.verbose ) {
  143. find_format( "", match_print );
  144. log << std::endl;
  145. }
  146. int format = find_format( flags.output_extension, match_recurse );
  147. if ( !format ) {
  148. throw exception( "unknown file type" );
  149. }
  150. SF_INFO info;
  151. std::memset( &info, 0, sizeof( SF_INFO ) );
  152. info.samplerate = flags.samplerate;
  153. info.channels = flags.channels;
  154. info.format = format;
  155. sndfile = sf_open( filename.c_str(), SFM_WRITE, &info );
  156. }
  157. ~sndfile_stream_raii() {
  158. sf_close( sndfile );
  159. sndfile = 0;
  160. }
  161. void write_metadata( std::map<std::string,std::string> metadata ) override {
  162. write_metadata_field( SF_STR_TITLE, metadata[ "title" ] );
  163. write_metadata_field( SF_STR_ARTIST, metadata[ "artist" ] );
  164. write_metadata_field( SF_STR_DATE, metadata[ "date" ] );
  165. write_metadata_field( SF_STR_COMMENT, metadata[ "message" ] );
  166. write_metadata_field( SF_STR_SOFTWARE, append_software_tag( metadata[ "tracker" ] ) );
  167. }
  168. void write( const std::vector<float*> buffers, std::size_t frames ) override {
  169. interleaved_float_buffer.clear();
  170. for ( std::size_t frame = 0; frame < frames; frame++ ) {
  171. for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
  172. interleaved_float_buffer.push_back( buffers[channel][frame] );
  173. }
  174. }
  175. sf_writef_float( sndfile, interleaved_float_buffer.data(), frames );
  176. }
  177. void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
  178. interleaved_int_buffer.clear();
  179. for ( std::size_t frame = 0; frame < frames; frame++ ) {
  180. for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
  181. interleaved_int_buffer.push_back( buffers[channel][frame] );
  182. }
  183. }
  184. sf_writef_short( sndfile, interleaved_int_buffer.data(), frames );
  185. }
  186. };
  187. } // namespace openmpt123
  188. #endif // MPT_WITH_SNDFILE
  189. #endif // OPENMPT123_SNDFILE_HPP