openmpt123_flac.hpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /*
  2. * openmpt123_flac.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_FLAC_HPP
  10. #define OPENMPT123_FLAC_HPP
  11. #include "openmpt123_config.hpp"
  12. #include "openmpt123.hpp"
  13. #if defined(MPT_WITH_FLAC)
  14. #if defined(_MSC_VER) && defined(__clang__) && defined(__c2__)
  15. #include <sys/types.h>
  16. #if __STDC__
  17. typedef _off_t off_t;
  18. #endif
  19. #endif
  20. #if defined(__clang__)
  21. #pragma clang diagnostic push
  22. #pragma clang diagnostic ignored "-Wreserved-id-macro"
  23. #endif
  24. #include <FLAC/metadata.h>
  25. #include <FLAC/format.h>
  26. #include <FLAC/stream_encoder.h>
  27. #if defined(__clang__)
  28. #pragma clang diagnostic pop
  29. #endif
  30. namespace openmpt123 {
  31. class flac_stream_raii : public file_audio_stream_base {
  32. private:
  33. commandlineflags flags;
  34. std::string filename;
  35. bool called_init;
  36. std::vector< std::pair< std::string, std::string > > tags;
  37. FLAC__StreamMetadata * flac_metadata[1];
  38. FLAC__StreamEncoder * encoder;
  39. std::vector<FLAC__int32> interleaved_buffer;
  40. void add_vorbiscomment_field( FLAC__StreamMetadata * vorbiscomment, const std::string & field, const std::string & value ) {
  41. if ( !value.empty() ) {
  42. FLAC__StreamMetadata_VorbisComment_Entry entry;
  43. FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair( &entry, field.c_str(), value.c_str() );
  44. FLAC__metadata_object_vorbiscomment_append_comment( vorbiscomment, entry, false );
  45. }
  46. }
  47. public:
  48. flac_stream_raii( const std::string & filename_, const commandlineflags & flags_, std::ostream & /*log*/ ) : flags(flags_), filename(filename_), called_init(false), encoder(0) {
  49. flac_metadata[0] = 0;
  50. encoder = FLAC__stream_encoder_new();
  51. if ( !encoder ) {
  52. throw exception( "error creating flac encoder" );
  53. }
  54. FLAC__stream_encoder_set_channels( encoder, flags.channels );
  55. FLAC__stream_encoder_set_bits_per_sample( encoder, flags.use_float ? 24 : 16 );
  56. FLAC__stream_encoder_set_sample_rate( encoder, flags.samplerate );
  57. FLAC__stream_encoder_set_compression_level( encoder, 8 );
  58. }
  59. ~flac_stream_raii() {
  60. if ( encoder ) {
  61. FLAC__stream_encoder_finish( encoder );
  62. FLAC__stream_encoder_delete( encoder );
  63. encoder = 0;
  64. }
  65. if ( flac_metadata[0] ) {
  66. FLAC__metadata_object_delete( flac_metadata[0] );
  67. flac_metadata[0] = 0;
  68. }
  69. }
  70. void write_metadata( std::map<std::string,std::string> metadata ) override {
  71. if ( called_init ) {
  72. return;
  73. }
  74. tags.clear();
  75. tags.push_back( std::make_pair( "TITLE", metadata[ "title" ] ) );
  76. tags.push_back( std::make_pair( "ARTIST", metadata[ "artist" ] ) );
  77. tags.push_back( std::make_pair( "DATE", metadata[ "date" ] ) );
  78. tags.push_back( std::make_pair( "COMMENT", metadata[ "message" ] ) );
  79. if ( !metadata[ "type" ].empty() && !metadata[ "tracker" ].empty() ) {
  80. tags.push_back( std::make_pair( "SOURCEMEDIA", std::string() + "'" + metadata[ "type" ] + "' tracked music file, made with '" + metadata[ "tracker" ] + "', rendered with '" + get_encoder_tag() + "'" ) );
  81. } else if ( !metadata[ "type_long" ].empty() ) {
  82. tags.push_back( std::make_pair( "SOURCEMEDIA", std::string() + "'" + metadata[ "type" ] + "' tracked music file, rendered with '" + get_encoder_tag() + "'" ) );
  83. } else if ( !metadata[ "tracker" ].empty() ) {
  84. tags.push_back( std::make_pair( "SOURCEMEDIA", std::string() + "tracked music file, made with '" + metadata[ "tracker" ] + "', rendered with '" + get_encoder_tag() + "'" ) );
  85. } else {
  86. tags.push_back( std::make_pair( "SOURCEMEDIA", std::string() + "tracked music file, rendered with '" + get_encoder_tag() + "'" ) );
  87. }
  88. tags.push_back( std::make_pair( "ENCODER", get_encoder_tag() ) );
  89. flac_metadata[0] = FLAC__metadata_object_new( FLAC__METADATA_TYPE_VORBIS_COMMENT );
  90. for ( std::vector< std::pair< std::string, std::string > >::iterator tag = tags.begin(); tag != tags.end(); ++tag ) {
  91. add_vorbiscomment_field( flac_metadata[0], tag->first, tag->second );
  92. }
  93. FLAC__stream_encoder_set_metadata( encoder, flac_metadata, 1 );
  94. }
  95. void write( const std::vector<float*> buffers, std::size_t frames ) override {
  96. if ( !called_init ) {
  97. FLAC__stream_encoder_init_file( encoder, filename.c_str(), NULL, 0 );
  98. called_init = true;
  99. }
  100. interleaved_buffer.clear();
  101. for ( std::size_t frame = 0; frame < frames; frame++ ) {
  102. for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
  103. float in = buffers[channel][frame];
  104. if ( in <= -1.0f ) {
  105. in = -1.0f;
  106. } else if ( in >= 1.0f ) {
  107. in = 1.0f;
  108. }
  109. FLAC__int32 out = mpt_lround( in * (1<<23) );
  110. out = std::max( 0 - (1<<23), out );
  111. out = std::min( out, 0 + (1<<23) - 1 );
  112. interleaved_buffer.push_back( out );
  113. }
  114. }
  115. FLAC__stream_encoder_process_interleaved( encoder, interleaved_buffer.data(), static_cast<unsigned int>( frames ) );
  116. }
  117. void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
  118. if ( !called_init ) {
  119. FLAC__stream_encoder_init_file( encoder, filename.c_str(), NULL, 0 );
  120. called_init = true;
  121. }
  122. interleaved_buffer.clear();
  123. for ( std::size_t frame = 0; frame < frames; frame++ ) {
  124. for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) {
  125. interleaved_buffer.push_back( buffers[channel][frame] );
  126. }
  127. }
  128. FLAC__stream_encoder_process_interleaved( encoder, interleaved_buffer.data(), static_cast<unsigned int>( frames ) );
  129. }
  130. };
  131. } // namespace openmpt123
  132. #endif // MPT_WITH_FLAC
  133. #endif // OPENMPT123_FLAC_HPP