31#if JUCE_MAC && ! defined (__MACOSX__)
35namespace OggVorbisNamespace
37#if JUCE_INCLUDE_OGGVORBIS_CODE || ! defined (JUCE_INCLUDE_OGGVORBIS_CODE)
38 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706 4995 4365 4456 4457 4459 6297 6011 6001 6308 6255 6386 6385 6246 6387 6263 6262 28182)
40 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-align",
42 "-Wdeprecated-declarations",
43 "-Wdeprecated-register",
46 "-Wmaybe-uninitialized",
47 "-Wmisleading-indentation",
48 "-Wmissing-prototypes",
54 "-Wzero-as-null-pointer-constant")
55 JUCE_BEGIN_NO_SANITIZE ("undefined")
57 #include "oggvorbis/vorbisenc.h"
58 #include "oggvorbis/codec.h"
59 #include "oggvorbis/vorbisfile.h"
61 #include "oggvorbis/bitwise.c"
62 #include "oggvorbis/framing.c"
63 #include "oggvorbis/libvorbis-1.3.7/lib/analysis.c"
64 #include "oggvorbis/libvorbis-1.3.7/lib/bitrate.c"
65 #include "oggvorbis/libvorbis-1.3.7/lib/block.c"
66 #include "oggvorbis/libvorbis-1.3.7/lib/codebook.c"
67 #include "oggvorbis/libvorbis-1.3.7/lib/envelope.c"
68 #include "oggvorbis/libvorbis-1.3.7/lib/floor0.c"
69 #include "oggvorbis/libvorbis-1.3.7/lib/floor1.c"
70 #include "oggvorbis/libvorbis-1.3.7/lib/info.c"
71 #include "oggvorbis/libvorbis-1.3.7/lib/lpc.c"
72 #include "oggvorbis/libvorbis-1.3.7/lib/lsp.c"
73 #include "oggvorbis/libvorbis-1.3.7/lib/mapping0.c"
74 #include "oggvorbis/libvorbis-1.3.7/lib/mdct.c"
75 #include "oggvorbis/libvorbis-1.3.7/lib/psy.c"
76 #include "oggvorbis/libvorbis-1.3.7/lib/registry.c"
77 #include "oggvorbis/libvorbis-1.3.7/lib/res0.c"
78 #include "oggvorbis/libvorbis-1.3.7/lib/sharedbook.c"
79 #include "oggvorbis/libvorbis-1.3.7/lib/smallft.c"
80 #include "oggvorbis/libvorbis-1.3.7/lib/synthesis.c"
81 #include "oggvorbis/libvorbis-1.3.7/lib/vorbisenc.c"
82 #include "oggvorbis/libvorbis-1.3.7/lib/vorbisfile.c"
83 #include "oggvorbis/libvorbis-1.3.7/lib/window.c"
86 JUCE_END_IGNORE_WARNINGS_MSVC
87 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
89 #include <vorbis/vorbisenc.h>
90 #include <vorbis/codec.h>
91 #include <vorbis/vorbisfile.h>
99static const char*
const oggFormatName =
"Ogg-Vorbis file";
101const char*
const OggVorbisAudioFormat::encoderName =
"encoder";
102const char*
const OggVorbisAudioFormat::id3title =
"id3title";
103const char*
const OggVorbisAudioFormat::id3artist =
"id3artist";
104const char*
const OggVorbisAudioFormat::id3album =
"id3album";
105const char*
const OggVorbisAudioFormat::id3comment =
"id3comment";
106const char*
const OggVorbisAudioFormat::id3date =
"id3date";
107const char*
const OggVorbisAudioFormat::id3genre =
"id3genre";
108const char*
const OggVorbisAudioFormat::id3trackNumber =
"id3trackNumber";
112class OggReader final :
public AudioFormatReader
115 OggReader (InputStream* inp) : AudioFormatReader (inp, oggFormatName)
118 usesFloatingPointData =
true;
120 callbacks.read_func = &oggReadCallback;
121 callbacks.seek_func = &oggSeekCallback;
122 callbacks.close_func = &oggCloseCallback;
123 callbacks.tell_func = &oggTellCallback;
125 auto err = ov_open_callbacks (input, &ovFile,
nullptr, 0, callbacks);
129 auto* info = ov_info (&ovFile, -1);
131 auto* comment = ov_comment (&ovFile, -1);
132 addMetadataItem (comment,
"ENCODER", OggVorbisAudioFormat::encoderName);
133 addMetadataItem (comment,
"TITLE", OggVorbisAudioFormat::id3title);
134 addMetadataItem (comment,
"ARTIST", OggVorbisAudioFormat::id3artist);
135 addMetadataItem (comment,
"ALBUM", OggVorbisAudioFormat::id3album);
136 addMetadataItem (comment,
"COMMENT", OggVorbisAudioFormat::id3comment);
137 addMetadataItem (comment,
"DATE", OggVorbisAudioFormat::id3date);
138 addMetadataItem (comment,
"GENRE", OggVorbisAudioFormat::id3genre);
139 addMetadataItem (comment,
"TRACKNUMBER", OggVorbisAudioFormat::id3trackNumber);
141 lengthInSamples = (uint32) ov_pcm_total (&ovFile, -1);
142 numChannels = (
unsigned int) info->channels;
144 sampleRate = (double) info->rate;
146 reservoir.setSize ((
int) numChannels, (
int) jmin (lengthInSamples, (int64) 4096));
150 ~OggReader()
override
155 void addMetadataItem (OggVorbisNamespace::vorbis_comment* comment,
const char* name,
const char* metadataName)
157 if (
auto* value = vorbis_comment_query (comment, name, 0))
158 metadataValues.set (metadataName, value);
162 bool readSamples (
int*
const* destSamples,
int numDestChannels,
int startOffsetInDestBuffer,
163 int64 startSampleInFile,
int numSamples)
override
165 const auto getBufferedRange = [
this] {
return bufferedRange; };
167 const auto readFromReservoir = [
this, &destSamples, &numDestChannels, &startOffsetInDestBuffer, &startSampleInFile] (
const Range<int64> rangeToRead)
169 const auto bufferIndices = rangeToRead - bufferedRange.getStart();
170 const auto writePos = (int64) startOffsetInDestBuffer + (rangeToRead.getStart() - startSampleInFile);
172 for (
int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
173 if (destSamples[i] !=
nullptr)
174 memcpy (destSamples[i] + writePos,
175 reservoir.getReadPointer (i) + bufferIndices.getStart(),
176 (
size_t) bufferIndices.getLength() * sizeof (
float));
179 const auto fillReservoir = [
this] (int64 requestedStart)
181 const auto newStart = jmax ((int64) 0, requestedStart);
182 bufferedRange = Range<int64> { newStart, newStart + reservoir.getNumSamples() };
184 if (bufferedRange.getStart() != ov_pcm_tell (&ovFile))
185 ov_pcm_seek (&ovFile, bufferedRange.getStart());
189 int numToRead = (int) bufferedRange.getLength();
191 while (numToRead > 0)
193 float** dataIn =
nullptr;
194 auto samps =
static_cast<int> (ov_read_float (&ovFile, &dataIn, numToRead, &bitStream));
199 jassert (samps <= numToRead);
201 for (
int i = jmin ((
int) numChannels, reservoir.getNumChannels()); --i >= 0;)
202 memcpy (reservoir.getWritePointer (i, offset), dataIn[i], (size_t) samps *
sizeof (
float));
209 reservoir.clear (offset, numToRead);
212 const auto remainingSamples =
Reservoir::doBufferedRead (Range<int64> { startSampleInFile, startSampleInFile + numSamples },
217 if (! remainingSamples.isEmpty())
218 for (
int i = numDestChannels; --i >= 0;)
219 if (destSamples[i] !=
nullptr)
220 zeromem (destSamples[i] + startOffsetInDestBuffer + (remainingSamples.getStart() - startSampleInFile),
221 (
size_t) remainingSamples.getLength() * sizeof (
int));
227 static size_t oggReadCallback (
void* ptr,
size_t size,
size_t nmemb,
void* datasource)
229 return (
size_t) (
static_cast<InputStream*
> (datasource)->read (ptr, (
int) (size * nmemb))) / size;
232 static int oggSeekCallback (
void* datasource, OggVorbisNamespace::ogg_int64_t offset,
int whence)
234 auto* in =
static_cast<InputStream*
> (datasource);
236 if (whence == SEEK_CUR)
237 offset += in->getPosition();
238 else if (whence == SEEK_END)
239 offset += in->getTotalLength();
241 in->setPosition (offset);
245 static int oggCloseCallback (
void*)
250 static long oggTellCallback (
void* datasource)
252 return (
long)
static_cast<InputStream*
> (datasource)->getPosition();
256 OggVorbisNamespace::OggVorbis_File ovFile;
257 OggVorbisNamespace::ov_callbacks callbacks;
258 AudioBuffer<float> reservoir;
259 Range<int64> bufferedRange;
261 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader)
265class OggWriter final :
public AudioFormatWriter
268 OggWriter (OutputStream* out,
double rate,
269 unsigned int numChans,
unsigned int bitsPerSamp,
270 int qualityIndex,
const StringPairArray& metadata)
271 : AudioFormatWriter (out, oggFormatName, rate, numChans, bitsPerSamp)
273 vorbis_info_init (&vi);
275 if (vorbis_encode_init_vbr (&vi, (
int) numChans, (
int) rate,
276 jlimit (0.0f, 1.0f, (
float) qualityIndex * 0.1f)) == 0)
278 vorbis_comment_init (&vc);
280 addMetadata (metadata, OggVorbisAudioFormat::encoderName,
"ENCODER");
281 addMetadata (metadata, OggVorbisAudioFormat::id3title,
"TITLE");
282 addMetadata (metadata, OggVorbisAudioFormat::id3artist,
"ARTIST");
283 addMetadata (metadata, OggVorbisAudioFormat::id3album,
"ALBUM");
284 addMetadata (metadata, OggVorbisAudioFormat::id3comment,
"COMMENT");
285 addMetadata (metadata, OggVorbisAudioFormat::id3date,
"DATE");
286 addMetadata (metadata, OggVorbisAudioFormat::id3genre,
"GENRE");
287 addMetadata (metadata, OggVorbisAudioFormat::id3trackNumber,
"TRACKNUMBER");
289 vorbis_analysis_init (&vd, &vi);
290 vorbis_block_init (&vd, &vb);
294 OggVorbisNamespace::ogg_packet header, header_comm, header_code;
295 vorbis_analysis_headerout (&vd, &vc, &header, &header_comm, &header_code);
297 ogg_stream_packetin (&os, &header);
298 ogg_stream_packetin (&os, &header_comm);
299 ogg_stream_packetin (&os, &header_code);
303 if (ogg_stream_flush (&os, &og) == 0)
306 output->write (og.header, (
size_t) og.header_len);
307 output->write (og.body, (
size_t) og.body_len);
314 ~OggWriter()
override
321 ogg_stream_clear (&os);
322 vorbis_block_clear (&vb);
323 vorbis_dsp_clear (&vd);
324 vorbis_comment_clear (&vc);
326 vorbis_info_clear (&vi);
331 vorbis_info_clear (&vi);
338 bool write (
const int** samplesToWrite,
int numSamples)
override
344 const double gain = 1.0 / 0x80000000u;
345 float**
const vorbisBuffer = vorbis_analysis_buffer (&vd, numSamples);
347 for (
int i = (
int) numChannels; --i >= 0;)
349 if (
auto* dst = vorbisBuffer[i])
351 if (
const int* src = samplesToWrite [i])
353 for (
int j = 0; j < numSamples; ++j)
354 dst[j] = (
float) (src[j] * gain);
360 writeSamples (numSamples);
366 void writeSamples (
int numSamples)
368 vorbis_analysis_wrote (&vd, numSamples);
370 while (vorbis_analysis_blockout (&vd, &vb) == 1)
372 vorbis_analysis (&vb,
nullptr);
373 vorbis_bitrate_addblock (&vb);
375 while (vorbis_bitrate_flushpacket (&vd, &op))
377 ogg_stream_packetin (&os, &op);
381 if (ogg_stream_pageout (&os, &og) == 0)
384 output->write (og.header, (
size_t) og.header_len);
385 output->write (og.body, (
size_t) og.body_len);
387 if (ogg_page_eos (&og))
397 OggVorbisNamespace::ogg_stream_state os;
398 OggVorbisNamespace::ogg_page og;
399 OggVorbisNamespace::ogg_packet op;
400 OggVorbisNamespace::vorbis_info vi;
401 OggVorbisNamespace::vorbis_comment vc;
402 OggVorbisNamespace::vorbis_dsp_state vd;
403 OggVorbisNamespace::vorbis_block vb;
405 void addMetadata (
const StringPairArray& metadata,
const char* name,
const char* vorbisName)
407 auto s = metadata [name];
410 vorbis_comment_add_tag (&vc, vorbisName,
const_cast<char*
> (s.toRawUTF8()));
413 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggWriter)
418OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName,
".ogg")
422OggVorbisAudioFormat::~OggVorbisAudioFormat()
426Array<int> OggVorbisAudioFormat::getPossibleSampleRates()
428 return { 8000, 11025, 12000, 16000, 22050, 32000,
429 44100, 48000, 88200, 96000, 176400, 192000 };
432Array<int> OggVorbisAudioFormat::getPossibleBitDepths()
437bool OggVorbisAudioFormat::canDoStereo() {
return true; }
438bool OggVorbisAudioFormat::canDoMono() {
return true; }
439bool OggVorbisAudioFormat::isCompressed() {
return true; }
441AudioFormatReader* OggVorbisAudioFormat::createReaderFor (InputStream* in,
bool deleteStreamIfOpeningFails)
443 std::unique_ptr<OggReader> r (
new OggReader (in));
445 if (r->sampleRate > 0)
448 if (! deleteStreamIfOpeningFails)
454AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out,
456 unsigned int numChannels,
458 const StringPairArray& metadataValues,
459 int qualityOptionIndex)
464 std::unique_ptr<OggWriter> w (
new OggWriter (out, sampleRate, numChannels,
465 (
unsigned int) bitsPerSample,
466 qualityOptionIndex, metadataValues));
468 return w->ok ? w.release() :
nullptr;
471StringArray OggVorbisAudioFormat::getQualityOptions()
473 return {
"64 kbps",
"80 kbps",
"96 kbps",
"112 kbps",
"128 kbps",
"160 kbps",
474 "192 kbps",
"224 kbps",
"256 kbps",
"320 kbps",
"500 kbps" };
477int OggVorbisAudioFormat::estimateOggFileQuality (
const File& source)
479 if (
auto in = source.createInputStream())
481 if (
auto r = std::unique_ptr<AudioFormatReader> (createReaderFor (in.release(),
true)))
483 auto lengthSecs = (double) r->lengthInSamples / r->sampleRate;
484 auto approxBitsPerSecond = (int) ((
double) source.getSize() * 8 / lengthSecs);
486 auto qualities = getQualityOptions();
488 int bestDiff = 10000;
490 for (
int i = qualities.size(); --i >= 0;)
492 auto diff = std::abs (qualities[i].getIntValue() - approxBitsPerSecond);
static Random & getSystemRandom() noexcept
static Range< Index > doBufferedRead(Range< Index > rangeToRead, GetBufferedRange &&getBufferedRange, ReadFromReservoir &&readFromReservoir, FillReservoir &&fillReservoir)