2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../core/juce_TargetPlatform.h"
27 #include "../../../juce_Config.h"
29 #if JUCE_USE_OGGVORBIS
31 #include "../../core/juce_StandardHeader.h"
33 #if JUCE_MAC && ! defined (__MACOSX__)
39 namespace OggVorbisNamespace
41 #if JUCE_INCLUDE_OGGVORBIS_CODE
42 #include "oggvorbis/vorbisenc.h"
43 #include "oggvorbis/codec.h"
44 #include "oggvorbis/vorbisfile.h"
46 #include "oggvorbis/bitwise.c"
47 #include "oggvorbis/framing.c"
48 #include "oggvorbis/libvorbis-1.3.2/lib/analysis.c"
49 #include "oggvorbis/libvorbis-1.3.2/lib/bitrate.c"
50 #include "oggvorbis/libvorbis-1.3.2/lib/block.c"
51 #include "oggvorbis/libvorbis-1.3.2/lib/codebook.c"
52 #include "oggvorbis/libvorbis-1.3.2/lib/envelope.c"
53 #include "oggvorbis/libvorbis-1.3.2/lib/floor0.c"
54 #include "oggvorbis/libvorbis-1.3.2/lib/floor1.c"
55 #include "oggvorbis/libvorbis-1.3.2/lib/info.c"
56 #include "oggvorbis/libvorbis-1.3.2/lib/lpc.c"
57 #include "oggvorbis/libvorbis-1.3.2/lib/lsp.c"
58 #include "oggvorbis/libvorbis-1.3.2/lib/mapping0.c"
59 #include "oggvorbis/libvorbis-1.3.2/lib/mdct.c"
60 #include "oggvorbis/libvorbis-1.3.2/lib/psy.c"
61 #include "oggvorbis/libvorbis-1.3.2/lib/registry.c"
62 #include "oggvorbis/libvorbis-1.3.2/lib/res0.c"
63 #include "oggvorbis/libvorbis-1.3.2/lib/sharedbook.c"
64 #include "oggvorbis/libvorbis-1.3.2/lib/smallft.c"
65 #include "oggvorbis/libvorbis-1.3.2/lib/synthesis.c"
66 #include "oggvorbis/libvorbis-1.3.2/lib/vorbisenc.c"
67 #include "oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c"
68 #include "oggvorbis/libvorbis-1.3.2/lib/window.c"
70 #include <vorbis/vorbisenc.h>
71 #include <vorbis/codec.h>
72 #include <vorbis/vorbisfile.h>
79 #include "juce_OggVorbisAudioFormat.h"
80 #include "../../application/juce_Application.h"
81 #include "../../maths/juce_Random.h"
82 #include "../../io/files/juce_FileInputStream.h"
83 #include "../../text/juce_LocalisedStrings.h"
85 //==============================================================================
86 static const char* const oggFormatName
= "Ogg-Vorbis file";
87 static const char* const oggExtensions
[] = { ".ogg", 0 };
89 //==============================================================================
90 class OggReader
: public AudioFormatReader
93 //==============================================================================
94 OggReader (InputStream
* const inp
)
95 : AudioFormatReader (inp
, TRANS (oggFormatName
)),
98 samplesInReservoir (0)
100 using namespace OggVorbisNamespace
;
102 usesFloatingPointData
= true;
104 callbacks
.read_func
= &oggReadCallback
;
105 callbacks
.seek_func
= &oggSeekCallback
;
106 callbacks
.close_func
= &oggCloseCallback
;
107 callbacks
.tell_func
= &oggTellCallback
;
109 const int err
= ov_open_callbacks (input
, &ovFile
, 0, 0, callbacks
);
113 vorbis_info
* info
= ov_info (&ovFile
, -1);
114 lengthInSamples
= (uint32
) ov_pcm_total (&ovFile
, -1);
115 numChannels
= info
->channels
;
117 sampleRate
= info
->rate
;
119 reservoir
.setSize (numChannels
,
120 (int) jmin (lengthInSamples
, (int64
) reservoir
.getNumSamples()));
126 OggVorbisNamespace::ov_clear (&ovFile
);
129 //==============================================================================
130 bool readSamples (int** destSamples
, int numDestChannels
, int startOffsetInDestBuffer
,
131 int64 startSampleInFile
, int numSamples
)
133 while (numSamples
> 0)
135 const int numAvailable
= reservoirStart
+ samplesInReservoir
- startSampleInFile
;
137 if (startSampleInFile
>= reservoirStart
&& numAvailable
> 0)
139 // got a few samples overlapping, so use them before seeking..
141 const int numToUse
= jmin (numSamples
, numAvailable
);
143 for (int i
= jmin (numDestChannels
, reservoir
.getNumChannels()); --i
>= 0;)
144 if (destSamples
[i
] != nullptr)
145 memcpy (destSamples
[i
] + startOffsetInDestBuffer
,
146 reservoir
.getSampleData (i
, (int) (startSampleInFile
- reservoirStart
)),
147 sizeof (float) * numToUse
);
149 startSampleInFile
+= numToUse
;
150 numSamples
-= numToUse
;
151 startOffsetInDestBuffer
+= numToUse
;
157 if (startSampleInFile
< reservoirStart
158 || startSampleInFile
+ numSamples
> reservoirStart
+ samplesInReservoir
)
160 // buffer miss, so refill the reservoir
163 reservoirStart
= jmax (0, (int) startSampleInFile
);
164 samplesInReservoir
= reservoir
.getNumSamples();
166 if (reservoirStart
!= (int) OggVorbisNamespace::ov_pcm_tell (&ovFile
))
167 OggVorbisNamespace::ov_pcm_seek (&ovFile
, reservoirStart
);
170 int numToRead
= samplesInReservoir
;
172 while (numToRead
> 0)
174 float** dataIn
= nullptr;
176 const int samps
= OggVorbisNamespace::ov_read_float (&ovFile
, &dataIn
, numToRead
, &bitStream
);
180 jassert (samps
<= numToRead
);
182 for (int i
= jmin ((int) numChannels
, reservoir
.getNumChannels()); --i
>= 0;)
184 memcpy (reservoir
.getSampleData (i
, offset
),
186 sizeof (float) * samps
);
194 reservoir
.clear (offset
, numToRead
);
200 for (int i
= numDestChannels
; --i
>= 0;)
201 if (destSamples
[i
] != nullptr)
202 zeromem (destSamples
[i
] + startOffsetInDestBuffer
,
203 sizeof (int) * numSamples
);
209 //==============================================================================
210 static size_t oggReadCallback (void* ptr
, size_t size
, size_t nmemb
, void* datasource
)
212 return (size_t) (static_cast <InputStream
*> (datasource
)->read (ptr
, (int) (size
* nmemb
)) / size
);
215 static int oggSeekCallback (void* datasource
, OggVorbisNamespace::ogg_int64_t offset
, int whence
)
217 InputStream
* const in
= static_cast <InputStream
*> (datasource
);
219 if (whence
== SEEK_CUR
)
220 offset
+= in
->getPosition();
221 else if (whence
== SEEK_END
)
222 offset
+= in
->getTotalLength();
224 in
->setPosition (offset
);
228 static int oggCloseCallback (void*)
233 static long oggTellCallback (void* datasource
)
235 return (long) static_cast <InputStream
*> (datasource
)->getPosition();
239 OggVorbisNamespace::OggVorbis_File ovFile
;
240 OggVorbisNamespace::ov_callbacks callbacks
;
241 AudioSampleBuffer reservoir
;
242 int reservoirStart
, samplesInReservoir
;
244 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader
);
247 //==============================================================================
248 class OggWriter
: public AudioFormatWriter
251 //==============================================================================
252 OggWriter (OutputStream
* const out
,
253 const double sampleRate_
,
254 const int numChannels_
,
255 const int bitsPerSample_
,
256 const int qualityIndex
)
257 : AudioFormatWriter (out
, TRANS (oggFormatName
), sampleRate_
, numChannels_
, bitsPerSample_
),
260 using namespace OggVorbisNamespace
;
262 vorbis_info_init (&vi
);
264 if (vorbis_encode_init_vbr (&vi
, numChannels_
, (int) sampleRate_
,
265 jlimit (0.0f
, 1.0f
, qualityIndex
* 0.1f
)) == 0)
267 vorbis_comment_init (&vc
);
269 if (JUCEApplication::getInstance() != nullptr)
270 vorbis_comment_add_tag (&vc
, "ENCODER", const_cast <char*> (JUCEApplication::getInstance()->getApplicationName().toUTF8().getAddress()));
272 vorbis_analysis_init (&vd
, &vi
);
273 vorbis_block_init (&vd
, &vb
);
275 ogg_stream_init (&os
, Random::getSystemRandom().nextInt());
278 ogg_packet header_comm
;
279 ogg_packet header_code
;
281 vorbis_analysis_headerout (&vd
, &vc
, &header
, &header_comm
, &header_code
);
283 ogg_stream_packetin (&os
, &header
);
284 ogg_stream_packetin (&os
, &header_comm
);
285 ogg_stream_packetin (&os
, &header_code
);
289 if (ogg_stream_flush (&os
, &og
) == 0)
292 output
->write (og
.header
, og
.header_len
);
293 output
->write (og
.body
, og
.body_len
);
302 using namespace OggVorbisNamespace
;
305 // write a zero-length packet to show ogg that we're finished..
308 ogg_stream_clear (&os
);
309 vorbis_block_clear (&vb
);
310 vorbis_dsp_clear (&vd
);
311 vorbis_comment_clear (&vc
);
313 vorbis_info_clear (&vi
);
318 vorbis_info_clear (&vi
);
319 output
= nullptr; // to stop the base class deleting this, as it needs to be returned
320 // to the caller of createWriter()
324 //==============================================================================
325 bool write (const int** samplesToWrite
, int numSamples
)
329 using namespace OggVorbisNamespace
;
333 const double gain
= 1.0 / 0x80000000u
;
334 float** const vorbisBuffer
= vorbis_analysis_buffer (&vd
, numSamples
);
336 for (int i
= numChannels
; --i
>= 0;)
338 float* const dst
= vorbisBuffer
[i
];
339 const int* const src
= samplesToWrite
[i
];
341 if (src
!= nullptr && dst
!= nullptr)
343 for (int j
= 0; j
< numSamples
; ++j
)
344 dst
[j
] = (float) (src
[j
] * gain
);
349 writeSamples (numSamples
);
355 void writeSamples (int numSamples
)
357 using namespace OggVorbisNamespace
;
359 vorbis_analysis_wrote (&vd
, numSamples
);
361 while (vorbis_analysis_blockout (&vd
, &vb
) == 1)
363 vorbis_analysis (&vb
, 0);
364 vorbis_bitrate_addblock (&vb
);
366 while (vorbis_bitrate_flushpacket (&vd
, &op
))
368 ogg_stream_packetin (&os
, &op
);
372 if (ogg_stream_pageout (&os
, &og
) == 0)
375 output
->write (og
.header
, og
.header_len
);
376 output
->write (og
.body
, og
.body_len
);
378 if (ogg_page_eos (&og
))
388 OggVorbisNamespace::ogg_stream_state os
;
389 OggVorbisNamespace::ogg_page og
;
390 OggVorbisNamespace::ogg_packet op
;
391 OggVorbisNamespace::vorbis_info vi
;
392 OggVorbisNamespace::vorbis_comment vc
;
393 OggVorbisNamespace::vorbis_dsp_state vd
;
394 OggVorbisNamespace::vorbis_block vb
;
396 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggWriter
);
400 //==============================================================================
401 OggVorbisAudioFormat::OggVorbisAudioFormat()
402 : AudioFormat (TRANS (oggFormatName
), StringArray (oggExtensions
))
406 OggVorbisAudioFormat::~OggVorbisAudioFormat()
410 const Array
<int> OggVorbisAudioFormat::getPossibleSampleRates()
412 const int rates
[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 };
413 return Array
<int> (rates
);
416 const Array
<int> OggVorbisAudioFormat::getPossibleBitDepths()
418 const int depths
[] = { 32, 0 };
419 return Array
<int> (depths
);
422 bool OggVorbisAudioFormat::canDoStereo() { return true; }
423 bool OggVorbisAudioFormat::canDoMono() { return true; }
424 bool OggVorbisAudioFormat::isCompressed() { return true; }
426 AudioFormatReader
* OggVorbisAudioFormat::createReaderFor (InputStream
* in
,
427 const bool deleteStreamIfOpeningFails
)
429 ScopedPointer
<OggReader
> r (new OggReader (in
));
431 if (r
->sampleRate
> 0)
434 if (! deleteStreamIfOpeningFails
)
440 AudioFormatWriter
* OggVorbisAudioFormat::createWriterFor (OutputStream
* out
,
442 unsigned int numChannels
,
444 const StringPairArray
& /*metadataValues*/,
445 int qualityOptionIndex
)
447 ScopedPointer
<OggWriter
> w (new OggWriter (out
,
451 qualityOptionIndex
));
453 return w
->ok
? w
.release() : nullptr;
456 StringArray
OggVorbisAudioFormat::getQualityOptions()
458 const char* options
[] = { "64 kbps", "80 kbps", "96 kbps", "112 kbps", "128 kbps", "160 kbps",
459 "192 kbps", "224 kbps", "256 kbps", "320 kbps", "500 kbps", 0 };
460 return StringArray (options
);
463 int OggVorbisAudioFormat::estimateOggFileQuality (const File
& source
)
465 FileInputStream
* const in
= source
.createInputStream();
469 ScopedPointer
<AudioFormatReader
> r (createReaderFor (in
, true));
473 const int64 numSamps
= r
->lengthInSamples
;
476 const int64 fileNumSamps
= source
.getSize() / 4;
477 const double ratio
= numSamps
/ (double) fileNumSamps
;
481 else if (ratio
> 6.0)