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 "-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> 104 static const char*
const oggFormatName =
"Ogg-Vorbis file";
117 class 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)
279 class 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);
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)
432 OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName,
".ogg")
436 OggVorbisAudioFormat::~OggVorbisAudioFormat()
442 return { 8000, 11025, 12000, 16000, 22050, 32000,
443 44100, 48000, 88200, 96000, 176400, 192000 };
457 std::unique_ptr<OggReader> r (
new OggReader (in));
459 if (r->sampleRate > 0)
462 if (! deleteStreamIfOpeningFails)
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;
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" };
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);
static Random & getSystemRandom() noexcept
The overhead of creating a new Random object is fairly small, but if you want to avoid it...