30 #if JUCE_USE_OGGVORBIS 32 #if JUCE_MAC && ! defined (__MACOSX__) 36 namespace 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 "-Wshadow" 55 #include "oggvorbis/vorbisenc.h" 56 #include "oggvorbis/codec.h" 57 #include "oggvorbis/vorbisfile.h" 59 #include "oggvorbis/bitwise.c" 60 #include "oggvorbis/framing.c" 61 #include "oggvorbis/libvorbis-1.3.2/lib/analysis.c" 62 #include "oggvorbis/libvorbis-1.3.2/lib/bitrate.c" 63 #include "oggvorbis/libvorbis-1.3.2/lib/block.c" 64 #include "oggvorbis/libvorbis-1.3.2/lib/codebook.c" 65 #include "oggvorbis/libvorbis-1.3.2/lib/envelope.c" 66 #include "oggvorbis/libvorbis-1.3.2/lib/floor0.c" 67 #include "oggvorbis/libvorbis-1.3.2/lib/floor1.c" 68 #include "oggvorbis/libvorbis-1.3.2/lib/info.c" 69 #include "oggvorbis/libvorbis-1.3.2/lib/lpc.c" 70 #include "oggvorbis/libvorbis-1.3.2/lib/lsp.c" 71 #include "oggvorbis/libvorbis-1.3.2/lib/mapping0.c" 72 #include "oggvorbis/libvorbis-1.3.2/lib/mdct.c" 73 #include "oggvorbis/libvorbis-1.3.2/lib/psy.c" 74 #include "oggvorbis/libvorbis-1.3.2/lib/registry.c" 75 #include "oggvorbis/libvorbis-1.3.2/lib/res0.c" 76 #include "oggvorbis/libvorbis-1.3.2/lib/sharedbook.c" 77 #include "oggvorbis/libvorbis-1.3.2/lib/smallft.c" 78 #include "oggvorbis/libvorbis-1.3.2/lib/synthesis.c" 79 #include "oggvorbis/libvorbis-1.3.2/lib/vorbisenc.c" 80 #include "oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c" 81 #include "oggvorbis/libvorbis-1.3.2/lib/window.c" 86 #pragma clang diagnostic pop 88 #pragma GCC diagnostic pop 91 #include <vorbis/vorbisenc.h> 92 #include <vorbis/codec.h> 93 #include <vorbis/vorbisfile.h> 101 static const char*
const oggFormatName =
"Ogg-Vorbis file";
114 class OggReader :
public AudioFormatReader
117 OggReader (InputStream* inp) : AudioFormatReader (inp, oggFormatName)
120 usesFloatingPointData =
true;
122 callbacks.read_func = &oggReadCallback;
123 callbacks.seek_func = &oggSeekCallback;
124 callbacks.close_func = &oggCloseCallback;
125 callbacks.tell_func = &oggTellCallback;
127 auto err = ov_open_callbacks (input, &ovFile,
nullptr, 0, callbacks);
131 auto* info = ov_info (&ovFile, -1);
133 auto* comment = ov_comment (&ovFile, -1);
134 addMetadataItem (comment,
"ENCODER", OggVorbisAudioFormat::encoderName);
135 addMetadataItem (comment,
"TITLE", OggVorbisAudioFormat::id3title);
136 addMetadataItem (comment,
"ARTIST", OggVorbisAudioFormat::id3artist);
137 addMetadataItem (comment,
"ALBUM", OggVorbisAudioFormat::id3album);
138 addMetadataItem (comment,
"COMMENT", OggVorbisAudioFormat::id3comment);
139 addMetadataItem (comment,
"DATE", OggVorbisAudioFormat::id3date);
140 addMetadataItem (comment,
"GENRE", OggVorbisAudioFormat::id3genre);
141 addMetadataItem (comment,
"TRACKNUMBER", OggVorbisAudioFormat::id3trackNumber);
143 lengthInSamples = (uint32) ov_pcm_total (&ovFile, -1);
144 numChannels = (
unsigned int) info->channels;
146 sampleRate = info->rate;
148 reservoir.setSize ((
int) numChannels, (
int) jmin (lengthInSamples, (int64) 4096));
152 ~OggReader()
override 157 void addMetadataItem (OggVorbisNamespace::vorbis_comment* comment,
const char* name,
const char* metadataName)
159 if (
auto* value = vorbis_comment_query (comment, name, 0))
160 metadataValues.set (metadataName, value);
164 bool readSamples (
int** destSamples,
int numDestChannels,
int startOffsetInDestBuffer,
165 int64 startSampleInFile,
int numSamples)
override 167 while (numSamples > 0)
169 auto numAvailable = (int) (reservoirStart + samplesInReservoir - startSampleInFile);
171 if (startSampleInFile >= reservoirStart && numAvailable > 0)
175 auto numToUse = jmin (numSamples, numAvailable);
177 for (
int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
178 if (destSamples[i] !=
nullptr)
179 memcpy (destSamples[i] + startOffsetInDestBuffer,
180 reservoir.getReadPointer (i, (
int) (startSampleInFile - reservoirStart)),
181 sizeof (float) * (
size_t) numToUse);
183 startSampleInFile += numToUse;
184 numSamples -= numToUse;
185 startOffsetInDestBuffer += numToUse;
191 if (startSampleInFile < reservoirStart
192 || startSampleInFile + numSamples > reservoirStart + samplesInReservoir)
195 reservoirStart = jmax (0, (
int) startSampleInFile);
196 samplesInReservoir = reservoir.getNumSamples();
198 if (reservoirStart != (
int) ov_pcm_tell (&ovFile))
199 ov_pcm_seek (&ovFile, reservoirStart);
203 int numToRead = samplesInReservoir;
205 while (numToRead > 0)
207 float** dataIn =
nullptr;
208 auto samps = ov_read_float (&ovFile, &dataIn, numToRead, &bitStream);
213 jassert (samps <= numToRead);
215 for (
int i = jmin ((
int) numChannels, reservoir.getNumChannels()); --i >= 0;)
216 memcpy (reservoir.getWritePointer (i, offset), dataIn[i],
sizeof (float) * (
size_t) samps);
223 reservoir.clear (offset, numToRead);
229 for (
int i = numDestChannels; --i >= 0;)
230 if (destSamples[i] !=
nullptr)
231 zeromem (destSamples[i] + startOffsetInDestBuffer,
sizeof (
int) * (
size_t) numSamples);
238 static size_t oggReadCallback (
void* ptr,
size_t size,
size_t nmemb,
void* datasource)
240 return (
size_t) (
static_cast<InputStream*
> (datasource)->read (ptr, (
int) (size * nmemb))) / size;
243 static int oggSeekCallback (
void* datasource, OggVorbisNamespace::ogg_int64_t offset,
int whence)
245 auto* in =
static_cast<InputStream*
> (datasource);
247 if (whence == SEEK_CUR)
248 offset += in->getPosition();
249 else if (whence == SEEK_END)
250 offset += in->getTotalLength();
252 in->setPosition (offset);
256 static int oggCloseCallback (
void*)
261 static long oggTellCallback (
void* datasource)
263 return (
long)
static_cast<InputStream*
> (datasource)->getPosition();
267 OggVorbisNamespace::OggVorbis_File ovFile;
268 OggVorbisNamespace::ov_callbacks callbacks;
269 AudioBuffer<float> reservoir;
270 int reservoirStart = 0, samplesInReservoir = 0;
272 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader)
276 class OggWriter :
public AudioFormatWriter
279 OggWriter (OutputStream* out,
double rate,
280 unsigned int numChans,
unsigned int bitsPerSamp,
281 int qualityIndex,
const StringPairArray& metadata)
282 : AudioFormatWriter (out, oggFormatName, rate, numChans, bitsPerSamp)
284 vorbis_info_init (&vi);
286 if (vorbis_encode_init_vbr (&vi, (
int) numChans, (
int) rate,
287 jlimit (0.0f, 1.0f, qualityIndex * 0.1f)) == 0)
289 vorbis_comment_init (&vc);
291 addMetadata (metadata, OggVorbisAudioFormat::encoderName,
"ENCODER");
292 addMetadata (metadata, OggVorbisAudioFormat::id3title,
"TITLE");
293 addMetadata (metadata, OggVorbisAudioFormat::id3artist,
"ARTIST");
294 addMetadata (metadata, OggVorbisAudioFormat::id3album,
"ALBUM");
295 addMetadata (metadata, OggVorbisAudioFormat::id3comment,
"COMMENT");
296 addMetadata (metadata, OggVorbisAudioFormat::id3date,
"DATE");
297 addMetadata (metadata, OggVorbisAudioFormat::id3genre,
"GENRE");
298 addMetadata (metadata, OggVorbisAudioFormat::id3trackNumber,
"TRACKNUMBER");
300 vorbis_analysis_init (&vd, &vi);
301 vorbis_block_init (&vd, &vb);
305 OggVorbisNamespace::ogg_packet header, header_comm, header_code;
306 vorbis_analysis_headerout (&vd, &vc, &header, &header_comm, &header_code);
308 ogg_stream_packetin (&os, &header);
309 ogg_stream_packetin (&os, &header_comm);
310 ogg_stream_packetin (&os, &header_code);
314 if (ogg_stream_flush (&os, &og) == 0)
317 output->write (og.header, (
size_t) og.header_len);
318 output->write (og.body, (
size_t) og.body_len);
325 ~OggWriter()
override 332 ogg_stream_clear (&os);
333 vorbis_block_clear (&vb);
334 vorbis_dsp_clear (&vd);
335 vorbis_comment_clear (&vc);
337 vorbis_info_clear (&vi);
342 vorbis_info_clear (&vi);
349 bool write (
const int** samplesToWrite,
int numSamples)
override 355 const double gain = 1.0 / 0x80000000u;
356 float**
const vorbisBuffer = vorbis_analysis_buffer (&vd, numSamples);
358 for (
int i = (
int) numChannels; --i >= 0;)
360 if (
auto* dst = vorbisBuffer[i])
362 if (
const int* src = samplesToWrite [i])
364 for (
int j = 0; j < numSamples; ++j)
365 dst[j] = (
float) (src[j] * gain);
371 writeSamples (numSamples);
377 void writeSamples (
int numSamples)
379 vorbis_analysis_wrote (&vd, numSamples);
381 while (vorbis_analysis_blockout (&vd, &vb) == 1)
383 vorbis_analysis (&vb,
nullptr);
384 vorbis_bitrate_addblock (&vb);
386 while (vorbis_bitrate_flushpacket (&vd, &op))
388 ogg_stream_packetin (&os, &op);
392 if (ogg_stream_pageout (&os, &og) == 0)
395 output->write (og.header, (
size_t) og.header_len);
396 output->write (og.body, (
size_t) og.body_len);
398 if (ogg_page_eos (&og))
408 OggVorbisNamespace::ogg_stream_state os;
409 OggVorbisNamespace::ogg_page og;
410 OggVorbisNamespace::ogg_packet op;
411 OggVorbisNamespace::vorbis_info vi;
412 OggVorbisNamespace::vorbis_comment vc;
413 OggVorbisNamespace::vorbis_dsp_state vd;
414 OggVorbisNamespace::vorbis_block vb;
416 void addMetadata (
const StringPairArray& metadata,
const char* name,
const char* vorbisName)
418 auto s = metadata [name];
421 vorbis_comment_add_tag (&vc, vorbisName, const_cast<char*> (s.toRawUTF8()));
424 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggWriter)
429 OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName,
".ogg")
433 OggVorbisAudioFormat::~OggVorbisAudioFormat()
439 return { 8000, 11025, 12000, 16000, 22050, 32000,
440 44100, 48000, 88200, 96000, 176400, 192000 };
454 std::unique_ptr<OggReader> r (
new OggReader (in));
456 if (r->sampleRate > 0)
459 if (! deleteStreamIfOpeningFails)
467 unsigned int numChannels,
469 const StringPairArray& metadataValues,
470 int qualityOptionIndex)
475 std::unique_ptr<OggWriter> w (
new OggWriter (out, sampleRate, numChannels,
476 (
unsigned int) bitsPerSample,
477 qualityOptionIndex, metadataValues));
479 return w->ok ? w.release() :
nullptr;
484 return {
"64 kbps",
"80 kbps",
"96 kbps",
"112 kbps",
"128 kbps",
"160 kbps",
485 "192 kbps",
"224 kbps",
"256 kbps",
"320 kbps",
"500 kbps" };
490 if (
auto* in = source.createInputStream())
492 if (
auto r = std::unique_ptr<AudioFormatReader> (createReaderFor (in,
true)))
494 auto lengthSecs = r->lengthInSamples / r->sampleRate;
495 auto approxBitsPerSecond = (int) (source.getSize() * 8 / lengthSecs);
497 auto qualities = getQualityOptions();
499 int bestDiff = 10000;
501 for (
int i = qualities.size(); --i >= 0;)
503 auto diff = std::abs (qualities[i].getIntValue() - approxBitsPerSecond);
static Random & getSystemRandom() noexcept
The overhead of creating a new Random object is fairly small, but if you want to avoid it...