space normalization

pull/18/head
Dimitri Diakopoulos 10 years ago
parent ce56c943b9
commit 97c2b2cd56

@ -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 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 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,8 +153,8 @@ 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;
}
@ -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

@ -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<float *>(d->samples.data());
size_t sampleDataSize = d->samples.size();
// Cast away const because we know what we are doing (Hopefully?)
float * sampleData = const_cast<float *>(d->samples.data());
size_t sampleDataSize = d->samples.size();
std::vector<float> sampleDataOptionalMix;
std::vector<float> sampleDataOptionalMix;
if (sampleDataSize <= 32)
{
return EncoderError::InsufficientSampleData;
}
if (sampleDataSize <= 32)
{
return EncoderError::InsufficientSampleData;
}
if (d->channelCount < 1 || d->channelCount > 8)
{
return EncoderError::UnsupportedChannelConfiguration;
}
if (d->channelCount < 1 || d->channelCount > 8)
{
return EncoderError::UnsupportedChannelConfiguration;
}
// Handle Channel Mixing --
// Handle Channel Mixing --
// Mono => Stereo
if (d->channelCount == 1 && p.channelCount == 2)
{
sampleDataOptionalMix.resize(sampleDataSize * 2);
MonoToStereo(sampleData, sampleDataOptionalMix.data(), sampleDataSize); // Mix
// 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();
}
// 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
// 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();
// Re-point data
sampleData = sampleDataOptionalMix.data();
sampleDataSize = sampleDataOptionalMix.size();
}
}
else if (d->channelCount == p.channelCount)
{
// No op
}
else if (d->channelCount == p.channelCount)
{
// No op
}
else
{
return EncoderError::UnsupportedChannelMix;
}
// -- End Channel Mixing
else
{
return EncoderError::UnsupportedChannelMix;
}
// -- End Channel Mixing
auto maxFileSizeInBytes = std::numeric_limits<uint32_t>::max();
auto samplesSizeInBytes = (sampleDataSize * GetFormatBitsPerSample(p.targetFormat)) / 8;
auto maxFileSizeInBytes = std::numeric_limits<uint32_t>::max();
auto samplesSizeInBytes = (sampleDataSize * GetFormatBitsPerSample(p.targetFormat)) / 8;
// 64 arbitrary
if ((samplesSizeInBytes - 64) >= maxFileSizeInBytes)
{
return EncoderError::BufferTooBig;
}
// 64 arbitrary
if ((samplesSizeInBytes - 64) >= maxFileSizeInBytes)
{
return EncoderError::BufferTooBig;
}
// Don't support PC64 or PCDBL
if (GetFormatBitsPerSample(p.targetFormat) > 32)
{
return EncoderError::UnsupportedBitdepth;
}
// 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);
std::ofstream fout(path.c_str(), std::ios::out | std::ios::binary);
if (!fout.is_open())
{
return EncoderError::FileIOError;
}
if (!fout.is_open())
{
return EncoderError::FileIOError;
}
char * chunkSizeBuff = new char[4];
char * chunkSizeBuff = new char[4];
// Initial size (this is changed after we're done writing the file)
to_bytes(uint32_t(36), chunkSizeBuff);
// 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);
// RIFF file header
fout.write(GenerateChunkCodeChar('R', 'I', 'F', 'F'), 4);
fout.write(chunkSizeBuff, 4);
fout.write(GenerateChunkCodeChar('W', 'A', 'V', 'E'), 4);
fout.write(GenerateChunkCodeChar('W', 'A', 'V', 'E'), 4);
// Fmt header
auto header = MakeWaveHeader(p, d->sampleRate);
fout.write(reinterpret_cast<char*>(&header), sizeof(WaveChunkHeader));
// Fmt header
auto header = MakeWaveHeader(p, d->sampleRate);
fout.write(reinterpret_cast<char*>(&header), sizeof(WaveChunkHeader));
auto sourceBits = GetFormatBitsPerSample(d->sourceFormat);
auto targetBits = GetFormatBitsPerSample(p.targetFormat);
auto sourceBits = GetFormatBitsPerSample(d->sourceFormat);
auto targetBits = GetFormatBitsPerSample(p.targetFormat);
////////////////////////////
//@todo - channel mixing! //
////////////////////////////
////////////////////////////
//@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<const char *>(&four), 4);
fout.write(reinterpret_cast<const char *>(&dataSz), 4); // Number of samples (per channel)
}
// 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<const char *>(&four), 4);
fout.write(reinterpret_cast<const char *>(&dataSz), 4); // Number of samples (per channel)
}
// Data header
fout.write(GenerateChunkCodeChar('d', 'a', 't', 'a'), 4);
// Data header
fout.write(GenerateChunkCodeChar('d', 'a', 't', 'a'), 4);
// + data chunk size
to_bytes(uint32_t(samplesSizeInBytes), chunkSizeBuff);
fout.write(chunkSizeBuff, 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<uint8_t> samplesCopy(samplesSizeInBytes);
ConvertFromFloat32(samplesCopy.data(), sampleData, sampleDataSize, p.targetFormat, p.dither);
fout.write(reinterpret_cast<const char*>(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<const char*>(sampleData), samplesSizeInBytes);
}
if (targetBits <= sourceBits && p.targetFormat != PCM_FLT)
{
// At most need this number of bytes in our copy
std::vector<uint8_t> samplesCopy(samplesSizeInBytes);
ConvertFromFloat32(samplesCopy.data(), sampleData, sampleDataSize, p.targetFormat, p.dither);
fout.write(reinterpret_cast<const char*>(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<const char*>(sampleData), samplesSizeInBytes);
}
// Padding byte
if (isOdd(samplesSizeInBytes))
{
const char * zero = "";
fout.write(zero, 1);
}
// Padding byte
if (isOdd(samplesSizeInBytes))
{
const char * zero = "";
fout.write(zero, 1);
}
// Find size
long totalSize = fout.tellp();
// Find size
long totalSize = fout.tellp();
// Modify RIFF header
fout.seekp(4);
// 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);
// Total size of the file, less 8 bytes for the RIFF header
to_bytes(uint32_t(totalSize - 8), chunkSizeBuff);
fout.write(chunkSizeBuff, 4);
fout.write(chunkSizeBuff, 4);
delete[] chunkSizeBuff;
delete[] chunkSizeBuff;
return EncoderError::NoError;
return EncoderError::NoError;
}
////////////////////////////
@ -216,178 +216,178 @@ typedef std::pair<std::string, std::string> metadata_t;
class OggWriter
{
void write_to_ostream(bool flush)
{
while (ogg_stream_pageout(&oss, &page))
{
ostream->write(reinterpret_cast<char*>(page.header), static_cast<std::streamsize>(page.header_len));
ostream->write(reinterpret_cast<char*>(page.body), static_cast<std::streamsize>(page.body_len));
}
if (flush && ogg_stream_flush(&oss, &page))
{
ostream->write(reinterpret_cast<char*>(page.header), static_cast<std::streamsize>(page.header_len));
ostream->write(reinterpret_cast<char*>(page.body), static_cast<std::streamsize>(page.body_len));
}
}
void write_to_ostream(bool flush)
{
while (ogg_stream_pageout(&oss, &page))
{
ostream->write(reinterpret_cast<char*>(page.header), static_cast<std::streamsize>(page.header_len));
ostream->write(reinterpret_cast<char*>(page.body), static_cast<std::streamsize>(page.body_len));
}
if (flush && ogg_stream_flush(&oss, &page))
{
ostream->write(reinterpret_cast<char*>(page.header), static_cast<std::streamsize>(page.header_len));
ostream->write(reinterpret_cast<char*>(page.body), static_cast<std::streamsize>(page.body_len));
}
}
std::vector<char> make_header(int channel_count, int preskip, long sample_rate, int gain)
{
std::vector<char> header;
std::vector<char> make_header(int channel_count, int preskip, long sample_rate, int gain)
{
std::vector<char> header;
std::array<char, 9> steam_count = {{ 0x0, 0x1, 0x1, 0x2, 0x2, 0x3, 0x4, 0x5, 0x5 }};
std::array<char, 9> coupled_streacount = {{ 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x3 }};
std::array<char, 9> steam_count = { { 0x0, 0x1, 0x1, 0x2, 0x2, 0x3, 0x4, 0x5, 0x5 } };
std::array<char, 9> coupled_streacount = { { 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x3 } };
std::array<char, 1> chan_map_1 = {{ 0x0 }};
std::array<char, 2> chan_map_2 = {{ 0x0, 0x1 }};
std::array<char, 1> chan_map_1 = { { 0x0 } };
std::array<char, 2> chan_map_2 = { { 0x0, 0x1 } };
std::array<char, 9> _preamble = {{ 'O', 'p', 'u', 's', 'H', 'e', 'a', 'd', 0x1 }};
std::array<char, 1> _channel_count;
std::array<char, 2> _preskip;
std::array<char, 4> _sample_rate;
std::array<char, 2> _gain;
std::array<char, 1> _channel_family = {{ 0x1 }};
std::array<char, 9> _preamble = { { 'O', 'p', 'u', 's', 'H', 'e', 'a', 'd', 0x1 } };
std::array<char, 1> _channel_count;
std::array<char, 2> _preskip;
std::array<char, 4> _sample_rate;
std::array<char, 2> _gain;
std::array<char, 1> _channel_family = { { 0x1 } };
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());
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());
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]);
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]);
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");
}
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;
}
return header;
}
std::vector<char> make_tags(const std::string & vendor, const std::vector<metadata_t> & metadata)
{
std::vector<char> tags;
std::vector<char> make_tags(const std::string & vendor, const std::vector<metadata_t> & metadata)
{
std::vector<char> tags;
std::array<char, 8> _preamble = {{ 'O', 'p', 'u', 's', 'T', 'a', 'g', 's' }};
std::array<char, 4> _vendor_length;
std::array<char, 4> _metadata_count;
std::array<char, 8> _preamble = { { 'O', 'p', 'u', 's', 'T', 'a', 'g', 's' } };
std::array<char, 4> _vendor_length;
std::array<char, 4> _metadata_count;
to_bytes(uint32_t(vendor.size()), _vendor_length.data());
to_bytes(uint32_t(metadata.size()), _metadata_count.data());
to_bytes(uint32_t(vendor.size()), _vendor_length.data());
to_bytes(uint32_t(metadata.size()), _metadata_count.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());
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<char, 4> _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());
}
// Process metadata.
for (const auto & metadata_entry : metadata)
{
std::array<char, 4> _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;
}
return tags;
}
ogg_int64_t packet_number;
ogg_int64_t granule;
ogg_page page;
ogg_stream_state oss;
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_t> metadata;
int channel_count;
long sample_rate;
int bits_per_sample;
std::ofstream * ostream;
std::string description;
std::vector<metadata_t> metadata;
public:
OggWriter(int channel_count, long sample_rate, int bits_per_sample, std::ofstream & stream, const std::vector<metadata_t> & 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<metadata_t> & 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<char> header_vector;
std::vector<char> tags_vector;
ogg_packet header_packet;
ogg_packet tags_packet;
std::vector<char> header_vector;
std::vector<char> tags_vector;
description = "Ogg";
packet_number = 0;
granule = 0;
description = "Ogg";
packet_number = 0;
granule = 0;
// Validate parameters
if (channel_count < 1 && channel_count > 255) throw std::runtime_error("Channel count must be between 1 and 255.");
// 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.
// 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<unsigned char*>(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<unsigned char*>(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<unsigned char*>(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++;
// Initialize tags
tags_vector = make_tags("libnyquist", metadata);
tags_packet.packet = reinterpret_cast<unsigned char*>(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
// 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);
if (packetErr) throw std::runtime_error("Could not write header packet to the Ogg stream.");
write_to_ostream(true);
// Write tags page
// 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<unsigned char*>(data);
packet.bytes = static_cast<long>(length);
packet.b_o_s = 0;
packet.e_o_s = end ? 1 : 0;
packet.packet = reinterpret_cast<unsigned char*>(data);
packet.bytes = static_cast<long>(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,23 +407,23 @@ 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<float *>(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<metadata_t> oggMetadata = {{"artist", "dimitri"}};
std::vector<metadata_t> oggMetadata = { { "artist", "dimitri" } };
OggWriter writer(d->channelCount, d->sampleRate, GetFormatBitsPerSample(d->sourceFormat), fout, oggMetadata);
std::vector<uint8_t> outBuffer(OPUS_MAX_PACKET_SIZE);
std::vector<uint8_t> outBuffer(OPUS_MAX_PACKET_SIZE);
int framesToEncode = (sampleDataSize / OPUS_FRAME_SIZE) - 1; // fixme
@ -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
Loading…
Cancel
Save