1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

WavAudioFormat: Add support for writing 32 bit integral (PCM) format

This commit is contained in:
attila 2025-05-09 14:53:09 +02:00 committed by Attila Szarvas
parent 34675235e5
commit 386daafe23
43 changed files with 677 additions and 125 deletions

View file

@ -525,6 +525,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriterOptions.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.h"
"../../../../../modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp"
@ -3184,6 +3185,7 @@ set_source_files_properties(
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriterOptions.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.h"
"../../../../../modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp"

View file

@ -3558,6 +3558,7 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReaderSource.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_BufferingAudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_MemoryMappedAudioFormatReader.h"/>

View file

@ -4974,6 +4974,9 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>

View file

@ -3558,6 +3558,7 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReaderSource.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_BufferingAudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_MemoryMappedAudioFormatReader.h"/>

View file

@ -4974,6 +4974,9 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>

View file

@ -480,6 +480,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriterOptions.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.h"
"../../../../../modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp"
@ -2753,6 +2754,7 @@ set_source_files_properties(
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriterOptions.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.h"
"../../../../../modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp"

View file

@ -3072,6 +3072,7 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReaderSource.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_BufferingAudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_MemoryMappedAudioFormatReader.h"/>

View file

@ -4236,6 +4236,9 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>

View file

@ -513,6 +513,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriterOptions.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.h"
"../../../../../modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp"
@ -2939,6 +2940,7 @@ set_source_files_properties(
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriterOptions.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.h"
"../../../../../modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp"

View file

@ -3252,6 +3252,7 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReaderSource.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_BufferingAudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_MemoryMappedAudioFormatReader.h"/>

View file

@ -4512,6 +4512,9 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>

View file

@ -3252,6 +3252,7 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReaderSource.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_BufferingAudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_MemoryMappedAudioFormatReader.h"/>

View file

@ -4512,6 +4512,9 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>

View file

@ -484,6 +484,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriterOptions.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.h"
"../../../../../modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp"
@ -2837,6 +2838,7 @@ set_source_files_properties(
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriter.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioFormatWriterOptions.h"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp"
"../../../../../modules/juce_audio_formats/format/juce_AudioSubsectionReader.h"
"../../../../../modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp"

View file

@ -3163,6 +3163,7 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReaderSource.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_BufferingAudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_MemoryMappedAudioFormatReader.h"/>

View file

@ -4377,6 +4377,9 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>

View file

@ -3364,6 +3364,7 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReaderSource.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_BufferingAudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_MemoryMappedAudioFormatReader.h"/>

View file

@ -4671,6 +4671,9 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>

View file

@ -3364,6 +3364,7 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReaderSource.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_BufferingAudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_MemoryMappedAudioFormatReader.h"/>

View file

@ -4671,6 +4671,9 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>

View file

@ -3139,6 +3139,7 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatReaderSource.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_BufferingAudioFormatReader.h"/>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_MemoryMappedAudioFormatReader.h"/>

View file

@ -4344,6 +4344,9 @@
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriter.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioFormatWriterOptions.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_audio_formats\format\juce_AudioSubsectionReader.h">
<Filter>JUCE Modules\juce_audio_formats\format</Filter>
</ClInclude>

View file

@ -1031,4 +1031,20 @@ AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out,
return nullptr;
}
std::unique_ptr<AudioFormatWriter> AiffAudioFormat::createWriterFor (std::unique_ptr<OutputStream>& streamToWriteTo,
const AudioFormatWriterOptions& options)
{
if (streamToWriteTo == nullptr || ! getPossibleBitDepths().contains (options.getBitsPerSample()))
return nullptr;
StringPairArray metadata;
metadata.addUnorderedMap (options.getMetadataValues());
return std::make_unique<AiffAudioFormatWriter> (std::exchange (streamToWriteTo, {}).release(),
options.getSampleRate(),
(unsigned int) options.getNumChannels(),
(unsigned int) options.getBitsPerSample(),
metadata);
}
} // namespace juce

View file

@ -94,6 +94,10 @@ public:
int bitsPerSample,
const StringPairArray& metadataValues,
int qualityOptionIndex) override;
std::unique_ptr<AudioFormatWriter> createWriterFor (std::unique_ptr<OutputStream>& streamToWriteTo,
const AudioFormatWriterOptions& options) override;
using AudioFormat::createWriterFor;
private:

View file

@ -665,6 +665,13 @@ AudioFormatWriter* CoreAudioFormat::createWriterFor (OutputStream*,
return nullptr;
}
std::unique_ptr<AudioFormatWriter> CoreAudioFormat::createWriterFor (std::unique_ptr<OutputStream>&,
const AudioFormatWriterOptions&)
{
jassertfalse; // not yet implemented!
return nullptr;
}
//==============================================================================
//==============================================================================

View file

@ -112,6 +112,10 @@ public:
int bitsPerSample,
const StringPairArray& metadataValues,
int qualityOptionIndex) override;
std::unique_ptr<AudioFormatWriter> createWriterFor (std::unique_ptr<OutputStream>& streamToWriteTo,
const AudioFormatWriterOptions& options) override;
using AudioFormat::createWriterFor;
private:

View file

@ -610,7 +610,7 @@ AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out,
if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample))
{
std::unique_ptr<FlacWriter> w (new FlacWriter (out, sampleRate, numberOfChannels,
(uint32) bitsPerSample, qualityOptionIndex));
(uint32) bitsPerSample, qualityOptionIndex));
if (w->ok)
return w.release();
}
@ -618,6 +618,24 @@ AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out,
return nullptr;
}
std::unique_ptr<AudioFormatWriter> FlacAudioFormat::createWriterFor (std::unique_ptr<OutputStream>& streamToWriteTo,
const AudioFormatWriterOptions& options)
{
if (streamToWriteTo == nullptr || ! getPossibleBitDepths().contains (options.getBitsPerSample()))
return nullptr;
auto writer = std::make_unique<FlacWriter> (std::exchange (streamToWriteTo, {}).release(),
options.getSampleRate(),
(uint32) options.getNumChannels(),
(uint32) options.getBitsPerSample(),
options.getQualityOptionIndex());
if (! writer->ok)
return nullptr;
return writer;
}
StringArray FlacAudioFormat::getQualityOptions()
{
return { "0 (Fastest)", "1", "2", "3", "4", "5 (Default)","6", "7", "8 (Highest quality)" };

View file

@ -63,6 +63,9 @@ public:
StringArray getQualityOptions() override;
//==============================================================================
std::unique_ptr<AudioFormatWriter> createWriterFor (std::unique_ptr<OutputStream>& streamToWriteTo,
const AudioFormatWriterOptions& options) override;
AudioFormatReader* createReaderFor (InputStream* sourceStream,
bool deleteStreamIfOpeningFails) override;
@ -72,6 +75,7 @@ public:
int bitsPerSample,
const StringPairArray& metadataValues,
int qualityOptionIndex) override;
using AudioFormat::createWriterFor;
private:

View file

@ -50,36 +50,39 @@ public:
{
WavAudioFormat wavFormat;
if (auto out = tempWav.getFile().createOutputStream())
writer = wavFormat.createWriterFor (tempWav.getFile().createOutputStream(),
AudioFormatWriter::Options{}.withSampleRate (sampleRateIn)
.withNumChannels ((int) numberOfChannels)
.withBitsPerSample (bitsPerSampleIn)
.withMetadataValues (metadata));
if (writer == nullptr)
return;
args.add (appFile.getFullPathName());
args.add ("--quiet");
if (cbrBitrate == 0)
{
writer.reset (wavFormat.createWriterFor (out.release(), sampleRateIn, numChannels,
bitsPerSampleIn, metadata, 0));
args.add (appFile.getFullPathName());
args.add ("--quiet");
if (cbrBitrate == 0)
{
args.add ("--vbr-new");
args.add ("-V");
args.add (String (vbrLevel));
}
else
{
args.add ("--cbr");
args.add ("-b");
args.add (String (cbrBitrate));
}
addMetadataArg (metadata, "id3title", "--tt");
addMetadataArg (metadata, "id3artist", "--ta");
addMetadataArg (metadata, "id3album", "--tl");
addMetadataArg (metadata, "id3comment", "--tc");
addMetadataArg (metadata, "id3date", "--ty");
addMetadataArg (metadata, "id3genre", "--tg");
addMetadataArg (metadata, "id3trackNumber", "--tn");
args.add ("--vbr-new");
args.add ("-V");
args.add (String (vbrLevel));
}
else
{
args.add ("--cbr");
args.add ("-b");
args.add (String (cbrBitrate));
}
addMetadataArg (metadata, "id3title", "--tt");
addMetadataArg (metadata, "id3artist", "--ta");
addMetadataArg (metadata, "id3album", "--tl");
addMetadataArg (metadata, "id3comment", "--tc");
addMetadataArg (metadata, "id3date", "--ty");
addMetadataArg (metadata, "id3genre", "--tg");
addMetadataArg (metadata, "id3trackNumber", "--tn");
}
void addMetadataArg (const StringPairArray& metadata, const char* key, const char* lameFlag)
@ -165,10 +168,6 @@ LAMEEncoderAudioFormat::LAMEEncoderAudioFormat (const File& lameApplication)
{
}
LAMEEncoderAudioFormat::~LAMEEncoderAudioFormat()
{
}
bool LAMEEncoderAudioFormat::canHandleFile (const File&)
{
return false;
@ -208,6 +207,33 @@ AudioFormatReader* LAMEEncoderAudioFormat::createReaderFor (InputStream*, const
return nullptr;
}
std::unique_ptr<AudioFormatWriter> LAMEEncoderAudioFormat::createWriterFor (std::unique_ptr<OutputStream> streamToWriteTo,
const AudioFormatWriterOptions& options)
{
if (streamToWriteTo == nullptr)
return nullptr;
int vbr = 4;
int cbr = 0;
const String qual (getQualityOptions() [options.getQualityOptionIndex()]);
if (qual.contains ("VBR"))
vbr = qual.retainCharacters ("0123456789").getIntValue();
else
cbr = qual.getIntValue();
return std::make_unique<Writer> (streamToWriteTo.release(),
getFormatName(),
lameApp,
vbr,
cbr,
options.getSampleRate(),
(unsigned int) options.getNumChannels(),
options.getBitsPerSample(),
options.getMetadataValues());
}
AudioFormatWriter* LAMEEncoderAudioFormat::createWriterFor (OutputStream* streamToWriteTo,
double sampleRateToUse,
unsigned int numberOfChannels,

View file

@ -60,21 +60,24 @@ public:
executable at the location given.
*/
LAMEEncoderAudioFormat (const File& lameExecutableToUse);
~LAMEEncoderAudioFormat();
bool canHandleFile (const File&);
Array<int> getPossibleSampleRates();
Array<int> getPossibleBitDepths();
bool canDoStereo();
bool canDoMono();
bool isCompressed();
StringArray getQualityOptions();
bool canHandleFile (const File&) override;
Array<int> getPossibleSampleRates() override;
Array<int> getPossibleBitDepths() override;
bool canDoStereo() override;
bool canDoMono() override;
bool isCompressed() override;
StringArray getQualityOptions() override;
AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails);
std::unique_ptr<AudioFormatWriter> createWriterFor (std::unique_ptr<OutputStream> streamToWriteTo,
const AudioFormatWriterOptions& options) override;
AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails) override;
AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse,
unsigned int numberOfChannels, int bitsPerSample,
const StringPairArray& metadataValues, int qualityOptionIndex);
using AudioFormat::createWriterFor;
private:

View file

@ -3173,6 +3173,13 @@ AudioFormatReader* MP3AudioFormat::createReaderFor (InputStream* sourceStream, c
return nullptr;
}
std::unique_ptr<AudioFormatWriter> MP3AudioFormat::createWriterFor (std::unique_ptr<OutputStream>&,
const AudioFormatWriterOptions&)
{
jassertfalse; // not yet implemented!
return nullptr;
}
AudioFormatWriter* MP3AudioFormat::createWriterFor (OutputStream*, double /*sampleRateToUse*/,
unsigned int /*numberOfChannels*/, int /*bitsPerSample*/,
const StringPairArray& /*metadataValues*/, int /*qualityOptionIndex*/)

View file

@ -74,6 +74,10 @@ public:
AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse,
unsigned int numberOfChannels, int bitsPerSample,
const StringPairArray& metadataValues, int qualityOptionIndex) override;
std::unique_ptr<AudioFormatWriter> createWriterFor (std::unique_ptr<OutputStream>& streamToWriteTo,
const AudioFormatWriterOptions& options) override;
using AudioFormat::createWriterFor;
};

View file

@ -463,6 +463,28 @@ AudioFormatReader* OggVorbisAudioFormat::createReaderFor (InputStream* in, bool
return nullptr;
}
std::unique_ptr<AudioFormatWriter> OggVorbisAudioFormat::createWriterFor (std::unique_ptr<OutputStream>& streamToWriteTo,
const AudioFormatWriterOptions& options)
{
if (streamToWriteTo == nullptr)
return nullptr;
StringPairArray metadata;
metadata.addUnorderedMap (options.getMetadataValues());
auto w = std::make_unique<OggWriter> (std::exchange (streamToWriteTo, {}).release(),
options.getSampleRate(),
(unsigned int) options.getNumChannels(),
(unsigned int) options.getBitsPerSample(),
options.getQualityOptionIndex(),
metadata);
if (! w->ok)
return nullptr;
return w;
}
AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out,
double sampleRate,
unsigned int numChannels,

View file

@ -93,12 +93,16 @@ public:
AudioFormatReader* createReaderFor (InputStream* sourceStream,
bool deleteStreamIfOpeningFails) override;
std::unique_ptr<AudioFormatWriter> createWriterFor (std::unique_ptr<OutputStream>& streamToWriteTo,
const AudioFormatWriterOptions& options) override;
AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo,
double sampleRateToUse,
unsigned int numberOfChannels,
int bitsPerSample,
const StringPairArray& metadataValues,
int qualityOptionIndex) override;
using AudioFormat::createWriterFor;
private:

View file

@ -35,18 +35,6 @@
namespace juce
{
using StringMap = std::unordered_map<String, String>;
static auto toMap (const StringPairArray& array)
{
StringMap result;
for (auto i = 0; i < array.size(); ++i)
result[array.getAllKeys()[i]] = array.getAllValues()[i];
return result;
}
static auto getValueWithDefault (const StringMap& m, const String& key, const String& fallback = {})
{
const auto iter = m.find (key);
@ -1599,30 +1587,36 @@ class WavAudioFormatWriter final : public AudioFormatWriter
public:
WavAudioFormatWriter (OutputStream* const out, const double rate,
const AudioChannelSet& channelLayoutToUse, const unsigned int bits,
const StringPairArray& metadataValues)
const StringMap& metadataValues,
AudioFormatWriterOptions::SampleFormat sampleFormat)
: AudioFormatWriter (out, wavFormatName, rate, channelLayoutToUse, bits)
{
using namespace WavFileHelpers;
using SampleFormat = AudioFormatWriterOptions::SampleFormat;
if (metadataValues.size() > 0)
// The floating point format is only supported with a bit depth of 32
jassert (sampleFormat != SampleFormat::floatingPoint || bits == 32);
usesFloatingPointData = bits == 32 && sampleFormat != SampleFormat::integral;
if (! metadataValues.empty())
{
// The meta data should have been sanitised for the WAV format.
// If it was originally sourced from an AIFF file the MetaDataSource
// key should be removed (or set to "WAV") once this has been done
jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF");
jassert (metadataValues.count ("MetaDataSource") == 0
|| metadataValues.at ("MetaDataSource") != "AIFF");
const auto map = toMap (metadataValues);
bwavChunk = BWAVChunk::createFrom (map);
ixmlChunk = IXMLChunk::createFrom (map);
axmlChunk = AXMLChunk::createFrom (map);
smplChunk = SMPLChunk::createFrom (map);
instChunk = InstChunk::createFrom (map);
cueChunk = CueChunk ::createFrom (map);
listChunk = ListChunk::createFrom (map);
listInfoChunk = ListInfoChunk::createFrom (map);
acidChunk = AcidChunk::createFrom (map);
trckChunk = TracktionChunk::createFrom (map);
bwavChunk = BWAVChunk::createFrom (metadataValues);
ixmlChunk = IXMLChunk::createFrom (metadataValues);
axmlChunk = AXMLChunk::createFrom (metadataValues);
smplChunk = SMPLChunk::createFrom (metadataValues);
instChunk = InstChunk::createFrom (metadataValues);
cueChunk = CueChunk ::createFrom (metadataValues);
listChunk = ListChunk::createFrom (metadataValues);
listInfoChunk = ListInfoChunk::createFrom (metadataValues);
acidChunk = AcidChunk::createFrom (metadataValues);
trckChunk = TracktionChunk::createFrom (metadataValues);
}
headerPosition = out->getPosition();
@ -1773,8 +1767,12 @@ private:
else
{
writeChunkHeader (chunkName ("fmt "), 16);
output->writeShort (bitsPerSample < 32 ? (short) 1 /*WAVE_FORMAT_PCM*/
: (short) 3 /*WAVE_FORMAT_IEEE_FLOAT*/);
constexpr short waveFormatIeeeFloat = 3;
constexpr short waveFormatPcm = 1;
output->writeShort (usesFloatingPointData ? waveFormatIeeeFloat
: waveFormatPcm);
}
output->writeShort ((short) numChannels);
@ -1789,7 +1787,7 @@ private:
output->writeShort ((short) bitsPerSample); // wValidBitsPerSample
output->writeInt (channelMask);
const ExtensibleWavSubFormat& subFormat = bitsPerSample < 32 ? pcmFormat : IEEEFloatFormat;
const ExtensibleWavSubFormat& subFormat = usesFloatingPointData ? IEEEFloatFormat : pcmFormat;
output->writeInt ((int) subFormat.data1);
output->writeShort ((short) subFormat.data2);
@ -1809,8 +1807,6 @@ private:
writeChunk (trckChunk, chunkName ("Trkn"));
writeChunkHeader (chunkName ("data"), isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame));
usesFloatingPointData = (bitsPerSample == 32);
}
static size_t chunkSize (const MemoryBlock& data) noexcept { return data.isEmpty() ? 0 : (8 + data.getSize()); }
@ -2027,6 +2023,25 @@ MemoryMappedAudioFormatReader* WavAudioFormat::createMemoryMappedReader (FileInp
return nullptr;
}
std::unique_ptr<AudioFormatWriter> WavAudioFormat::createWriterFor (std::unique_ptr<OutputStream>& streamToWriteTo,
const AudioFormatWriterOptions& options)
{
if (streamToWriteTo == nullptr || ! getPossibleBitDepths().contains (options.getBitsPerSample()))
return nullptr;
const auto layout = options.getChannelLayout().value_or (WavFileHelpers::canonicalWavChannelSet (options.getNumChannels()));
if (! isChannelLayoutSupported (layout))
return nullptr;
return std::make_unique<WavAudioFormatWriter> (std::exchange (streamToWriteTo, {}).release(),
options.getSampleRate(),
layout,
(unsigned int) options.getBitsPerSample(),
options.getMetadataValues(),
options.getSampleFormat());
}
AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, double sampleRate,
unsigned int numChannels, int bitsPerSample,
const StringPairArray& metadataValues, int qualityOptionIndex)
@ -2044,44 +2059,39 @@ AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out,
{
if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample) && isChannelLayoutSupported (channelLayout))
return new WavAudioFormatWriter (out, sampleRate, channelLayout,
(unsigned int) bitsPerSample, metadataValues);
(unsigned int) bitsPerSample, toMap (metadataValues),
AudioFormatWriterOptions::SampleFormat::automatic);
return nullptr;
}
namespace WavFileHelpers
{
static bool slowCopyWavFileWithNewMetadata (const File& file, const StringPairArray& metadata)
static bool slowCopyWavFileWithNewMetadata (const File& file, const StringMap& metadata)
{
TemporaryFile tempFile (file);
WavAudioFormat wav;
std::unique_ptr<AudioFormatReader> reader (wav.createReaderFor (file.createInputStream().release(), true));
if (reader != nullptr)
{
std::unique_ptr<OutputStream> outStream (tempFile.getFile().createOutputStream());
if (reader == nullptr)
return false;
if (outStream != nullptr)
{
std::unique_ptr<AudioFormatWriter> writer (wav.createWriterFor (outStream.get(), reader->sampleRate,
reader->numChannels, (int) reader->bitsPerSample,
metadata, 0));
std::unique_ptr<OutputStream> stream = tempFile.getFile().createOutputStream();
auto writer = wav.createWriterFor (stream,
AudioFormatWriter::Options{}.withSampleRate (reader->sampleRate)
.withNumChannels ((int) reader->numChannels)
.withBitsPerSample ((int) reader->bitsPerSample)
.withMetadataValues (metadata));
if (writer != nullptr)
{
outStream.release();
if (writer == nullptr)
return false;
bool ok = writer->writeFromAudioReader (*reader, 0, -1);
writer.reset();
reader.reset();
bool ok = writer->writeFromAudioReader (*reader, 0, -1);
writer.reset();
reader.reset();
return ok && tempFile.overwriteTargetFileWithTemporary();
}
}
}
return false;
return ok && tempFile.overwriteTargetFileWithTemporary();
}
}
@ -2123,7 +2133,7 @@ bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPai
}
}
return slowCopyWavFileWithNewMetadata (wavFile, newMetadata);
return slowCopyWavFileWithNewMetadata (wavFile, toMap (newMetadata));
}
@ -2172,15 +2182,14 @@ struct WaveAudioFormatTests final : public UnitTest
{
beginTest ("Metadata can be written and read");
const auto newMetadata = getMetadataAfterReading (format, writeToBlock (format, metadataArray));
const auto newMetadata = getMetadataAfterReading (format, writeToBlock (format, metadataValues));
expect (newMetadata == metadataArray, "Somehow, the metadata is different!");
}
{
beginTest ("Files containing a riff info source and an empty ISRC associate the source with the riffInfoSource key");
StringPairArray meta;
meta.addMap ({ { WavAudioFormat::riffInfoSource, "customsource" },
{ WavAudioFormat::internationalStandardRecordingCode, "" } });
StringMap meta { { WavAudioFormat::riffInfoSource, "customsource" },
{ WavAudioFormat::internationalStandardRecordingCode, "" } };
const auto mb = writeToBlock (format, meta);
checkPatternsPresent (mb, { "INFOISRC" });
checkPatternsNotPresent (mb, { "ISRC:", "<ebucore" });
@ -2192,8 +2201,7 @@ struct WaveAudioFormatTests final : public UnitTest
{
beginTest ("Files containing a riff info source and no ISRC associate the source with both keys "
"for backwards compatibility");
StringPairArray meta;
meta.addMap ({ { WavAudioFormat::riffInfoSource, "customsource" } });
StringMap meta { { WavAudioFormat::riffInfoSource, "customsource" } };
const auto mb = writeToBlock (format, meta);
checkPatternsPresent (mb, { "INFOISRC", "ISRC:customsource", "<ebucore" });
const auto a = getMetadataAfterReading (format, mb);
@ -2204,8 +2212,7 @@ struct WaveAudioFormatTests final : public UnitTest
{
beginTest ("Files containing an ISRC associate the value with the internationalStandardRecordingCode key "
"and the riffInfoSource key for backwards compatibility");
StringPairArray meta;
meta.addMap ({ { WavAudioFormat::internationalStandardRecordingCode, "AABBBCCDDDDD" } });
StringMap meta { { WavAudioFormat::internationalStandardRecordingCode, "AABBBCCDDDDD" } };
const auto mb = writeToBlock (format, meta);
checkPatternsPresent (mb, { "ISRC:AABBBCCDDDDD", "<ebucore" });
checkPatternsNotPresent (mb, { "INFOISRC" });
@ -2216,9 +2223,8 @@ struct WaveAudioFormatTests final : public UnitTest
{
beginTest ("Files containing an ISRC and a riff info source associate the values with the appropriate keys");
StringPairArray meta;
meta.addMap ({ { WavAudioFormat::riffInfoSource, "source" } });
meta.addMap ({ { WavAudioFormat::internationalStandardRecordingCode, "UUVVVXXYYYYY" } });
StringMap meta { { WavAudioFormat::riffInfoSource, "source" },
{ WavAudioFormat::internationalStandardRecordingCode, "UUVVVXXYYYYY" } };
const auto mb = writeToBlock (format, meta);
checkPatternsPresent (mb, { "INFOISRC", "ISRC:UUVVVXXYYYYY", "<ebucore" });
const auto a = getMetadataAfterReading (format, mb);
@ -2229,13 +2235,19 @@ struct WaveAudioFormatTests final : public UnitTest
{
beginTest ("Files containing ASWG metadata read and write correctly");
MemoryBlock block;
StringPairArray meta;
StringMap meta;
for (const auto& key : WavFileHelpers::IXMLChunk::aswgMetadataKeys)
meta.set (key, "Test123&<>");
meta[key] = "Test123&<>";
{
auto writer = rawToUniquePtr (WavAudioFormat().createWriterFor (new MemoryOutputStream (block, false), 48000, 1, 32, meta, 0));
const auto writerOptions = AudioFormatWriterOptions{}.withSampleRate (48000.0)
.withNumChannels (1)
.withBitsPerSample (32)
.withMetadataValues (meta);
std::unique_ptr<OutputStream> stream = std::make_unique<MemoryOutputStream> (block, false);
auto writer = format.createWriterFor (stream, writerOptions);
expect (writer != nullptr);
}
@ -2270,9 +2282,8 @@ struct WaveAudioFormatTests final : public UnitTest
auto reader = rawToUniquePtr (WavAudioFormat().createReaderFor (new MemoryInputStream (block, false), true));
expect (reader != nullptr);
for (const auto& key : meta.getAllKeys())
for (const auto& [key, oldValue] : meta)
{
const auto oldValue = meta.getValue (key, "!");
const auto newValue = reader->metadataValues.getValue (key, "");
expectEquals (oldValue, newValue);
}
@ -2280,22 +2291,128 @@ struct WaveAudioFormatTests final : public UnitTest
expect (reader->metadataValues.getValue (WavAudioFormat::aswgVersion, "") == "3.01");
}
}
{
beginTest ("Writing 32-bit integer samples should work");
const auto minimum = std::numeric_limits<int>::min();
const auto maximum = std::numeric_limits<int>::max();
std::vector<int> dataIn { minimum,
minimum + 1,
minimum + 999,
0,
1,
maximum - 1001,
maximum - 1,
maximum };
const int* ptr = dataIn.data();
WavFormatWriterTestData integralTestData { &ptr,
dataIn.size(),
AudioFormatWriterOptions::SampleFormat::integral };
auto* reader = integralTestData.getReader();
if (reader == nullptr)
{
expect (false, "WavFormatReader should be non-null");
return;
}
std::vector<int> dataOut ((size_t) reader->lengthInSamples);
int* const outPtr = dataOut.data();
reader->read (&outPtr, 1, 0, (int) dataOut.size(), false);
expect (reader->usesFloatingPointData == false);
expect (dataIn == dataOut);
}
{
beginTest ("Writing 32-bit float samples should work");
std::vector<float> dataIn { -1.0f,
-0.8f,
0.0f,
0.8f,
1.0f };
const int* ptr = (int*) dataIn.data();
WavFormatWriterTestData floatingPointTestData { &ptr,
dataIn.size(),
AudioFormatWriterOptions::SampleFormat::floatingPoint };
auto* reader = floatingPointTestData.getReader();
if (reader == nullptr)
{
expect (false, "WavFormatReader should be non-null");
return;
}
std::vector<float> dataOut ((size_t) reader->lengthInSamples);
float* const outPtr = dataOut.data();
reader->read (&outPtr, 1, 0, (int) dataOut.size());
expect (reader->usesFloatingPointData == true);
expect (dataIn.size() == dataOut.size());
for (auto [index, value] : enumerate (dataOut, size_t{}))
expect (approximatelyEqual (value, dataIn[index]));
}
}
private:
MemoryBlock writeToBlock (WavAudioFormat& format, StringPairArray meta)
struct WavFormatWriterTestData
{
WavFormatWriterTestData (const int** ptr,
size_t numSamples,
AudioFormatWriterOptions::SampleFormat sampleFormat)
{
const auto writerOptions = AudioFormatWriterOptions{}.withSampleRate (48000.0)
.withNumChannels (1)
.withBitsPerSample (32)
.withSampleFormat (sampleFormat);
WavAudioFormat format;
{
std::unique_ptr<OutputStream> stream = std::make_unique<MemoryOutputStream> (block, false);
auto writer = format.createWriterFor (stream, writerOptions);
writer->write (ptr, (int) numSamples);
}
reader = rawToUniquePtr (format.createReaderFor (new MemoryInputStream (block, false), true));
}
AudioFormatReader* getReader()
{
return reader.get();
}
private:
MemoryBlock block;
std::unique_ptr<AudioFormatReader> reader;
};
MemoryBlock writeToBlock (WavAudioFormat& format, const StringMap& meta)
{
MemoryBlock mb;
{
const auto writerOptions = AudioFormatWriterOptions{}.withSampleRate (44100.0)
.withNumChannels ((int) numTestAudioBufferChannels)
.withBitsPerSample (16)
.withMetadataValues (meta);
// The destructor of the writer will modify the block, so make sure that we've
// destroyed the writer before returning the block!
auto writer = rawToUniquePtr (format.createWriterFor (new MemoryOutputStream (mb, false),
44100.0,
numTestAudioBufferChannels,
16,
meta,
0));
std::unique_ptr<OutputStream> stream = std::make_unique<MemoryOutputStream> (mb, false);
auto writer = format.createWriterFor (stream, writerOptions);
expect (writer != nullptr);
AudioBuffer<float> buffer (numTestAudioBufferChannels, numTestAudioBufferSamples);
expect (writer->writeFromAudioSampleBuffer (buffer, 0, numTestAudioBufferSamples));

View file

@ -297,6 +297,9 @@ public:
MemoryMappedAudioFormatReader* createMemoryMappedReader (const File&) override;
MemoryMappedAudioFormatReader* createMemoryMappedReader (FileInputStream*) override;
std::unique_ptr<AudioFormatWriter> createWriterFor (std::unique_ptr<OutputStream>& streamToWriteTo,
const AudioFormatWriterOptions& options) override;
AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo,
double sampleRateToUse,
unsigned int numberOfChannels,
@ -310,6 +313,7 @@ public:
int bitsPerSample,
const StringPairArray& metadataValues,
int qualityOptionIndex) override;
using AudioFormat::createWriterFor;
//==============================================================================

View file

@ -365,6 +365,13 @@ AudioFormatWriter* WindowsMediaAudioFormat::createWriterFor (OutputStream* /*str
return nullptr;
}
std::unique_ptr<AudioFormatWriter> WindowsMediaAudioFormat::createWriterFor (std::unique_ptr<OutputStream>&,
const AudioFormatWriterOptions&)
{
jassertfalse; // not yet implemented!
return nullptr;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS

View file

@ -60,9 +60,13 @@ public:
//==============================================================================
AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails) override;
std::unique_ptr<AudioFormatWriter> createWriterFor (std::unique_ptr<OutputStream>& streamToWriteTo,
const AudioFormatWriterOptions& options) override;
AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse,
unsigned int numberOfChannels, int bitsPerSample,
const StringPairArray& metadataValues, int qualityOptionIndex) override;
using AudioFormat::createWriterFor;
};

View file

@ -82,6 +82,53 @@ bool AudioFormat::isChannelLayoutSupported (const AudioChannelSet& channelSet)
return false;
}
using StringMap = std::unordered_map<String, String>;
static StringMap toMap (const StringPairArray& array)
{
StringMap result;
for (auto i = 0; i < array.size(); ++i)
result[array.getAllKeys()[i]] = array.getAllValues()[i];
return result;
}
AudioFormatWriter* AudioFormat::createWriterForRawPtr (OutputStream* streamToWriteTo,
const AudioFormatWriterOptions& opt)
{
auto owned = rawToUniquePtr (streamToWriteTo);
if (auto writer = createWriterFor (owned, opt))
{
// Creating the writer succeeded, so it's the writer's responsibility to eventually free
// the stream
jassert (owned == nullptr);
return writer.release();
}
// Creating the writer failed, so the stream should remain alive for re-use
jassert (owned != nullptr);
owned.release();
return {};
}
AudioFormatWriter* AudioFormat::createWriterFor (OutputStream* streamToWriteTo,
double sampleRateToUse,
unsigned int numberOfChannels,
int bitsPerSample,
const StringPairArray& metadataValues,
int qualityOptionIndex)
{
auto opt = AudioFormatWriter::Options{}.withSampleRate (sampleRateToUse)
.withNumChannels ((int) numberOfChannels)
.withBitsPerSample (bitsPerSample)
.withMetadataValues (toMap (metadataValues))
.withQualityOptionIndex (qualityOptionIndex);
return createWriterForRawPtr (streamToWriteTo, opt);
}
AudioFormatWriter* AudioFormat::createWriterFor (OutputStream* streamToWriteTo,
double sampleRateToUse,
const AudioChannelSet& channelLayout,
@ -89,12 +136,15 @@ AudioFormatWriter* AudioFormat::createWriterFor (OutputStream* streamToWriteTo,
const StringPairArray& metadataValues,
int qualityOptionIndex)
{
if (isChannelLayoutSupported (channelLayout))
return createWriterFor (streamToWriteTo, sampleRateToUse,
static_cast<unsigned int> (channelLayout.size()),
bitsPerSample, metadataValues, qualityOptionIndex);
if (! isChannelLayoutSupported (channelLayout))
return nullptr;
return nullptr;
auto opt = AudioFormatWriter::Options{}.withSampleRate (sampleRateToUse)
.withChannelLayout (channelLayout)
.withBitsPerSample (bitsPerSample)
.withMetadataValues (toMap (metadataValues))
.withQualityOptionIndex (qualityOptionIndex);
return createWriterForRawPtr (streamToWriteTo, opt);
}
} // namespace juce

View file

@ -125,6 +125,26 @@ public:
virtual MemoryMappedAudioFormatReader* createMemoryMappedReader (const File& file);
virtual MemoryMappedAudioFormatReader* createMemoryMappedReader (FileInputStream* fin);
/** Tries to create an object that can write to a stream with this audio format.
If the writer can't be created for some reason (e.g. the parameters passed in
here aren't suitable), this will return nullptr.
@param streamToWriteTo a reference to a unique_ptr that owns the output stream. If creating
the writer succeeds, then ownership of the stream will be
transferred to the writer, and this argument will be set to nullptr.
If creating the writer fails, then streamToWriteTo will remain
unchanged, allowing it to be reused to create a writer of a
different format.
@param options options that specify details of the output file. If the audio format
does not support these settings, then this function may return
nullptr.
@see AudioFormatWriterOptions
*/
virtual std::unique_ptr<AudioFormatWriter> createWriterFor (std::unique_ptr<OutputStream>& streamToWriteTo,
const AudioFormatWriterOptions& options) = 0;
/** Tries to create an object that can write to a stream with this audio format.
The writer object that is returned can be used to write to the stream, and
@ -217,6 +237,8 @@ protected:
AudioFormat (StringRef formatName, StringRef fileExtensions);
private:
AudioFormatWriter* createWriterForRawPtr (OutputStream*, const AudioFormatWriterOptions&);
//==============================================================================
String formatName;
StringArray fileExtensions;

View file

@ -93,6 +93,8 @@ protected:
unsigned int bitsPerSample);
public:
using Options = AudioFormatWriterOptions;
/** Destructor. */
virtual ~AudioFormatWriter();

View file

@ -0,0 +1,182 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
Options that affect the output data format produced by an AudioFormatWriter. Format
specific writers may ignore some of these options.
@see AudioFormat::createWriterFor(), AudioFormatWriter
@tags{Audio}
*/
class JUCE_API AudioFormatWriterOptions final
{
public:
/** Used to provide a hint to the AudioFormatWriter for the output sample format.
Use automatic for the old behaviour. The values integral and floatingPoint can be used with
the WavAudioFormat when using a bit depth of 32. Other formats are not affected by this
setting.
*/
enum class SampleFormat
{
automatic, ///< Lets the writer decide the format based on the other parameter values.
integral, ///< Integral format, e.g. PCM in case of the WavAudioFormat
floatingPoint ///< IEEE floating point format
};
/** Returns a copy of these options with the specified sample format.
@see SampleFormat
*/
[[nodiscard]] AudioFormatWriterOptions withSampleFormat (SampleFormat x) const
{
return withMember (*this, &AudioFormatWriterOptions::sampleFormat, x);
}
/** Returns a copy of these options with the specified sample rate.
This specifies the sample rate for the file, which must be one of the ones
returned by AudioFormat::getPossibleSampleRates().
*/
[[nodiscard]] AudioFormatWriterOptions withSampleRate (double x) const
{
return withMember (*this, &AudioFormatWriterOptions::sampleRate, x);
}
/** Returns a copy of these options with the specified channel set.
Setting this option will supersede the value passed into withNumChannels().
You should prefer to use withChannelLayout(), if specifying an AudioChannelSet is
applicable, and withNumChannels() otherwise.
*/
[[nodiscard]] AudioFormatWriterOptions withChannelLayout (const AudioChannelSet& x) const
{
return withMember (*this, &AudioFormatWriterOptions::channelLayout, x);
}
/** Returns a copy of these options with the specified number of channels.
This is meant as a fallback for specifying the channel layout. Setting this option will
have no effect if the channel layout is specified.
@see withChannelLayout()
*/
[[nodiscard]] AudioFormatWriterOptions withNumChannels (int x) const
{
return withMember (*this, &AudioFormatWriterOptions::numChannels, x);
}
/** Returns a copy of these options with the specified bit size per sample.
This must be one of the values returned by AudioFormat::getPossibleBitDepths().
*/
[[nodiscard]] AudioFormatWriterOptions withBitsPerSample (int x) const
{
return withMember (*this, &AudioFormatWriterOptions::bitsPerSample, x);
}
/** Returns a copy of these options with the specified metadata container.
As an alternative to this function, you can specify the key-value pairs one-by-one using
the withMetadata function.
Subsequent calls of this function overwrites all previously added metadata.
This parameter is a set of metadata values that the writer should try to write to the stream.
Exactly what these are depends on the format, and the subclass doesn't actually have to do
anything with them if it doesn't want to. Have a look at the specific format implementation
classes to see possible values that can be used.
*/
[[nodiscard]] AudioFormatWriterOptions withMetadataValues (const std::unordered_map<String, String>& x) const
{
return withMember (*this, &AudioFormatWriterOptions::metadataValues, x);
}
/** Returns a copy of these options with the specified metadata added.
Subsequent calls of this function adds new metadata values, while also preserving the
previously added ones.
Here you can specify metadata values that the writer should try to write to the stream.
Exactly what these are depends on the format, and the subclass doesn't actually have to do
anything with them if it doesn't want to. Have a look at the specific format implementation
classes to see possible values that can be used.
*/
[[nodiscard]] AudioFormatWriterOptions withMetadata (const String& key, const String& value) const
{
auto copy = *this;
copy.metadataValues[key] = value;
return copy;
}
/** Returns a copy of these options with the specified quality option index.
The index of one of the items returned by the AudioFormat::getQualityOptions() method.
*/
[[nodiscard]] AudioFormatWriterOptions withQualityOptionIndex (int x) const
{
return withMember (*this, &AudioFormatWriterOptions::qualityOptionIndex, x);
}
/** @see withSampleRate() */
[[nodiscard]] auto getSampleRate() const { return sampleRate; }
/** @see withChannelLayout() */
[[nodiscard]] auto getChannelLayout() const { return channelLayout; }
/** @see withNumChannels() */
[[nodiscard]] auto getNumChannels() const { return channelLayout.has_value() ? channelLayout->size() : numChannels; }
/** @see withBitsPerSample() */
[[nodiscard]] auto getBitsPerSample() const { return bitsPerSample; }
/** @see withMetadataValues() */
[[nodiscard]] auto getMetadataValues() const { return metadataValues; }
/** @see withQualityOptionIndex() */
[[nodiscard]] auto getQualityOptionIndex() const { return qualityOptionIndex; }
/** @see withSampleFormat() */
[[nodiscard]] auto getSampleFormat() const { return sampleFormat; }
private:
double sampleRate = 48000.0;
int numChannels = 1;
std::optional<AudioChannelSet> channelLayout = std::nullopt;
int bitsPerSample = 16;
std::unordered_map<String, String> metadataValues;
int qualityOptionIndex = 0;
SampleFormat sampleFormat = SampleFormat::automatic;
};
} // namespace juce

View file

@ -121,6 +121,7 @@
//==============================================================================
#include "format/juce_AudioFormatReader.h"
#include "format/juce_AudioFormatWriterOptions.h"
#include "format/juce_AudioFormatWriter.h"
#include "format/juce_MemoryMappedAudioFormatReader.h"
#include "format/juce_AudioFormat.h"