aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/mptrack/StreamEncoderFLAC.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/mptrack/StreamEncoderFLAC.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/StreamEncoderFLAC.cpp')
-rw-r--r--Src/external_dependencies/openmpt-trunk/mptrack/StreamEncoderFLAC.cpp219
1 files changed, 219 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/StreamEncoderFLAC.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/StreamEncoderFLAC.cpp
new file mode 100644
index 00000000..a5347311
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/mptrack/StreamEncoderFLAC.cpp
@@ -0,0 +1,219 @@
+/*
+ * StreamEncoder.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 "StreamEncoderFLAC.h"
+
+#include "Mptrack.h"
+#include "TrackerSettings.h"
+
+#include <FLAC/metadata.h>
+#include <FLAC/format.h>
+#include <FLAC/stream_encoder.h>
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+class FLACStreamWriter : public StreamWriterBase
+{
+private:
+ const FLACEncoder &enc;
+ Encoder::Settings settings;
+ FLAC__StreamMetadata *flac_metadata[1];
+ FLAC__StreamEncoder *encoder;
+ std::vector<FLAC__int32> sampleBuf;
+private:
+ static FLAC__StreamEncoderWriteStatus FLACWriteCallback(const FLAC__StreamEncoder *flacenc, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data)
+ {
+ return reinterpret_cast<FLACStreamWriter*>(client_data)->WriteCallback(flacenc, buffer, bytes, samples, current_frame);
+ }
+ static FLAC__StreamEncoderSeekStatus FLACSeekCallback(const FLAC__StreamEncoder *flacenc, FLAC__uint64 absolute_byte_offset, void *client_data)
+ {
+ return reinterpret_cast<FLACStreamWriter*>(client_data)->SeekCallback(flacenc, absolute_byte_offset);
+ }
+ static FLAC__StreamEncoderTellStatus FLACTellCallback(const FLAC__StreamEncoder *flacenc, FLAC__uint64 *absolute_byte_offset, void *client_data)
+ {
+ return reinterpret_cast<FLACStreamWriter*>(client_data)->TellCallback(flacenc, absolute_byte_offset);
+ }
+ FLAC__StreamEncoderWriteStatus WriteCallback(const FLAC__StreamEncoder *flacenc, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame)
+ {
+ MPT_UNREFERENCED_PARAMETER(flacenc);
+ MPT_UNREFERENCED_PARAMETER(samples);
+ MPT_UNREFERENCED_PARAMETER(current_frame);
+ f.write(reinterpret_cast<const char*>(buffer), bytes);
+ if(!f) return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
+ return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+ }
+ FLAC__StreamEncoderSeekStatus SeekCallback(const FLAC__StreamEncoder *flacenc, FLAC__uint64 absolute_byte_offset)
+ {
+ MPT_UNREFERENCED_PARAMETER(flacenc);
+ f.seekp(absolute_byte_offset);
+ if(!f) return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR;
+ return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
+ }
+ FLAC__StreamEncoderTellStatus TellCallback(const FLAC__StreamEncoder *flacenc, FLAC__uint64 *absolute_byte_offset)
+ {
+ MPT_UNREFERENCED_PARAMETER(flacenc);
+ if(absolute_byte_offset)
+ {
+ *absolute_byte_offset = f.tellp();
+ }
+ if(!f) return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR;
+ return FLAC__STREAM_ENCODER_TELL_STATUS_OK;
+ }
+private:
+ void AddCommentField(const std::string &field, const mpt::ustring &data)
+ {
+ if(!field.empty() && !data.empty())
+ {
+ FLAC__StreamMetadata_VorbisComment_Entry entry;
+ FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, field.c_str(), mpt::ToCharset(mpt::Charset::UTF8, data).c_str());
+ FLAC__metadata_object_vorbiscomment_append_comment(flac_metadata[0], entry, false);
+ }
+ }
+public:
+ FLACStreamWriter(const FLACEncoder &enc_, std::ostream &stream, const Encoder::Settings &settings_, const FileTags &tags)
+ : StreamWriterBase(stream)
+ , enc(enc_)
+ , settings(settings_)
+ {
+ flac_metadata[0] = nullptr;
+ encoder = nullptr;
+
+ MPT_ASSERT(settings.Format.GetSampleFormat().IsValid());
+ MPT_ASSERT(settings.Samplerate > 0);
+ MPT_ASSERT(settings.Channels > 0);
+
+ encoder = FLAC__stream_encoder_new();
+
+ FLAC__stream_encoder_set_channels(encoder, settings.Channels);
+ FLAC__stream_encoder_set_bits_per_sample(encoder, settings.Format.GetSampleFormat().GetBitsPerSample());
+ FLAC__stream_encoder_set_sample_rate(encoder, settings.Samplerate);
+
+ int compressionLevel = settings.Details.FLACCompressionLevel;
+ FLAC__stream_encoder_set_compression_level(encoder, compressionLevel);
+
+ if(settings.Tags)
+ {
+ flac_metadata[0] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
+ 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 );
+ FLAC__stream_encoder_set_metadata(encoder, flac_metadata, 1);
+ }
+
+ FLAC__stream_encoder_init_stream(encoder, FLACWriteCallback, FLACSeekCallback, FLACTellCallback, nullptr, this);
+
+ }
+ SampleFormat GetSampleFormat() const
+ {
+ return settings.Format.GetSampleFormat();
+ }
+ template <typename Tsample>
+ void WriteInterleavedInt(std::size_t frameCount, const Tsample *p)
+ {
+ MPT_ASSERT(settings.Format.GetSampleFormat() == SampleFormatTraits<Tsample>::sampleFormat());
+ sampleBuf.resize(frameCount * settings.Channels);
+ for(std::size_t frame = 0; frame < frameCount; ++frame)
+ {
+ for(int channel = 0; channel < settings.Channels; ++channel)
+ {
+ sampleBuf[frame * settings.Channels + channel] = *p;
+ p++;
+ }
+ }
+ while(frameCount > 0)
+ {
+ unsigned int frameCountChunk = mpt::saturate_cast<unsigned int>(frameCount);
+ FLAC__stream_encoder_process_interleaved(encoder, sampleBuf.data(), frameCountChunk);
+ frameCount -= frameCountChunk;
+ }
+ }
+ void WriteInterleaved(std::size_t frameCount, const int8 *interleaved) override
+ {
+ WriteInterleavedInt(frameCount, interleaved);
+ }
+ void WriteInterleaved(std::size_t frameCount, const int16 *interleaved) override
+ {
+ WriteInterleavedInt(frameCount, interleaved);
+ }
+ void WriteInterleaved(std::size_t frameCount, const int24 *interleaved) override
+ {
+ WriteInterleavedInt(frameCount, interleaved);
+ }
+ void WriteFinalize() override
+ {
+ FLAC__stream_encoder_finish(encoder);
+ }
+ virtual ~FLACStreamWriter()
+ {
+ FLAC__stream_encoder_delete(encoder);
+ encoder = nullptr;
+
+ if(flac_metadata[0])
+ {
+ FLAC__metadata_object_delete(flac_metadata[0]);
+ flac_metadata[0] = nullptr;
+ }
+ }
+};
+
+
+
+FLACEncoder::FLACEncoder()
+{
+ Encoder::Traits traits;
+ traits.fileExtension = P_("flac");
+ traits.fileShortDescription = U_("FLAC");
+ traits.fileDescription = U_("Free Lossless Audio Codec");
+ traits.encoderSettingsName = U_("FLAC");
+ traits.canTags = true;
+ traits.maxChannels = 4;
+ traits.samplerates = TrackerSettings::Instance().GetSampleRates();
+ traits.modes = Encoder::ModeLossless;
+ traits.formats.push_back({ Encoder::Format::Encoding::Integer, 24, mpt::get_endian() });
+ traits.formats.push_back({ Encoder::Format::Encoding::Integer, 16, mpt::get_endian() });
+ traits.formats.push_back({ Encoder::Format::Encoding::Integer, 8, mpt::get_endian() });
+ traits.defaultSamplerate = 48000;
+ traits.defaultChannels = 2;
+ traits.defaultMode = Encoder::ModeLossless;
+ traits.defaultFormat = { Encoder::Format::Encoding::Integer, 24, mpt::get_endian() };
+ SetTraits(traits);
+}
+
+
+bool FLACEncoder::IsAvailable() const
+{
+ return true;
+}
+
+
+std::unique_ptr<IAudioStreamEncoder> FLACEncoder::ConstructStreamEncoder(std::ostream &file, const Encoder::Settings &settings, const FileTags &tags) const
+{
+ if(!IsAvailable())
+ {
+ return nullptr;
+ }
+ return std::make_unique<FLACStreamWriter>(*this, file, settings, tags);
+}
+
+
+OPENMPT_NAMESPACE_END