diff --git a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
index 13d7014a85..db6131893f 100644
--- a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
+++ b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
@@ -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"
diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
index 7599772405..5f8a3eed3e 100644
--- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
@@ -3558,6 +3558,7 @@
+
diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
index 24bb4fc7b8..52a5a9990f 100644
--- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
@@ -4974,6 +4974,9 @@
JUCE Modules\juce_audio_formats\format
+
+ JUCE Modules\juce_audio_formats\format
+
JUCE Modules\juce_audio_formats\format
diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
index 6d877a27d9..a2dc7771e0 100644
--- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
@@ -3558,6 +3558,7 @@
+
diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
index 800afa748e..8eb54cd7f0 100644
--- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
@@ -4974,6 +4974,9 @@
JUCE Modules\juce_audio_formats\format
+
+ JUCE Modules\juce_audio_formats\format
+
JUCE Modules\juce_audio_formats\format
diff --git a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
index 2ccad172fb..1ae065577f 100644
--- a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
+++ b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
@@ -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"
diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
index 5dabc514ed..cb12398a5a 100644
--- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
+++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
@@ -3072,6 +3072,7 @@
+
diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
index f5c2fa7388..db9d24942e 100644
--- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
+++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
@@ -4236,6 +4236,9 @@
JUCE Modules\juce_audio_formats\format
+
+ JUCE Modules\juce_audio_formats\format
+
JUCE Modules\juce_audio_formats\format
diff --git a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
index bf0710f7ba..bf8ed75274 100644
--- a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
+++ b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
@@ -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"
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
index e947c78ba5..d543831872 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
@@ -3252,6 +3252,7 @@
+
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
index 5d8c24d5c7..90e15a6859 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
@@ -4512,6 +4512,9 @@
JUCE Modules\juce_audio_formats\format
+
+ JUCE Modules\juce_audio_formats\format
+
JUCE Modules\juce_audio_formats\format
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
index 3c87281588..077b98bbff 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
@@ -3252,6 +3252,7 @@
+
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
index 181ee2f945..c1e2985b4c 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
@@ -4512,6 +4512,9 @@
JUCE Modules\juce_audio_formats\format
+
+ JUCE Modules\juce_audio_formats\format
+
JUCE Modules\juce_audio_formats\format
diff --git a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
index bb8244c728..1b7042adb1 100644
--- a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
+++ b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
@@ -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"
diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
index 4a95e18f40..035ddec6bb 100644
--- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
+++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
@@ -3163,6 +3163,7 @@
+
diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
index 8a16cb8319..b6bf08b851 100644
--- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
+++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
@@ -4377,6 +4377,9 @@
JUCE Modules\juce_audio_formats\format
+
+ JUCE Modules\juce_audio_formats\format
+
JUCE Modules\juce_audio_formats\format
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
index 1f81c0163d..62fab9dc3c 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
+++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
@@ -3364,6 +3364,7 @@
+
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
index 05bc025378..3f3f2701a8 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
+++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
@@ -4671,6 +4671,9 @@
JUCE Modules\juce_audio_formats\format
+
+ JUCE Modules\juce_audio_formats\format
+
JUCE Modules\juce_audio_formats\format
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
index f41b0d6e06..0fb1939aca 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
+++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
@@ -3364,6 +3364,7 @@
+
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
index 580366010d..ea787cb498 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
+++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
@@ -4671,6 +4671,9 @@
JUCE Modules\juce_audio_formats\format
+
+ JUCE Modules\juce_audio_formats\format
+
JUCE Modules\juce_audio_formats\format
diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj
index 9ad0c94d94..7983b3c5aa 100644
--- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj
+++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj
@@ -3139,6 +3139,7 @@
+
diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters
index 9cb74f3ea3..2aeb535a06 100644
--- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters
+++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters
@@ -4344,6 +4344,9 @@
JUCE Modules\juce_audio_formats\format
+
+ JUCE Modules\juce_audio_formats\format
+
JUCE Modules\juce_audio_formats\format
diff --git a/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp
index 71e07ca871..1b5b9ece0b 100644
--- a/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp
+++ b/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp
@@ -1031,4 +1031,20 @@ AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out,
return nullptr;
}
+std::unique_ptr AiffAudioFormat::createWriterFor (std::unique_ptr& streamToWriteTo,
+ const AudioFormatWriterOptions& options)
+{
+ if (streamToWriteTo == nullptr || ! getPossibleBitDepths().contains (options.getBitsPerSample()))
+ return nullptr;
+
+ StringPairArray metadata;
+ metadata.addUnorderedMap (options.getMetadataValues());
+
+ return std::make_unique (std::exchange (streamToWriteTo, {}).release(),
+ options.getSampleRate(),
+ (unsigned int) options.getNumChannels(),
+ (unsigned int) options.getBitsPerSample(),
+ metadata);
+}
+
} // namespace juce
diff --git a/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h b/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h
index b17cd67569..0830638581 100644
--- a/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h
+++ b/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h
@@ -94,6 +94,10 @@ public:
int bitsPerSample,
const StringPairArray& metadataValues,
int qualityOptionIndex) override;
+
+ std::unique_ptr createWriterFor (std::unique_ptr& streamToWriteTo,
+ const AudioFormatWriterOptions& options) override;
+
using AudioFormat::createWriterFor;
private:
diff --git a/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp
index 73151b374b..8c0e429968 100644
--- a/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp
+++ b/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp
@@ -665,6 +665,13 @@ AudioFormatWriter* CoreAudioFormat::createWriterFor (OutputStream*,
return nullptr;
}
+std::unique_ptr CoreAudioFormat::createWriterFor (std::unique_ptr&,
+ const AudioFormatWriterOptions&)
+{
+ jassertfalse; // not yet implemented!
+ return nullptr;
+}
+
//==============================================================================
//==============================================================================
diff --git a/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h b/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h
index 72f1896169..16c284a078 100644
--- a/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h
+++ b/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h
@@ -112,6 +112,10 @@ public:
int bitsPerSample,
const StringPairArray& metadataValues,
int qualityOptionIndex) override;
+
+ std::unique_ptr createWriterFor (std::unique_ptr& streamToWriteTo,
+ const AudioFormatWriterOptions& options) override;
+
using AudioFormat::createWriterFor;
private:
diff --git a/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp
index 851aea9466..2a886a1d53 100644
--- a/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp
+++ b/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp
@@ -610,7 +610,7 @@ AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out,
if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample))
{
std::unique_ptr 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 FlacAudioFormat::createWriterFor (std::unique_ptr& streamToWriteTo,
+ const AudioFormatWriterOptions& options)
+{
+ if (streamToWriteTo == nullptr || ! getPossibleBitDepths().contains (options.getBitsPerSample()))
+ return nullptr;
+
+ auto writer = std::make_unique (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)" };
diff --git a/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h b/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h
index 0a555b6ae6..99f88d7118 100644
--- a/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h
+++ b/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h
@@ -63,6 +63,9 @@ public:
StringArray getQualityOptions() override;
//==============================================================================
+ std::unique_ptr createWriterFor (std::unique_ptr& 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:
diff --git a/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp
index 06b8440201..0983ae0149 100644
--- a/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp
+++ b/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp
@@ -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 LAMEEncoderAudioFormat::createWriterFor (std::unique_ptr 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 (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,
diff --git a/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h b/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h
index a56925b877..50161131f1 100644
--- a/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h
+++ b/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h
@@ -60,21 +60,24 @@ public:
executable at the location given.
*/
LAMEEncoderAudioFormat (const File& lameExecutableToUse);
- ~LAMEEncoderAudioFormat();
- bool canHandleFile (const File&);
- Array getPossibleSampleRates();
- Array getPossibleBitDepths();
- bool canDoStereo();
- bool canDoMono();
- bool isCompressed();
- StringArray getQualityOptions();
+ bool canHandleFile (const File&) override;
+ Array getPossibleSampleRates() override;
+ Array getPossibleBitDepths() override;
+ bool canDoStereo() override;
+ bool canDoMono() override;
+ bool isCompressed() override;
+ StringArray getQualityOptions() override;
- AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails);
+ std::unique_ptr createWriterFor (std::unique_ptr 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:
diff --git a/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp
index 9506b56e5a..dd91fe632c 100644
--- a/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp
+++ b/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp
@@ -3173,6 +3173,13 @@ AudioFormatReader* MP3AudioFormat::createReaderFor (InputStream* sourceStream, c
return nullptr;
}
+std::unique_ptr MP3AudioFormat::createWriterFor (std::unique_ptr&,
+ const AudioFormatWriterOptions&)
+{
+ jassertfalse; // not yet implemented!
+ return nullptr;
+}
+
AudioFormatWriter* MP3AudioFormat::createWriterFor (OutputStream*, double /*sampleRateToUse*/,
unsigned int /*numberOfChannels*/, int /*bitsPerSample*/,
const StringPairArray& /*metadataValues*/, int /*qualityOptionIndex*/)
diff --git a/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h b/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h
index 0256b8bab7..e3e1c67d01 100644
--- a/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h
+++ b/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h
@@ -74,6 +74,10 @@ public:
AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse,
unsigned int numberOfChannels, int bitsPerSample,
const StringPairArray& metadataValues, int qualityOptionIndex) override;
+
+ std::unique_ptr createWriterFor (std::unique_ptr& streamToWriteTo,
+ const AudioFormatWriterOptions& options) override;
+
using AudioFormat::createWriterFor;
};
diff --git a/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp
index 15f744ac16..4c5b0c99ff 100644
--- a/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp
+++ b/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp
@@ -463,6 +463,28 @@ AudioFormatReader* OggVorbisAudioFormat::createReaderFor (InputStream* in, bool
return nullptr;
}
+std::unique_ptr OggVorbisAudioFormat::createWriterFor (std::unique_ptr& streamToWriteTo,
+ const AudioFormatWriterOptions& options)
+{
+ if (streamToWriteTo == nullptr)
+ return nullptr;
+
+ StringPairArray metadata;
+ metadata.addUnorderedMap (options.getMetadataValues());
+
+ auto w = std::make_unique (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,
diff --git a/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h b/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h
index 3cc49a8556..6b404823bb 100644
--- a/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h
+++ b/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h
@@ -93,12 +93,16 @@ public:
AudioFormatReader* createReaderFor (InputStream* sourceStream,
bool deleteStreamIfOpeningFails) override;
+ std::unique_ptr createWriterFor (std::unique_ptr& 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:
diff --git a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp
index a9a0ecb476..11a420fd5c 100644
--- a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp
+++ b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp
@@ -35,18 +35,6 @@
namespace juce
{
-using StringMap = std::unordered_map;
-
-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 WavAudioFormat::createWriterFor (std::unique_ptr& 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 (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 reader (wav.createReaderFor (file.createInputStream().release(), true));
- if (reader != nullptr)
- {
- std::unique_ptr outStream (tempFile.getFile().createOutputStream());
+ if (reader == nullptr)
+ return false;
- if (outStream != nullptr)
- {
- std::unique_ptr writer (wav.createWriterFor (outStream.get(), reader->sampleRate,
- reader->numChannels, (int) reader->bitsPerSample,
- metadata, 0));
+ std::unique_ptr 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:", "");
+ 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 stream = std::make_unique (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::min();
+ const auto maximum = std::numeric_limits::max();
+
+ std::vector 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 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 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 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 stream = std::make_unique (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 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 stream = std::make_unique (mb, false);
+ auto writer = format.createWriterFor (stream, writerOptions);
+
expect (writer != nullptr);
AudioBuffer buffer (numTestAudioBufferChannels, numTestAudioBufferSamples);
expect (writer->writeFromAudioSampleBuffer (buffer, 0, numTestAudioBufferSamples));
diff --git a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h
index c55556b62f..9c9164bee6 100644
--- a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h
+++ b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h
@@ -297,6 +297,9 @@ public:
MemoryMappedAudioFormatReader* createMemoryMappedReader (const File&) override;
MemoryMappedAudioFormatReader* createMemoryMappedReader (FileInputStream*) override;
+ std::unique_ptr createWriterFor (std::unique_ptr& 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;
//==============================================================================
diff --git a/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp
index b0f34de302..1db0977d42 100644
--- a/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp
+++ b/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp
@@ -365,6 +365,13 @@ AudioFormatWriter* WindowsMediaAudioFormat::createWriterFor (OutputStream* /*str
return nullptr;
}
+std::unique_ptr WindowsMediaAudioFormat::createWriterFor (std::unique_ptr&,
+ const AudioFormatWriterOptions&)
+{
+ jassertfalse; // not yet implemented!
+ return nullptr;
+}
+
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
diff --git a/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.h b/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.h
index d9f54372bf..7205eb82f5 100644
--- a/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.h
+++ b/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.h
@@ -60,9 +60,13 @@ public:
//==============================================================================
AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails) override;
+ std::unique_ptr createWriterFor (std::unique_ptr& streamToWriteTo,
+ const AudioFormatWriterOptions& options) override;
+
AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse,
unsigned int numberOfChannels, int bitsPerSample,
const StringPairArray& metadataValues, int qualityOptionIndex) override;
+
using AudioFormat::createWriterFor;
};
diff --git a/modules/juce_audio_formats/format/juce_AudioFormat.cpp b/modules/juce_audio_formats/format/juce_AudioFormat.cpp
index 29ae945ed2..1ba965e1d8 100644
--- a/modules/juce_audio_formats/format/juce_AudioFormat.cpp
+++ b/modules/juce_audio_formats/format/juce_AudioFormat.cpp
@@ -82,6 +82,53 @@ bool AudioFormat::isChannelLayoutSupported (const AudioChannelSet& channelSet)
return false;
}
+using StringMap = std::unordered_map;
+
+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 (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
diff --git a/modules/juce_audio_formats/format/juce_AudioFormat.h b/modules/juce_audio_formats/format/juce_AudioFormat.h
index 347c7d5af7..d04f01d3b8 100644
--- a/modules/juce_audio_formats/format/juce_AudioFormat.h
+++ b/modules/juce_audio_formats/format/juce_AudioFormat.h
@@ -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 createWriterFor (std::unique_ptr& 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;
diff --git a/modules/juce_audio_formats/format/juce_AudioFormatWriter.h b/modules/juce_audio_formats/format/juce_AudioFormatWriter.h
index ae4a90d83b..65b49624a3 100644
--- a/modules/juce_audio_formats/format/juce_AudioFormatWriter.h
+++ b/modules/juce_audio_formats/format/juce_AudioFormatWriter.h
@@ -93,6 +93,8 @@ protected:
unsigned int bitsPerSample);
public:
+ using Options = AudioFormatWriterOptions;
+
/** Destructor. */
virtual ~AudioFormatWriter();
diff --git a/modules/juce_audio_formats/format/juce_AudioFormatWriterOptions.h b/modules/juce_audio_formats/format/juce_AudioFormatWriterOptions.h
new file mode 100644
index 0000000000..c5b2712499
--- /dev/null
+++ b/modules/juce_audio_formats/format/juce_AudioFormatWriterOptions.h
@@ -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& 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 channelLayout = std::nullopt;
+ int bitsPerSample = 16;
+ std::unordered_map metadataValues;
+ int qualityOptionIndex = 0;
+ SampleFormat sampleFormat = SampleFormat::automatic;
+};
+
+} // namespace juce
diff --git a/modules/juce_audio_formats/juce_audio_formats.h b/modules/juce_audio_formats/juce_audio_formats.h
index e8030e0cc3..52ed9a82b2 100644
--- a/modules/juce_audio_formats/juce_audio_formats.h
+++ b/modules/juce_audio_formats/juce_audio_formats.h
@@ -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"