123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- /*
- * StreamEncoderOpus.cpp
- * ---------------------
- * Purpose: Exporting streamed music files.
- * Notes : none
- * Authors: Joern Heusipp
- * OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #include "stdafx.h"
- #include "StreamEncoder.h"
- #include "StreamEncoderOpus.h"
- #include <sstream>
- #include "Mptrack.h"
- #include <deque>
- #if defined(MPT_WITH_OPUS) && defined(MPT_WITH_OPUSENC)
- #include <opusenc.h>
- #endif
- OPENMPT_NAMESPACE_BEGIN
- static Encoder::Traits BuildTraits()
- {
- Encoder::Traits traits;
- #if defined(MPT_WITH_OPUS) && defined(MPT_WITH_OPUSENC)
- traits.fileExtension = P_("opus");
- traits.fileShortDescription = U_("Opus");
- traits.fileDescription = U_("Ogg Opus");
- traits.encoderSettingsName = U_("Opus");
- traits.canTags = true;
- traits.maxChannels = 4;
- traits.samplerates = mpt::make_vector(opus_all_samplerates);
- traits.modes = Encoder::ModeCBR | Encoder::ModeVBR;
- traits.bitrates = mpt::make_vector(opus_bitrates);
- traits.defaultSamplerate = 48000;
- traits.defaultChannels = 2;
- traits.defaultMode = Encoder::ModeVBR;
- traits.defaultBitrate = 128;
- #endif
- return traits;
- }
- #if defined(MPT_WITH_OPUS) && defined(MPT_WITH_OPUSENC)
- class OpusStreamWriter : public StreamWriterBase
- {
- private:
- OpusEncCallbacks ope_callbacks;
- OggOpusComments *ope_comments;
- OggOpusEnc *ope_encoder;
- std::vector<std::pair<std::string, std::string> > opus_comments;
- private:
- static int CallbackWrite(void *user_data, const unsigned char *ptr, opus_int32 len)
- {
- return reinterpret_cast<OpusStreamWriter*>(user_data)->CallbackWriteImpl(ptr, len);
- }
- static int CallbackClose(void *user_data)
- {
- return reinterpret_cast<OpusStreamWriter*>(user_data)->CallbackCloseImpl();
- }
- int CallbackWriteImpl(const unsigned char *ptr, opus_int32 len)
- {
- if(len < 0)
- {
- return 1;
- }
- if(!ptr && len > 0)
- {
- return 1;
- }
- buf.assign(ptr, ptr + len);
- WriteBuffer();
- return 0;
- }
- int CallbackCloseImpl()
- {
- return 0;
- }
- private:
- void AddCommentField(const std::string &field, const mpt::ustring &data)
- {
- if(!field.empty() && !data.empty())
- {
- opus_comments.push_back(std::make_pair(field, mpt::ToCharset(mpt::Charset::UTF8, data)));
- }
- }
- public:
- OpusStreamWriter(std::ostream &stream, const Encoder::Settings &settings, const FileTags &tags)
- : StreamWriterBase(stream)
- {
- ope_callbacks.write = &CallbackWrite;
- ope_callbacks.close = &CallbackClose;
- opus_comments.clear();
- bool opus_cbr = (settings.Mode == Encoder::ModeCBR);
- int opus_bitrate = settings.Bitrate * 1000;
- if(settings.Tags)
- {
- AddCommentField("ENCODER", tags.encoder);
- AddCommentField("SOURCEMEDIA", U_("tracked music file"));
- AddCommentField("TITLE", tags.title );
- AddCommentField("ARTIST", tags.artist );
- AddCommentField("ALBUM", tags.album );
- AddCommentField("DATE", tags.year );
- AddCommentField("COMMENT", tags.comments );
- AddCommentField("GENRE", tags.genre );
- AddCommentField("CONTACT", tags.url );
- AddCommentField("BPM", tags.bpm ); // non-standard
- AddCommentField("TRACKNUMBER", tags.trackno );
- }
- int ope_error = 0;
- ope_comments = ope_comments_create();
- if(settings.Tags && ope_comments)
- {
- for(const auto & comment : opus_comments)
- {
- ope_comments_add(ope_comments, comment.first.c_str(), comment.second.c_str());
- }
- }
- ope_encoder = ope_encoder_create_callbacks(&ope_callbacks, this, ope_comments, settings.Samplerate, settings.Channels, settings.Channels > 2 ? 1 : 0, &ope_error);
-
- opus_int32 ctl_serial = mpt::random<uint32>(theApp.PRNG());
- ope_encoder_ctl(ope_encoder, OPE_SET_SERIALNO(ctl_serial));
- opus_int32 ctl_bitrate = opus_bitrate;
- ope_encoder_ctl(ope_encoder, OPUS_SET_BITRATE(ctl_bitrate));
- if(opus_cbr)
- {
- opus_int32 ctl_vbr = 0;
- ope_encoder_ctl(ope_encoder, OPUS_SET_VBR(ctl_vbr));
- } else
- {
- opus_int32 ctl_vbr = 1;
- ope_encoder_ctl(ope_encoder, OPUS_SET_VBR(ctl_vbr));
- opus_int32 ctl_vbrcontraint = 0;
- ope_encoder_ctl(ope_encoder, OPUS_SET_VBR_CONSTRAINT(ctl_vbrcontraint));
- }
- opus_int32 complexity = settings.Details.OpusComplexity;
- if(complexity >= 0)
- {
- ope_encoder_ctl(ope_encoder, OPUS_SET_COMPLEXITY(complexity));
- }
- ope_encoder_flush_header(ope_encoder);
-
- }
- void WriteInterleaved(size_t count, const float *interleaved) override
- {
- while(count > 0)
- {
- ope_encoder_write_float(ope_encoder, interleaved, mpt::saturate_cast<int>(count));
- count -= static_cast<size_t>(mpt::saturate_cast<int>(count));
- }
- }
- void WriteFinalize() override
- {
- ope_encoder_drain(ope_encoder);
- }
- virtual ~OpusStreamWriter()
- {
- ope_encoder_destroy(ope_encoder);
- ope_encoder = NULL;
- ope_comments_destroy(ope_comments);
- ope_comments = NULL;
- }
- };
- #endif // MPT_WITH_OGG
- OggOpusEncoder::OggOpusEncoder()
- {
- SetTraits(BuildTraits());
- }
- bool OggOpusEncoder::IsAvailable() const
- {
- #if defined(MPT_WITH_OPUS) && defined(MPT_WITH_OPUSENC)
- return true;
- #else
- return false;
- #endif
- }
- std::unique_ptr<IAudioStreamEncoder> OggOpusEncoder::ConstructStreamEncoder(std::ostream &file, const Encoder::Settings &settings, const FileTags &tags) const
- {
- if(!IsAvailable())
- {
- return nullptr;
- }
- #if defined(MPT_WITH_OPUS) && defined(MPT_WITH_OPUSENC)
- return std::make_unique<OpusStreamWriter>(file, settings, tags);
- #else
- return nullptr;
- #endif
- }
- OPENMPT_NAMESPACE_END
|