From 97c2b2cd56de291f100da99236c41c5842f99820 Mon Sep 17 00:00:00 2001 From: Dimitri Diakopoulos Date: Mon, 25 Jul 2016 20:21:29 -0700 Subject: [PATCH] space normalization --- examples/src/Main.cpp | 140 ++++++---- src/WavEncoder.cpp | 612 +++++++++++++++++++++--------------------- 2 files changed, 392 insertions(+), 360 deletions(-) diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index 00eb184..d645829 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -1,3 +1,31 @@ + +Skip to content +This repository + +Pull requests +Issues +Gist + +@ddiakopoulos + +5 +22 + +3 + +ddiakopoulos / libnyquist +Code +Issues 6 +Pull requests 0 +Pulse +Graphs +Settings +libnyquist / examples / src / Main.cpp +ce56c94 2 minutes ago +@ddiakopoulos ddiakopoulos the most basic, generally not- correct linear interpolating resampling… +@ddiakopoulos +@r-lyeh +150 lines(123 sloc) 5.71 KB // Note to Visual Studio / Windows users: you must set the working directory manually on the project file // to $(ProjectDir)../../../ since these settings are not saved directly in project. The loader // will be unable to find the example assets unless the proper working directory is set. @@ -27,64 +55,64 @@ int main(int argc, const char **argv) try NyquistIO loader; - if (argc > 1) - { - std::string cli_arg = std::string(argv[1]); - loader.Load(fileData.get(), cli_arg); - } - else - { - // Circular libnyquist testing - //loader.Load(fileData.get(), "encoded.opus"); + if (argc > 1) + { + std::string cli_arg = std::string(argv[1]); + loader.Load(fileData.get(), cli_arg); + } + else + { + // Circular libnyquist testing + //loader.Load(fileData.get(), "encoded.opus"); - // 1-channel wave - //loader.Load(fileData.get(), "test_data/1ch/44100/8/test.wav"); - //loader.Load(fileData.get(), "test_data/1ch/44100/16/test.wav"); - loader.Load(fileData.get(), "test_data/1ch/44100/24/test.wav"); - //loader.Load(fileData.get(), "test_data/1ch/44100/32/test.wav"); - //loader.Load(fileData.get(), "test_data/1ch/44100/64/test.wav"); + // 1-channel wave + //loader.Load(fileData.get(), "test_data/1ch/44100/8/test.wav"); + //loader.Load(fileData.get(), "test_data/1ch/44100/16/test.wav"); + loader.Load(fileData.get(), "test_data/1ch/44100/24/test.wav"); + //loader.Load(fileData.get(), "test_data/1ch/44100/32/test.wav"); + //loader.Load(fileData.get(), "test_data/1ch/44100/64/test.wav"); - // 2-channel wave - //loader.Load(fileData.get(), "test_data/2ch/44100/8/test.wav"); - //loader.Load(fileData.get(), "test_data/2ch/44100/16/test.wav"); - //loader.Load(fileData.get(), "test_data/2ch/44100/24/test.wav"); - //loader.Load(fileData.get(), "test_data/2ch/44100/32/test.wav"); - //loader.Load(fileData.get(), "test_data/2ch/44100/64/test.wav"); + // 2-channel wave + //loader.Load(fileData.get(), "test_data/2ch/44100/8/test.wav"); + //loader.Load(fileData.get(), "test_data/2ch/44100/16/test.wav"); + //loader.Load(fileData.get(), "test_data/2ch/44100/24/test.wav"); + //loader.Load(fileData.get(), "test_data/2ch/44100/32/test.wav"); + //loader.Load(fileData.get(), "test_data/2ch/44100/64/test.wav"); - //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_44_16_mono-ima4-reaper.wav"); - //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_44_16_stereo-ima4-reaper.wav"); + //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_44_16_mono-ima4-reaper.wav"); + //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_44_16_stereo-ima4-reaper.wav"); - // Multi-channel wave - //loader.Load(fileData.get(), "test_data/ad_hoc/6_channel_44k_16b.wav"); + // Multi-channel wave + //loader.Load(fileData.get(), "test_data/ad_hoc/6_channel_44k_16b.wav"); - // 1 + 2 channel ogg - //loader.Load(fileData.get(), "test_data/ad_hoc/LR_Stereo.ogg"); - //loader.Load(fileData.get(), "test_data/ad_hoc/TestLaugh_44k.ogg"); - //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat.ogg"); - //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeatMono.ogg"); - //loader.Load(fileData.get(), "test_data/ad_hoc/BlockWoosh_Stereo.ogg"); + // 1 + 2 channel ogg + //loader.Load(fileData.get(), "test_data/ad_hoc/LR_Stereo.ogg"); + //loader.Load(fileData.get(), "test_data/ad_hoc/TestLaugh_44k.ogg"); + //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat.ogg"); + //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeatMono.ogg"); + //loader.Load(fileData.get(), "test_data/ad_hoc/BlockWoosh_Stereo.ogg"); - // 1 + 2 channel flac - //loader.Load(fileData.get(), "test_data/ad_hoc/KittyPurr8_Stereo_Dithered.flac"); - //loader.Load(fileData.get(), "test_data/ad_hoc/KittyPurr16_Stereo.flac"); - //loader.Load(fileData.get(), "test_data/ad_hoc/KittyPurr16_Mono.flac"); - //loader.Load(fileData.get(), "test_data/ad_hoc/KittyPurr24_Stereo.flac"); + // 1 + 2 channel flac + //loader.Load(fileData.get(), "test_data/ad_hoc/KittyPurr8_Stereo_Dithered.flac"); + //loader.Load(fileData.get(), "test_data/ad_hoc/KittyPurr16_Stereo.flac"); + //loader.Load(fileData.get(), "test_data/ad_hoc/KittyPurr16_Mono.flac"); + //loader.Load(fileData.get(), "test_data/ad_hoc/KittyPurr24_Stereo.flac"); - // Single-channel opus - //loader.Load(fileData.get(), "test_data/ad_hoc/detodos.opus"); // "Firefox: From All, To All" + // Single-channel opus + //loader.Load(fileData.get(), "test_data/ad_hoc/detodos.opus"); // "Firefox: From All, To All" - // 1 + 2 channel wavepack - //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_Float32.wv"); - //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_Float32_Mono.wv"); - //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_Int16.wv"); - //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_Int24.wv"); - //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_Int32.wv"); - //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_Int24_Mono.wv"); - - // 1 + 2 channel musepack - //loader.Load(fileData.get(), "test_data/ad_hoc/44_16_stereo.mpc"); - //loader.Load(fileData.get(), "test_data/ad_hoc/44_16_mono.mpc"); - } + // 1 + 2 channel wavepack + //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_Float32.wv"); + //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_Float32_Mono.wv"); + //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_Int16.wv"); + //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_Int24.wv"); + //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_Int32.wv"); + //loader.Load(fileData.get(), "test_data/ad_hoc/TestBeat_Int24_Mono.wv"); + + // 1 + 2 channel musepack + //loader.Load(fileData.get(), "test_data/ad_hoc/44_16_stereo.mpc"); + //loader.Load(fileData.get(), "test_data/ad_hoc/44_16_mono.mpc"); + } // Test Recording Capabilities of AudioDevice /* @@ -93,7 +121,6 @@ int main(int argc, const char **argv) try fileData->frameSize = 32; fileData->lengthSeconds = 2.0; fileData->sampleRate = 44100; - std::cout << "Starting recording for two seconds..." << std::endl; myDevice.Record(fileData->sampleRate * fileData->lengthSeconds, fileData->samples); */ @@ -126,9 +153,9 @@ int main(int argc, const char **argv) try } fileData->samples = outputBuffer; - int encoderStatus = OggOpusEncoder::WriteFile({1, PCM_FLT, DITHER_NONE}, fileData.get(), "encoded.opus"); - std::cout << "Encoder Status: " << encoderStatus << std::endl; - + int encoderStatus = OggOpusEncoder::WriteFile({ 1, PCM_FLT, DITHER_NONE }, fileData.get(), "encoded.opus"); + std::cout << "Encoder Status: " << encoderStatus << std::endl; + return EXIT_SUCCESS; } catch (const UnsupportedExtensionEx & e) @@ -147,3 +174,8 @@ catch (const std::exception & e) { std::cerr << "Caught: " << e.what() << std::endl; } + +Contact GitHub API Training Shop Blog About + +© 2016 GitHub, Inc.Terms Privacy Security Status Help + diff --git a/src/WavEncoder.cpp b/src/WavEncoder.cpp index 4ec4bae..a56cbd9 100644 --- a/src/WavEncoder.cpp +++ b/src/WavEncoder.cpp @@ -5,11 +5,11 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. +list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -30,21 +30,21 @@ using namespace nqr; static inline void to_bytes(uint8_t value, char * arr) { - arr[0] = (value) & 0xFF; + arr[0] = (value)& 0xFF; } static inline void to_bytes(uint16_t value, char * arr) { - arr[0] = (value) & 0xFF; - arr[1] = (value >> 8) & 0xFF; + arr[0] = (value)& 0xFF; + arr[1] = (value >> 8) & 0xFF; } static inline void to_bytes(uint32_t value, char * arr) { - arr[0] = (value) & 0xFF; - arr[1] = (value >> 8) & 0xFF; - arr[2] = (value >> 16) & 0xFF; - arr[3] = (value >> 24) & 0xFF; + arr[0] = (value)& 0xFF; + arr[1] = (value >> 8) & 0xFF; + arr[2] = (value >> 16) & 0xFF; + arr[3] = (value >> 24) & 0xFF; } //////////////////////////// @@ -53,156 +53,156 @@ static inline void to_bytes(uint32_t value, char * arr) int WavEncoder::WriteFile(const EncoderParams p, const AudioData * d, const std::string & path) { - assert(d->samples.size() > 0); + assert(d->samples.size() > 0); - // Cast away const because we know what we are doing (Hopefully?) - float * sampleData = const_cast(d->samples.data()); - size_t sampleDataSize = d->samples.size(); - - std::vector sampleDataOptionalMix; - - if (sampleDataSize <= 32) - { - return EncoderError::InsufficientSampleData; - } - - if (d->channelCount < 1 || d->channelCount > 8) - { - return EncoderError::UnsupportedChannelConfiguration; - } - - // Handle Channel Mixing -- - - // Mono => Stereo - if (d->channelCount == 1 && p.channelCount == 2) - { - sampleDataOptionalMix.resize(sampleDataSize * 2); - MonoToStereo(sampleData, sampleDataOptionalMix.data(), sampleDataSize); // Mix - - // Re-point data - sampleData = sampleDataOptionalMix.data(); - sampleDataSize = sampleDataOptionalMix.size(); - } - - // Stereo => Mono - else if (d->channelCount == 2 && p.channelCount == 1) - { - sampleDataOptionalMix.resize(sampleDataSize / 2); - StereoToMono(sampleData, sampleDataOptionalMix.data(), sampleDataSize); // Mix - - // Re-point data - sampleData = sampleDataOptionalMix.data(); - sampleDataSize = sampleDataOptionalMix.size(); - - } - - else if (d->channelCount == p.channelCount) - { - // No op - } - - else - { - return EncoderError::UnsupportedChannelMix; - } - // -- End Channel Mixing - - auto maxFileSizeInBytes = std::numeric_limits::max(); - auto samplesSizeInBytes = (sampleDataSize * GetFormatBitsPerSample(p.targetFormat)) / 8; - - // 64 arbitrary - if ((samplesSizeInBytes - 64) >= maxFileSizeInBytes) - { - return EncoderError::BufferTooBig; - } - - // Don't support PC64 or PCDBL - if (GetFormatBitsPerSample(p.targetFormat) > 32) - { - return EncoderError::UnsupportedBitdepth; - } - - std::ofstream fout(path.c_str(), std::ios::out | std::ios::binary); - - if (!fout.is_open()) - { - return EncoderError::FileIOError; - } - - char * chunkSizeBuff = new char[4]; - - // Initial size (this is changed after we're done writing the file) - to_bytes(uint32_t(36), chunkSizeBuff); + // Cast away const because we know what we are doing (Hopefully?) + float * sampleData = const_cast(d->samples.data()); + size_t sampleDataSize = d->samples.size(); - // RIFF file header - fout.write(GenerateChunkCodeChar('R', 'I', 'F', 'F'), 4); - fout.write(chunkSizeBuff, 4); - - fout.write(GenerateChunkCodeChar('W', 'A', 'V', 'E'), 4); - - // Fmt header - auto header = MakeWaveHeader(p, d->sampleRate); - fout.write(reinterpret_cast(&header), sizeof(WaveChunkHeader)); - - auto sourceBits = GetFormatBitsPerSample(d->sourceFormat); - auto targetBits = GetFormatBitsPerSample(p.targetFormat); - - //////////////////////////// - //@todo - channel mixing! // - //////////////////////////// - - // Write out fact chunk - if (p.targetFormat == PCM_FLT) - { - uint32_t four = 4; - uint32_t dataSz = int(sampleDataSize / p.channelCount); - fout.write(GenerateChunkCodeChar('f', 'a', 'c', 't'), 4); - fout.write(reinterpret_cast(&four), 4); - fout.write(reinterpret_cast(&dataSz), 4); // Number of samples (per channel) - } - - // Data header - fout.write(GenerateChunkCodeChar('d', 'a', 't', 'a'), 4); - - // + data chunk size - to_bytes(uint32_t(samplesSizeInBytes), chunkSizeBuff); - fout.write(chunkSizeBuff, 4); - - if (targetBits <= sourceBits && p.targetFormat != PCM_FLT) - { - // At most need this number of bytes in our copy - std::vector samplesCopy(samplesSizeInBytes); - ConvertFromFloat32(samplesCopy.data(), sampleData, sampleDataSize, p.targetFormat, p.dither); - fout.write(reinterpret_cast(samplesCopy.data()), samplesSizeInBytes); - } - else - { - // Handle PCFLT. Coming in from AudioData ensures we start with 32 bits, so we're fine - // since we've also rejected formats with more than 32 bits above. - fout.write(reinterpret_cast(sampleData), samplesSizeInBytes); - } - - // Padding byte - if (isOdd(samplesSizeInBytes)) - { - const char * zero = ""; - fout.write(zero, 1); - } - - // Find size - long totalSize = fout.tellp(); - - // Modify RIFF header - fout.seekp(4); - - // Total size of the file, less 8 bytes for the RIFF header - to_bytes(uint32_t(totalSize - 8), chunkSizeBuff); - - fout.write(chunkSizeBuff, 4); + std::vector sampleDataOptionalMix; - delete[] chunkSizeBuff; - - return EncoderError::NoError; + if (sampleDataSize <= 32) + { + return EncoderError::InsufficientSampleData; + } + + if (d->channelCount < 1 || d->channelCount > 8) + { + return EncoderError::UnsupportedChannelConfiguration; + } + + // Handle Channel Mixing -- + + // Mono => Stereo + if (d->channelCount == 1 && p.channelCount == 2) + { + sampleDataOptionalMix.resize(sampleDataSize * 2); + MonoToStereo(sampleData, sampleDataOptionalMix.data(), sampleDataSize); // Mix + + // Re-point data + sampleData = sampleDataOptionalMix.data(); + sampleDataSize = sampleDataOptionalMix.size(); + } + + // Stereo => Mono + else if (d->channelCount == 2 && p.channelCount == 1) + { + sampleDataOptionalMix.resize(sampleDataSize / 2); + StereoToMono(sampleData, sampleDataOptionalMix.data(), sampleDataSize); // Mix + + // Re-point data + sampleData = sampleDataOptionalMix.data(); + sampleDataSize = sampleDataOptionalMix.size(); + + } + + else if (d->channelCount == p.channelCount) + { + // No op + } + + else + { + return EncoderError::UnsupportedChannelMix; + } + // -- End Channel Mixing + + auto maxFileSizeInBytes = std::numeric_limits::max(); + auto samplesSizeInBytes = (sampleDataSize * GetFormatBitsPerSample(p.targetFormat)) / 8; + + // 64 arbitrary + if ((samplesSizeInBytes - 64) >= maxFileSizeInBytes) + { + return EncoderError::BufferTooBig; + } + + // Don't support PC64 or PCDBL + if (GetFormatBitsPerSample(p.targetFormat) > 32) + { + return EncoderError::UnsupportedBitdepth; + } + + std::ofstream fout(path.c_str(), std::ios::out | std::ios::binary); + + if (!fout.is_open()) + { + return EncoderError::FileIOError; + } + + char * chunkSizeBuff = new char[4]; + + // Initial size (this is changed after we're done writing the file) + to_bytes(uint32_t(36), chunkSizeBuff); + + // RIFF file header + fout.write(GenerateChunkCodeChar('R', 'I', 'F', 'F'), 4); + fout.write(chunkSizeBuff, 4); + + fout.write(GenerateChunkCodeChar('W', 'A', 'V', 'E'), 4); + + // Fmt header + auto header = MakeWaveHeader(p, d->sampleRate); + fout.write(reinterpret_cast(&header), sizeof(WaveChunkHeader)); + + auto sourceBits = GetFormatBitsPerSample(d->sourceFormat); + auto targetBits = GetFormatBitsPerSample(p.targetFormat); + + //////////////////////////// + //@todo - channel mixing! // + //////////////////////////// + + // Write out fact chunk + if (p.targetFormat == PCM_FLT) + { + uint32_t four = 4; + uint32_t dataSz = int(sampleDataSize / p.channelCount); + fout.write(GenerateChunkCodeChar('f', 'a', 'c', 't'), 4); + fout.write(reinterpret_cast(&four), 4); + fout.write(reinterpret_cast(&dataSz), 4); // Number of samples (per channel) + } + + // Data header + fout.write(GenerateChunkCodeChar('d', 'a', 't', 'a'), 4); + + // + data chunk size + to_bytes(uint32_t(samplesSizeInBytes), chunkSizeBuff); + fout.write(chunkSizeBuff, 4); + + if (targetBits <= sourceBits && p.targetFormat != PCM_FLT) + { + // At most need this number of bytes in our copy + std::vector samplesCopy(samplesSizeInBytes); + ConvertFromFloat32(samplesCopy.data(), sampleData, sampleDataSize, p.targetFormat, p.dither); + fout.write(reinterpret_cast(samplesCopy.data()), samplesSizeInBytes); + } + else + { + // Handle PCFLT. Coming in from AudioData ensures we start with 32 bits, so we're fine + // since we've also rejected formats with more than 32 bits above. + fout.write(reinterpret_cast(sampleData), samplesSizeInBytes); + } + + // Padding byte + if (isOdd(samplesSizeInBytes)) + { + const char * zero = ""; + fout.write(zero, 1); + } + + // Find size + long totalSize = fout.tellp(); + + // Modify RIFF header + fout.seekp(4); + + // Total size of the file, less 8 bytes for the RIFF header + to_bytes(uint32_t(totalSize - 8), chunkSizeBuff); + + fout.write(chunkSizeBuff, 4); + + delete[] chunkSizeBuff; + + return EncoderError::NoError; } //////////////////////////// @@ -216,178 +216,178 @@ typedef std::pair metadata_t; class OggWriter { - void write_to_ostream(bool flush) - { - while (ogg_stream_pageout(&oss, &page)) - { - ostream->write(reinterpret_cast(page.header), static_cast(page.header_len)); - ostream->write(reinterpret_cast(page.body), static_cast(page.body_len)); - } - if (flush && ogg_stream_flush(&oss, &page)) - { - ostream->write(reinterpret_cast(page.header), static_cast(page.header_len)); - ostream->write(reinterpret_cast(page.body), static_cast(page.body_len)); - } - } + void write_to_ostream(bool flush) + { + while (ogg_stream_pageout(&oss, &page)) + { + ostream->write(reinterpret_cast(page.header), static_cast(page.header_len)); + ostream->write(reinterpret_cast(page.body), static_cast(page.body_len)); + } + if (flush && ogg_stream_flush(&oss, &page)) + { + ostream->write(reinterpret_cast(page.header), static_cast(page.header_len)); + ostream->write(reinterpret_cast(page.body), static_cast(page.body_len)); + } + } - std::vector make_header(int channel_count, int preskip, long sample_rate, int gain) - { - std::vector header; - - std::array steam_count = {{ 0x0, 0x1, 0x1, 0x2, 0x2, 0x3, 0x4, 0x5, 0x5 }}; - std::array coupled_streacount = {{ 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x3 }}; - - std::array chan_map_1 = {{ 0x0 }}; - std::array chan_map_2 = {{ 0x0, 0x1 }}; + std::vector make_header(int channel_count, int preskip, long sample_rate, int gain) + { + std::vector header; - std::array _preamble = {{ 'O', 'p', 'u', 's', 'H', 'e', 'a', 'd', 0x1 }}; - std::array _channel_count; - std::array _preskip; - std::array _sample_rate; - std::array _gain; - std::array _channel_family = {{ 0x1 }}; + std::array steam_count = { { 0x0, 0x1, 0x1, 0x2, 0x2, 0x3, 0x4, 0x5, 0x5 } }; + std::array coupled_streacount = { { 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x3 } }; - to_bytes(uint8_t(channel_count), _channel_count.data()); - to_bytes(uint16_t(preskip), _preskip.data()); - to_bytes(uint32_t(sample_rate), _sample_rate.data()); - to_bytes(uint16_t(gain), _gain.data()); + std::array chan_map_1 = { { 0x0 } }; + std::array chan_map_2 = { { 0x0, 0x1 } }; - header.insert(header.end(), _preamble.cbegin(), _preamble.cend()); - header.insert(header.end(), _channel_count.cbegin(), _channel_count.cend()); - header.insert(header.end(), _preskip.cbegin(), _preskip.cend()); - header.insert(header.end(), _sample_rate.cbegin(), _sample_rate.cend()); - header.insert(header.end(), _gain.cbegin(), _gain.cend()); - header.insert(header.end(), _channel_family.cbegin(), _channel_family.cend()); - header.push_back(steam_count[channel_count]); - header.push_back(coupled_streacount[channel_count]); + std::array _preamble = { { 'O', 'p', 'u', 's', 'H', 'e', 'a', 'd', 0x1 } }; + std::array _channel_count; + std::array _preskip; + std::array _sample_rate; + std::array _gain; + std::array _channel_family = { { 0x1 } }; - switch (channel_count) - { - case 1: header.insert(header.end(), chan_map_1.cbegin(), chan_map_1.cend()); break; - case 2: header.insert(header.end(), chan_map_2.cbegin(), chan_map_2.cend()); break; - default: throw std::runtime_error("couldn't map channel data"); - } - - return header; - } - - std::vector make_tags(const std::string & vendor, const std::vector & metadata) - { - std::vector tags; + to_bytes(uint8_t(channel_count), _channel_count.data()); + to_bytes(uint16_t(preskip), _preskip.data()); + to_bytes(uint32_t(sample_rate), _sample_rate.data()); + to_bytes(uint16_t(gain), _gain.data()); - std::array _preamble = {{ 'O', 'p', 'u', 's', 'T', 'a', 'g', 's' }}; - std::array _vendor_length; - std::array _metadata_count; + header.insert(header.end(), _preamble.cbegin(), _preamble.cend()); + header.insert(header.end(), _channel_count.cbegin(), _channel_count.cend()); + header.insert(header.end(), _preskip.cbegin(), _preskip.cend()); + header.insert(header.end(), _sample_rate.cbegin(), _sample_rate.cend()); + header.insert(header.end(), _gain.cbegin(), _gain.cend()); + header.insert(header.end(), _channel_family.cbegin(), _channel_family.cend()); + header.push_back(steam_count[channel_count]); + header.push_back(coupled_streacount[channel_count]); - to_bytes(uint32_t(vendor.size()), _vendor_length.data()); - to_bytes(uint32_t(metadata.size()), _metadata_count.data()); + switch (channel_count) + { + case 1: header.insert(header.end(), chan_map_1.cbegin(), chan_map_1.cend()); break; + case 2: header.insert(header.end(), chan_map_2.cbegin(), chan_map_2.cend()); break; + default: throw std::runtime_error("couldn't map channel data"); + } - tags.insert(tags.end(), _preamble.cbegin() , _preamble.cend()); - tags.insert(tags.end(), _vendor_length.cbegin() , _vendor_length.cend()); - tags.insert(tags.end(), vendor.cbegin() , vendor.cend()); - tags.insert(tags.end(), _metadata_count.cbegin() , _metadata_count.cend()); + return header; + } - // Process metadata. - for (const auto & metadata_entry : metadata) - { - std::array _metadata_entry_length; - std::string entry = metadata_entry.first + "=" + metadata_entry.second; - to_bytes(uint32_t(entry.size()), _metadata_entry_length.data()); - tags.insert(tags.end(), _metadata_entry_length.cbegin(), _metadata_entry_length.cend()); - tags.insert(tags.end(), entry.cbegin() , entry.cend()); - } + std::vector make_tags(const std::string & vendor, const std::vector & metadata) + { + std::vector tags; - return tags; - } + std::array _preamble = { { 'O', 'p', 'u', 's', 'T', 'a', 'g', 's' } }; + std::array _vendor_length; + std::array _metadata_count; - ogg_int64_t packet_number; - ogg_int64_t granule; - ogg_page page; - ogg_stream_state oss; + to_bytes(uint32_t(vendor.size()), _vendor_length.data()); + to_bytes(uint32_t(metadata.size()), _metadata_count.data()); - int channel_count; - long sample_rate; - int bits_per_sample; - std::ofstream * ostream; - std::string description; - std::vector metadata; + tags.insert(tags.end(), _preamble.cbegin(), _preamble.cend()); + tags.insert(tags.end(), _vendor_length.cbegin(), _vendor_length.cend()); + tags.insert(tags.end(), vendor.cbegin(), vendor.cend()); + tags.insert(tags.end(), _metadata_count.cbegin(), _metadata_count.cend()); + + // Process metadata. + for (const auto & metadata_entry : metadata) + { + std::array _metadata_entry_length; + std::string entry = metadata_entry.first + "=" + metadata_entry.second; + to_bytes(uint32_t(entry.size()), _metadata_entry_length.data()); + tags.insert(tags.end(), _metadata_entry_length.cbegin(), _metadata_entry_length.cend()); + tags.insert(tags.end(), entry.cbegin(), entry.cend()); + } + + return tags; + } + + ogg_int64_t packet_number; + ogg_int64_t granule; + ogg_page page; + ogg_stream_state oss; + + int channel_count; + long sample_rate; + int bits_per_sample; + std::ofstream * ostream; + std::string description; + std::vector metadata; public: - OggWriter(int channel_count, long sample_rate, int bits_per_sample, std::ofstream & stream, const std::vector & md) - { - this->channel_count = channel_count; - this->sample_rate = sample_rate; - this->bits_per_sample = bits_per_sample; - this-> ostream = &stream; - this->metadata = md; + OggWriter(int channel_count, long sample_rate, int bits_per_sample, std::ofstream & stream, const std::vector & md) + { + this->channel_count = channel_count; + this->sample_rate = sample_rate; + this->bits_per_sample = bits_per_sample; + this->ostream = &stream; + this->metadata = md; - int oggInitErr, packetErr; + int oggInitErr, packetErr; - ogg_packet header_packet; - ogg_packet tags_packet; - std::vector header_vector; - std::vector tags_vector; - - description = "Ogg"; - packet_number = 0; - granule = 0; + ogg_packet header_packet; + ogg_packet tags_packet; + std::vector header_vector; + std::vector tags_vector; - // Validate parameters - if (channel_count < 1 && channel_count > 255) throw std::runtime_error("Channel count must be between 1 and 255."); + description = "Ogg"; + packet_number = 0; + granule = 0; - // Initialize the Ogg stream. + // Validate parameters + if (channel_count < 1 && channel_count > 255) throw std::runtime_error("Channel count must be between 1 and 255."); + + // Initialize the Ogg stream. oggInitErr = ogg_stream_init(&oss, 12345); - if (oggInitErr) throw std::runtime_error("Could not initialize the Ogg stream state."); + if (oggInitErr) throw std::runtime_error("Could not initialize the Ogg stream state."); - // Initialize the header packet. - header_vector = make_header(channel_count, 3840, sample_rate, 0); - header_packet.packet = reinterpret_cast(header_vector.data()); - header_packet.bytes = header_vector.size(); - header_packet.b_o_s = 1; - header_packet.e_o_s = 0; - header_packet.granulepos = 0; - header_packet.packetno = packet_number++; + // Initialize the header packet. + header_vector = make_header(channel_count, 3840, sample_rate, 0); + header_packet.packet = reinterpret_cast(header_vector.data()); + header_packet.bytes = header_vector.size(); + header_packet.b_o_s = 1; + header_packet.e_o_s = 0; + header_packet.granulepos = 0; + header_packet.packetno = packet_number++; - // Initialize tags - tags_vector = make_tags("libnyquist", metadata); - tags_packet.packet = reinterpret_cast(tags_vector.data()); - tags_packet.bytes = tags_vector.size(); - tags_packet.b_o_s = 0; - tags_packet.e_o_s = 0; - tags_packet.granulepos = 0; - tags_packet.packetno = packet_number++; - - // Write header page + // Initialize tags + tags_vector = make_tags("libnyquist", metadata); + tags_packet.packet = reinterpret_cast(tags_vector.data()); + tags_packet.bytes = tags_vector.size(); + tags_packet.b_o_s = 0; + tags_packet.e_o_s = 0; + tags_packet.granulepos = 0; + tags_packet.packetno = packet_number++; + + // Write header page packetErr = ogg_stream_packetin(&oss, &header_packet); - if (packetErr) throw std::runtime_error("Could not write header packet to the Ogg stream."); - write_to_ostream(true); - - // Write tags page + if (packetErr) throw std::runtime_error("Could not write header packet to the Ogg stream."); + write_to_ostream(true); + + // Write tags page packetErr = ogg_stream_packetin(&oss, &tags_packet); - if (packetErr) throw std::runtime_error("Could not write tags packet to the Ogg stream."); - write_to_ostream(true); - } + if (packetErr) throw std::runtime_error("Could not write tags packet to the Ogg stream."); + write_to_ostream(true); + } - ~OggWriter() - { - write_to_ostream(true); - ogg_stream_clear(&oss); - } + ~OggWriter() + { + write_to_ostream(true); + ogg_stream_clear(&oss); + } - bool write(char * data, std::streamsize length, size_t sampleCount, bool end) + bool write(char * data, std::streamsize length, size_t sampleCount, bool end) { int err; ogg_packet packet; granule += sampleCount; - packet.packet = reinterpret_cast(data); - packet.bytes = static_cast(length); - packet.b_o_s = 0; - packet.e_o_s = end ? 1 : 0; + packet.packet = reinterpret_cast(data); + packet.bytes = static_cast(length); + packet.b_o_s = 0; + packet.e_o_s = end ? 1 : 0; packet.granulepos = granule; - packet.packetno = packet_number++; + packet.packetno = packet_number++; err = ogg_stream_packetin(&oss, &packet); @@ -407,30 +407,30 @@ public: // This encoder only supports mono for the time being int OggOpusEncoder::WriteFile(const EncoderParams p, const AudioData * d, const std::string & path) { - assert(d->samples.size() > 0); + assert(d->samples.size() > 0); //assert(d->sampleRate == 48000); - + float * sampleData = const_cast(d->samples.data()); const size_t sampleDataSize = d->samples.size(); int opus_error; - OpusEncoder * enc; - enc = opus_encoder_create(48000, 1, OPUS_APPLICATION_AUDIO, &opus_error); + OpusEncoder * enc; + enc = opus_encoder_create(48000, 1, OPUS_APPLICATION_AUDIO, &opus_error); if (!enc) throw std::runtime_error("opus_encoder_create caused an error!"); std::ofstream fout(path.c_str(), std::ios::out | std::ios::binary); - std::vector oggMetadata = {{"artist", "dimitri"}}; + std::vector oggMetadata = { { "artist", "dimitri" } }; OggWriter writer(d->channelCount, d->sampleRate, GetFormatBitsPerSample(d->sourceFormat), fout, oggMetadata); - std::vector outBuffer(OPUS_MAX_PACKET_SIZE); - + std::vector outBuffer(OPUS_MAX_PACKET_SIZE); + int framesToEncode = (sampleDataSize / OPUS_FRAME_SIZE) - 1; // fixme while (framesToEncode >= 0) - { + { auto encoded_size = opus_encode_float(enc, sampleData, OPUS_FRAME_SIZE, outBuffer.data(), OPUS_MAX_PACKET_SIZE); - + if (encoded_size < 0) { std::cerr << "Bad Opus Status: " << encoded_size << std::endl; @@ -447,7 +447,7 @@ int OggOpusEncoder::WriteFile(const EncoderParams p, const AudioData * d, const opus_encoder_destroy(enc); - return EncoderError::NoError; + return EncoderError::NoError; } #undef OPUS_MAX_PACKET_SIZE \ No newline at end of file