32#if JUCE_MAC && ! defined (__MACOSX__)
36namespace OggVorbisNamespace
38#if JUCE_INCLUDE_OGGVORBIS_CODE || ! defined (JUCE_INCLUDE_OGGVORBIS_CODE)
40 #pragma warning (push)
41 #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706 4995 4365 4456 4457 4459)
43 #pragma clang diagnostic push
44 #pragma clang diagnostic ignored "-Wconversion"
45 #pragma clang diagnostic ignored "-Wshadow"
46 #pragma clang diagnostic ignored "-Wdeprecated-register"
47 #if __has_warning("-Wzero-as-null-pointer-constant")
48 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
51 #pragma GCC diagnostic push
52 #pragma GCC diagnostic ignored "-Wconversion"
53 #pragma GCC diagnostic ignored "-Wshadow"
54 #pragma GCC diagnostic ignored "-Wsign-conversion"
55 #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
58 #include "oggvorbis/vorbisenc.h"
59 #include "oggvorbis/codec.h"
60 #include "oggvorbis/vorbisfile.h"
62 #include "oggvorbis/bitwise.c"
63 #include "oggvorbis/framing.c"
64 #include "oggvorbis/libvorbis-1.3.2/lib/analysis.c"
65 #include "oggvorbis/libvorbis-1.3.2/lib/bitrate.c"
66 #include "oggvorbis/libvorbis-1.3.2/lib/block.c"
67 #include "oggvorbis/libvorbis-1.3.2/lib/codebook.c"
68 #include "oggvorbis/libvorbis-1.3.2/lib/envelope.c"
69 #include "oggvorbis/libvorbis-1.3.2/lib/floor0.c"
70 #include "oggvorbis/libvorbis-1.3.2/lib/floor1.c"
71 #include "oggvorbis/libvorbis-1.3.2/lib/info.c"
72 #include "oggvorbis/libvorbis-1.3.2/lib/lpc.c"
73 #include "oggvorbis/libvorbis-1.3.2/lib/lsp.c"
74 #include "oggvorbis/libvorbis-1.3.2/lib/mapping0.c"
75 #include "oggvorbis/libvorbis-1.3.2/lib/mdct.c"
76 #include "oggvorbis/libvorbis-1.3.2/lib/psy.c"
77 #include "oggvorbis/libvorbis-1.3.2/lib/registry.c"
78 #include "oggvorbis/libvorbis-1.3.2/lib/res0.c"
79 #include "oggvorbis/libvorbis-1.3.2/lib/sharedbook.c"
80 #include "oggvorbis/libvorbis-1.3.2/lib/smallft.c"
81 #include "oggvorbis/libvorbis-1.3.2/lib/synthesis.c"
82 #include "oggvorbis/libvorbis-1.3.2/lib/vorbisenc.c"
83 #include "oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c"
84 #include "oggvorbis/libvorbis-1.3.2/lib/window.c"
89 #pragma clang diagnostic pop
91 #pragma GCC diagnostic pop
94 #include <vorbis/vorbisenc.h>
95 #include <vorbis/codec.h>
96 #include <vorbis/vorbisfile.h>
104static const char*
const oggFormatName =
"Ogg-Vorbis file";
106const char*
const OggVorbisAudioFormat::encoderName =
"encoder";
107const char*
const OggVorbisAudioFormat::id3title =
"id3title";
108const char*
const OggVorbisAudioFormat::id3artist =
"id3artist";
109const char*
const OggVorbisAudioFormat::id3album =
"id3album";
110const char*
const OggVorbisAudioFormat::id3comment =
"id3comment";
111const char*
const OggVorbisAudioFormat::id3date =
"id3date";
112const char*
const OggVorbisAudioFormat::id3genre =
"id3genre";
113const char*
const OggVorbisAudioFormat::id3trackNumber =
"id3trackNumber";
117class OggReader :
public AudioFormatReader
120 OggReader (InputStream* inp) : AudioFormatReader (inp, oggFormatName)
123 usesFloatingPointData =
true;
125 callbacks.read_func = &oggReadCallback;
126 callbacks.seek_func = &oggSeekCallback;
127 callbacks.close_func = &oggCloseCallback;
128 callbacks.tell_func = &oggTellCallback;
130 auto err = ov_open_callbacks (input, &ovFile,
nullptr, 0, callbacks);
134 auto* info = ov_info (&ovFile, -1);
136 auto* comment = ov_comment (&ovFile, -1);
137 addMetadataItem (comment,
"ENCODER", OggVorbisAudioFormat::encoderName);
138 addMetadataItem (comment,
"TITLE", OggVorbisAudioFormat::id3title);
139 addMetadataItem (comment,
"ARTIST", OggVorbisAudioFormat::id3artist);
140 addMetadataItem (comment,
"ALBUM", OggVorbisAudioFormat::id3album);
141 addMetadataItem (comment,
"COMMENT", OggVorbisAudioFormat::id3comment);
142 addMetadataItem (comment,
"DATE", OggVorbisAudioFormat::id3date);
143 addMetadataItem (comment,
"GENRE", OggVorbisAudioFormat::id3genre);
144 addMetadataItem (comment,
"TRACKNUMBER", OggVorbisAudioFormat::id3trackNumber);
146 lengthInSamples = (uint32) ov_pcm_total (&ovFile, -1);
147 numChannels = (
unsigned int) info->channels;
149 sampleRate = info->rate;
151 reservoir.setSize ((
int) numChannels, (
int) jmin (lengthInSamples, (int64) 4096));
155 ~OggReader()
override
160 void addMetadataItem (OggVorbisNamespace::vorbis_comment* comment,
const char* name,
const char* metadataName)
162 if (
auto* value = vorbis_comment_query (comment, name, 0))
163 metadataValues.set (metadataName, value);
167 bool readSamples (
int** destSamples,
int numDestChannels,
int startOffsetInDestBuffer,
168 int64 startSampleInFile,
int numSamples)
override
170 while (numSamples > 0)
172 auto numAvailable = (int) (reservoirStart + samplesInReservoir - startSampleInFile);
174 if (startSampleInFile >= reservoirStart && numAvailable > 0)
178 auto numToUse = jmin (numSamples, numAvailable);
180 for (
int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
181 if (destSamples[i] !=
nullptr)
182 memcpy (destSamples[i] + startOffsetInDestBuffer,
183 reservoir.getReadPointer (i, (
int) (startSampleInFile - reservoirStart)),
184 (
size_t) numToUse * sizeof (
float));
186 startSampleInFile += numToUse;
187 numSamples -= numToUse;
188 startOffsetInDestBuffer += numToUse;
194 if (startSampleInFile < reservoirStart
195 || startSampleInFile + numSamples > reservoirStart + samplesInReservoir)
198 reservoirStart = jmax (0, (
int) startSampleInFile);
199 samplesInReservoir = reservoir.getNumSamples();
201 if (reservoirStart != (
int) ov_pcm_tell (&ovFile))
202 ov_pcm_seek (&ovFile, reservoirStart);
206 int numToRead = samplesInReservoir;
208 while (numToRead > 0)
210 float** dataIn =
nullptr;
211 auto samps = ov_read_float (&ovFile, &dataIn, numToRead, &bitStream);
216 jassert (samps <= numToRead);
218 for (
int i = jmin ((
int) numChannels, reservoir.getNumChannels()); --i >= 0;)
219 memcpy (reservoir.getWritePointer (i, offset), dataIn[i], (
size_t) samps * sizeof (
float));
226 reservoir.clear (offset, numToRead);
232 for (
int i = numDestChannels; --i >= 0;)
233 if (destSamples[i] !=
nullptr)
234 zeromem (destSamples[i] + startOffsetInDestBuffer, (
size_t) numSamples *
sizeof (
int));
241 static size_t oggReadCallback (
void* ptr,
size_t size,
size_t nmemb,
void* datasource)
243 return (
size_t) (
static_cast<InputStream*
> (datasource)->read (ptr, (
int) (size * nmemb))) / size;
246 static int oggSeekCallback (
void* datasource, OggVorbisNamespace::ogg_int64_t offset,
int whence)
248 auto* in =
static_cast<InputStream*
> (datasource);
250 if (whence == SEEK_CUR)
251 offset += in->getPosition();
252 else if (whence == SEEK_END)
253 offset += in->getTotalLength();
255 in->setPosition (offset);
259 static int oggCloseCallback (
void*)
264 static long oggTellCallback (
void* datasource)
266 return (
long)
static_cast<InputStream*
> (datasource)->getPosition();
270 OggVorbisNamespace::OggVorbis_File ovFile;
271 OggVorbisNamespace::ov_callbacks callbacks;
272 AudioBuffer<float> reservoir;
273 int reservoirStart = 0, samplesInReservoir = 0;
275 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader)
279class OggWriter :
public AudioFormatWriter
282 OggWriter (OutputStream* out,
double rate,
283 unsigned int numChans,
unsigned int bitsPerSamp,
284 int qualityIndex,
const StringPairArray& metadata)
285 : AudioFormatWriter (out, oggFormatName, rate, numChans, bitsPerSamp)
287 vorbis_info_init (&vi);
289 if (vorbis_encode_init_vbr (&vi, (
int) numChans, (
int) rate,
290 jlimit (0.0f, 1.0f, qualityIndex * 0.1f)) == 0)
292 vorbis_comment_init (&vc);
294 addMetadata (metadata, OggVorbisAudioFormat::encoderName,
"ENCODER");
295 addMetadata (metadata, OggVorbisAudioFormat::id3title,
"TITLE");
296 addMetadata (metadata, OggVorbisAudioFormat::id3artist,
"ARTIST");
297 addMetadata (metadata, OggVorbisAudioFormat::id3album,
"ALBUM");
298 addMetadata (metadata, OggVorbisAudioFormat::id3comment,
"COMMENT");
299 addMetadata (metadata, OggVorbisAudioFormat::id3date,
"DATE");
300 addMetadata (metadata, OggVorbisAudioFormat::id3genre,
"GENRE");
301 addMetadata (metadata, OggVorbisAudioFormat::id3trackNumber,
"TRACKNUMBER");
303 vorbis_analysis_init (&vd, &vi);
304 vorbis_block_init (&vd, &vb);
306 ogg_stream_init (&os, Random::getSystemRandom().nextInt());
308 OggVorbisNamespace::ogg_packet header, header_comm, header_code;
309 vorbis_analysis_headerout (&vd, &vc, &header, &header_comm, &header_code);
311 ogg_stream_packetin (&os, &header);
312 ogg_stream_packetin (&os, &header_comm);
313 ogg_stream_packetin (&os, &header_code);
317 if (ogg_stream_flush (&os, &og) == 0)
320 output->write (og.header, (
size_t) og.header_len);
321 output->write (og.body, (
size_t) og.body_len);
328 ~OggWriter()
override
335 ogg_stream_clear (&os);
336 vorbis_block_clear (&vb);
337 vorbis_dsp_clear (&vd);
338 vorbis_comment_clear (&vc);
340 vorbis_info_clear (&vi);
345 vorbis_info_clear (&vi);
352 bool write (
const int** samplesToWrite,
int numSamples)
override
358 const double gain = 1.0 / 0x80000000u;
359 float**
const vorbisBuffer = vorbis_analysis_buffer (&vd, numSamples);
361 for (
int i = (
int) numChannels; --i >= 0;)
363 if (
auto* dst = vorbisBuffer[i])
365 if (
const int* src = samplesToWrite [i])
367 for (
int j = 0; j < numSamples; ++j)
368 dst[j] = (
float) (src[j] * gain);
374 writeSamples (numSamples);
380 void writeSamples (
int numSamples)
382 vorbis_analysis_wrote (&vd, numSamples);
384 while (vorbis_analysis_blockout (&vd, &vb) == 1)
386 vorbis_analysis (&vb,
nullptr);
387 vorbis_bitrate_addblock (&vb);
389 while (vorbis_bitrate_flushpacket (&vd, &op))
391 ogg_stream_packetin (&os, &op);
395 if (ogg_stream_pageout (&os, &og) == 0)
398 output->write (og.header, (
size_t) og.header_len);
399 output->write (og.body, (
size_t) og.body_len);
401 if (ogg_page_eos (&og))
411 OggVorbisNamespace::ogg_stream_state os;
412 OggVorbisNamespace::ogg_page og;
413 OggVorbisNamespace::ogg_packet op;
414 OggVorbisNamespace::vorbis_info vi;
415 OggVorbisNamespace::vorbis_comment vc;
416 OggVorbisNamespace::vorbis_dsp_state vd;
417 OggVorbisNamespace::vorbis_block vb;
419 void addMetadata (
const StringPairArray& metadata,
const char* name,
const char* vorbisName)
421 auto s = metadata [name];
424 vorbis_comment_add_tag (&vc, vorbisName,
const_cast<char*
> (s.toRawUTF8()));
427 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggWriter)
432OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName,
".ogg")
436OggVorbisAudioFormat::~OggVorbisAudioFormat()
440Array<int> OggVorbisAudioFormat::getPossibleSampleRates()
442 return { 8000, 11025, 12000, 16000, 22050, 32000,
443 44100, 48000, 88200, 96000, 176400, 192000 };
446Array<int> OggVorbisAudioFormat::getPossibleBitDepths()
451bool OggVorbisAudioFormat::canDoStereo() {
return true; }
452bool OggVorbisAudioFormat::canDoMono() {
return true; }
453bool OggVorbisAudioFormat::isCompressed() {
return true; }
455AudioFormatReader* OggVorbisAudioFormat::createReaderFor (InputStream* in,
bool deleteStreamIfOpeningFails)
457 std::unique_ptr<OggReader> r (
new OggReader (in));
459 if (r->sampleRate > 0)
462 if (! deleteStreamIfOpeningFails)
468AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out,
470 unsigned int numChannels,
472 const StringPairArray& metadataValues,
473 int qualityOptionIndex)
478 std::unique_ptr<OggWriter> w (
new OggWriter (out, sampleRate, numChannels,
479 (
unsigned int) bitsPerSample,
480 qualityOptionIndex, metadataValues));
482 return w->ok ? w.release() :
nullptr;
485StringArray OggVorbisAudioFormat::getQualityOptions()
487 return {
"64 kbps",
"80 kbps",
"96 kbps",
"112 kbps",
"128 kbps",
"160 kbps",
488 "192 kbps",
"224 kbps",
"256 kbps",
"320 kbps",
"500 kbps" };
491int OggVorbisAudioFormat::estimateOggFileQuality (
const File& source)
493 if (
auto* in = source.createInputStream())
495 if (
auto r = std::unique_ptr<AudioFormatReader> (createReaderFor (in,
true)))
497 auto lengthSecs = r->lengthInSamples / r->sampleRate;
498 auto approxBitsPerSecond = (int) (source.getSize() * 8 / lengthSecs);
500 auto qualities = getQualityOptions();
502 int bestDiff = 10000;
504 for (
int i = qualities.size(); --i >= 0;)
506 auto diff = std::abs (qualities[i].getIntValue() - approxBitsPerSecond);