222 lines
5.4 KiB
C++
222 lines
5.4 KiB
C++
|
/*
|
||
|
* 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
|