1
0

StreamEncoderVorbis.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /*
  2. * StreamEncoder.cpp
  3. * -----------------
  4. * Purpose: Exporting streamed music files.
  5. * Notes : none
  6. * Authors: Joern Heusipp
  7. * OpenMPT Devs
  8. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  9. */
  10. #include "stdafx.h"
  11. #include "StreamEncoder.h"
  12. #include "StreamEncoderVorbis.h"
  13. #include "Mptrack.h"
  14. #ifdef MPT_WITH_OGG
  15. #include <ogg/ogg.h>
  16. #endif
  17. #ifdef MPT_WITH_VORBIS
  18. #include <vorbis/codec.h>
  19. #endif
  20. #ifdef MPT_WITH_VORBISENC
  21. #include <vorbis/vorbisenc.h>
  22. #endif
  23. OPENMPT_NAMESPACE_BEGIN
  24. static Encoder::Traits VorbisBuildTraits()
  25. {
  26. Encoder::Traits traits;
  27. #if defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISENC)
  28. traits.fileExtension = P_("ogg");
  29. traits.fileShortDescription = U_("Vorbis");
  30. traits.fileDescription = U_("Ogg Vorbis");
  31. traits.encoderSettingsName = U_("Vorbis");
  32. traits.canTags = true;
  33. traits.maxChannels = 4;
  34. traits.samplerates = mpt::make_vector(vorbis_samplerates);
  35. traits.modes = Encoder::ModeABR | Encoder::ModeQuality;
  36. traits.bitrates = mpt::make_vector(vorbis_bitrates);
  37. traits.defaultSamplerate = 48000;
  38. traits.defaultChannels = 2;
  39. traits.defaultMode = Encoder::ModeQuality;
  40. traits.defaultBitrate = 160;
  41. traits.defaultQuality = 0.5;
  42. #endif
  43. return traits;
  44. }
  45. #if defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISENC)
  46. class VorbisStreamWriter : public StreamWriterBase
  47. {
  48. private:
  49. ogg_stream_state os;
  50. ogg_page og;
  51. ogg_packet op;
  52. vorbis_info vi;
  53. vorbis_comment vc;
  54. vorbis_dsp_state vd;
  55. vorbis_block vb;
  56. int vorbis_channels;
  57. private:
  58. void WritePage()
  59. {
  60. buf.resize(og.header_len);
  61. std::memcpy(buf.data(), og.header, og.header_len);
  62. WriteBuffer();
  63. buf.resize(og.body_len);
  64. std::memcpy(buf.data(), og.body, og.body_len);
  65. WriteBuffer();
  66. }
  67. void AddCommentField(const std::string &field, const mpt::ustring &data)
  68. {
  69. if(!field.empty() && !data.empty())
  70. {
  71. vorbis_comment_add_tag(&vc, field.c_str(), mpt::ToCharset(mpt::Charset::UTF8, data).c_str());
  72. }
  73. }
  74. public:
  75. VorbisStreamWriter(std::ostream &stream, const Encoder::Settings &settings, const FileTags &tags)
  76. : StreamWriterBase(stream)
  77. {
  78. vorbis_channels = 0;
  79. vorbis_channels = settings.Channels;
  80. vorbis_info_init(&vi);
  81. vorbis_comment_init(&vc);
  82. if(settings.Mode == Encoder::ModeQuality)
  83. {
  84. vorbis_encode_init_vbr(&vi, vorbis_channels, settings.Samplerate, settings.Quality);
  85. } else
  86. {
  87. vorbis_encode_init(&vi, vorbis_channels, settings.Samplerate, -1, settings.Bitrate * 1000, -1);
  88. }
  89. vorbis_analysis_init(&vd, &vi);
  90. vorbis_block_init(&vd, &vb);
  91. ogg_stream_init(&os, mpt::random<uint32>(theApp.PRNG()));
  92. if(settings.Tags)
  93. {
  94. AddCommentField("ENCODER", tags.encoder);
  95. AddCommentField("SOURCEMEDIA", U_("tracked music file"));
  96. AddCommentField("TITLE", tags.title );
  97. AddCommentField("ARTIST", tags.artist );
  98. AddCommentField("ALBUM", tags.album );
  99. AddCommentField("DATE", tags.year );
  100. AddCommentField("COMMENT", tags.comments );
  101. AddCommentField("GENRE", tags.genre );
  102. AddCommentField("CONTACT", tags.url );
  103. AddCommentField("BPM", tags.bpm ); // non-standard
  104. AddCommentField("TRACKNUMBER", tags.trackno );
  105. }
  106. ogg_packet header;
  107. ogg_packet header_comm;
  108. ogg_packet header_code;
  109. vorbis_analysis_headerout(&vd, &vc, &header, &header_comm, &header_code);
  110. ogg_stream_packetin(&os, &header);
  111. while(ogg_stream_flush(&os, &og))
  112. {
  113. WritePage();
  114. }
  115. ogg_stream_packetin(&os, &header_comm);
  116. ogg_stream_packetin(&os, &header_code);
  117. while(ogg_stream_flush(&os, &og))
  118. {
  119. WritePage();
  120. }
  121. }
  122. void WriteInterleaved(size_t count, const float *interleaved) override
  123. {
  124. size_t countTotal = count;
  125. while(countTotal > 0)
  126. {
  127. int countChunk = mpt::saturate_cast<int>(countTotal);
  128. countTotal -= countChunk;
  129. float **buffer = vorbis_analysis_buffer(&vd, countChunk);
  130. for(int frame = 0; frame < countChunk; ++frame)
  131. {
  132. for(int channel = 0; channel < vorbis_channels; ++channel)
  133. {
  134. buffer[channel][frame] = interleaved[frame*vorbis_channels+channel];
  135. }
  136. }
  137. vorbis_analysis_wrote(&vd, countChunk);
  138. while(vorbis_analysis_blockout(&vd, &vb) == 1)
  139. {
  140. vorbis_analysis(&vb, NULL);
  141. vorbis_bitrate_addblock(&vb);
  142. while(vorbis_bitrate_flushpacket(&vd, &op))
  143. {
  144. ogg_stream_packetin(&os, &op);
  145. while(ogg_stream_pageout(&os, &og))
  146. {
  147. WritePage();
  148. }
  149. }
  150. }
  151. }
  152. }
  153. void WriteFinalize() override
  154. {
  155. vorbis_analysis_wrote(&vd, 0);
  156. while(vorbis_analysis_blockout(&vd, &vb) == 1)
  157. {
  158. vorbis_analysis(&vb, NULL);
  159. vorbis_bitrate_addblock(&vb);
  160. while(vorbis_bitrate_flushpacket(&vd, &op))
  161. {
  162. ogg_stream_packetin(&os, &op);
  163. while(ogg_stream_flush(&os, &og))
  164. {
  165. WritePage();
  166. if(ogg_page_eos(&og))
  167. {
  168. break;
  169. }
  170. }
  171. }
  172. }
  173. }
  174. virtual ~VorbisStreamWriter()
  175. {
  176. ogg_stream_clear(&os);
  177. vorbis_block_clear(&vb);
  178. vorbis_dsp_clear(&vd);
  179. vorbis_comment_clear(&vc);
  180. vorbis_info_clear(&vi);
  181. }
  182. };
  183. #endif // MPT_WITH_OGG && MPT_WITH_VORBIS && MPT_WITH_VORBISENC
  184. VorbisEncoder::VorbisEncoder()
  185. {
  186. SetTraits(VorbisBuildTraits());
  187. }
  188. bool VorbisEncoder::IsAvailable() const
  189. {
  190. #if defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISENC)
  191. return true;
  192. #else
  193. return false;
  194. #endif
  195. }
  196. std::unique_ptr<IAudioStreamEncoder> VorbisEncoder::ConstructStreamEncoder(std::ostream &file, const Encoder::Settings &settings, const FileTags &tags) const
  197. {
  198. #if defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISENC)
  199. return std::make_unique<VorbisStreamWriter>(file, settings, tags);
  200. #else
  201. MPT_UNREFERENCED_PARAMETER(file);
  202. MPT_UNREFERENCED_PARAMETER(settings);
  203. MPT_UNREFERENCED_PARAMETER(tags);
  204. return nullptr;
  205. #endif
  206. }
  207. bool VorbisEncoder::IsBitrateSupported(int samplerate, int channels, int bitrate) const
  208. {
  209. #if defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISENC)
  210. vorbis_info vi;
  211. vorbis_info_init(&vi);
  212. bool result = (0 == vorbis_encode_init(&vi, channels, samplerate, -1, bitrate * 1000, -1));
  213. vorbis_info_clear(&vi);
  214. return result;
  215. #else
  216. MPT_UNREFERENCED_PARAMETER(samplerate);
  217. MPT_UNREFERENCED_PARAMETER(channels);
  218. MPT_UNREFERENCED_PARAMETER(bitrate);
  219. return false;
  220. #endif
  221. }
  222. mpt::ustring VorbisEncoder::DescribeQuality(float quality) const
  223. {
  224. static constexpr int q_table[11] = { 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 500 }; // http://wiki.hydrogenaud.io/index.php?title=Recommended_Ogg_Vorbis
  225. int q = Clamp(mpt::saturate_round<int>(quality * 10.0f), 0, 10);
  226. return MPT_UFORMAT("Q{} (~{} kbit)")(mpt::ufmt::fix(quality * 10.0f, 1), q_table[q]);
  227. }
  228. OPENMPT_NAMESPACE_END