aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llaudio/llvorbisencode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/llaudio/llvorbisencode.cpp')
-rw-r--r--linden/indra/llaudio/llvorbisencode.cpp505
1 files changed, 505 insertions, 0 deletions
diff --git a/linden/indra/llaudio/llvorbisencode.cpp b/linden/indra/llaudio/llvorbisencode.cpp
new file mode 100644
index 0000000..a24394d
--- /dev/null
+++ b/linden/indra/llaudio/llvorbisencode.cpp
@@ -0,0 +1,505 @@
1/**
2 * @file vorbisencode.cpp
3 * @brief Vorbis encoding routine routine for Indra.
4 *
5 * $LicenseInfo:firstyear=2000&license=viewergpl$
6 *
7 * Copyright (c) 2000-2009, Linden Research, Inc.
8 *
9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab
11 * to you under the terms of the GNU General Public License, version 2.0
12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
16 *
17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at
21 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
22 *
23 * By copying, modifying or distributing this software, you acknowledge
24 * that you have read and understood your obligations described above,
25 * and agree to abide by those obligations.
26 *
27 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
28 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
29 * COMPLETENESS OR PERFORMANCE.
30 * $/LicenseInfo$
31 */
32
33#include "linden_common.h"
34
35#include "vorbis/vorbisenc.h"
36
37#include "llvorbisencode.h"
38#include "llerror.h"
39#include "llrand.h"
40#include "llmath.h"
41#include "llapr.h"
42
43//#if LL_DARWIN
44// MBW -- XXX -- Getting rid of SecondLifeVorbis for now -- no fmod means no name collisions.
45#if 0
46#include "VorbisFramework.h"
47
48#define vorbis_analysis mac_vorbis_analysis
49#define vorbis_analysis_headerout mac_vorbis_analysis_headerout
50#define vorbis_analysis_init mac_vorbis_analysis_init
51#define vorbis_encode_ctl mac_vorbis_encode_ctl
52#define vorbis_encode_setup_init mac_vorbis_encode_setup_init
53#define vorbis_encode_setup_managed mac_vorbis_encode_setup_managed
54
55#define vorbis_info_init mac_vorbis_info_init
56#define vorbis_info_clear mac_vorbis_info_clear
57#define vorbis_comment_init mac_vorbis_comment_init
58#define vorbis_comment_clear mac_vorbis_comment_clear
59#define vorbis_block_init mac_vorbis_block_init
60#define vorbis_block_clear mac_vorbis_block_clear
61#define vorbis_dsp_clear mac_vorbis_dsp_clear
62#define vorbis_analysis_buffer mac_vorbis_analysis_buffer
63#define vorbis_analysis_wrote mac_vorbis_analysis_wrote
64#define vorbis_analysis_blockout mac_vorbis_analysis_blockout
65
66#define ogg_stream_packetin mac_ogg_stream_packetin
67#define ogg_stream_init mac_ogg_stream_init
68#define ogg_stream_flush mac_ogg_stream_flush
69#define ogg_stream_pageout mac_ogg_stream_pageout
70#define ogg_page_eos mac_ogg_page_eos
71#define ogg_stream_clear mac_ogg_stream_clear
72
73#endif
74
75S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& error_msg)
76{
77 U16 num_channels = 0;
78 U32 sample_rate = 0;
79 U32 bits_per_sample = 0;
80 U32 physical_file_size = 0;
81 U32 chunk_length = 0;
82 U32 raw_data_length = 0;
83 U32 bytes_per_sec = 0;
84 BOOL uncompressed_pcm = FALSE;
85
86 unsigned char wav_header[44]; /*Flawfinder: ignore*/
87
88 error_msg.clear();
89
90 //********************************
91 LLAPRFile infile ;
92 infile.open(in_fname,LL_APR_RB, LLAPRFile::global);
93 //********************************
94 if (!infile.getFileHandle())
95 {
96 error_msg = "CannotUploadSoundFile";
97 return(LLVORBISENC_SOURCE_OPEN_ERR);
98 }
99
100 infile.read(wav_header, 44);
101 physical_file_size = infile.seek(APR_END,0);
102
103 if (strncmp((char *)&(wav_header[0]),"RIFF",4))
104 {
105 error_msg = "SoundFileNotRIFF";
106 return(LLVORBISENC_WAV_FORMAT_ERR);
107 }
108
109 if (strncmp((char *)&(wav_header[8]),"WAVE",4))
110 {
111 error_msg = "SoundFileNotRIFF";
112 return(LLVORBISENC_WAV_FORMAT_ERR);
113 }
114
115 // parse the chunks
116
117 U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
118
119 while ((file_pos + 8)< physical_file_size)
120 {
121 infile.seek(APR_SET,file_pos);
122 infile.read(wav_header, 44);
123
124 chunk_length = ((U32) wav_header[7] << 24)
125 + ((U32) wav_header[6] << 16)
126 + ((U32) wav_header[5] << 8)
127 + wav_header[4];
128
129// llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
130
131 if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
132 {
133 if ((wav_header[8] == 0x01) && (wav_header[9] == 0x00))
134 {
135 uncompressed_pcm = TRUE;
136 }
137 num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
138 sample_rate = ((U32) wav_header[15] << 24)
139 + ((U32) wav_header[14] << 16)
140 + ((U32) wav_header[13] << 8)
141 + wav_header[12];
142 bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
143 bytes_per_sec = ((U32) wav_header[19] << 24)
144 + ((U32) wav_header[18] << 16)
145 + ((U32) wav_header[17] << 8)
146 + wav_header[16];
147 }
148 else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
149 {
150 raw_data_length = chunk_length;
151 }
152 file_pos += (chunk_length + 8);
153 chunk_length = 0;
154 }
155 //****************
156 infile.close();
157 //****************
158
159 if (!uncompressed_pcm)
160 {
161 error_msg = "SoundFileNotPCM";
162 return(LLVORBISENC_PCM_FORMAT_ERR);
163 }
164
165 if ((num_channels < 1) || (num_channels > LLVORBIS_CLIP_MAX_CHANNELS))
166 {
167 error_msg = "SoundFileInvalidChannelCount";
168 return(LLVORBISENC_MULTICHANNEL_ERR);
169 }
170
171 if (sample_rate != LLVORBIS_CLIP_SAMPLE_RATE)
172 {
173 error_msg = "SoundFileInvalidSampleRate";
174 return(LLVORBISENC_UNSUPPORTED_SAMPLE_RATE);
175 }
176
177 if ((bits_per_sample != 16) && (bits_per_sample != 8))
178 {
179 error_msg = "SoundFileInvalidWordSize";
180 return(LLVORBISENC_UNSUPPORTED_WORD_SIZE);
181 }
182
183 if (!raw_data_length)
184 {
185 error_msg = "SoundFileInvalidHeader";
186 return(LLVORBISENC_CLIP_TOO_LONG);
187 }
188
189 F32 clip_length = (F32)raw_data_length/(F32)bytes_per_sec;
190
191 if (clip_length > LLVORBIS_CLIP_MAX_TIME)
192 {
193 error_msg = "SoundFileInvalidTooLong";
194 return(LLVORBISENC_CLIP_TOO_LONG);
195 }
196
197 return(LLVORBISENC_NOERR);
198}
199
200S32 encode_vorbis_file(const std::string& in_fname, const std::string& out_fname)
201{
202#define READ_BUFFER 1024
203 unsigned char readbuffer[READ_BUFFER*4+44]; /* out of the data segment, not the stack */ /*Flawfinder: ignore*/
204
205 ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */
206 ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
207 ogg_packet op; /* one raw packet of data for decode */
208
209 vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */
210 vorbis_comment vc; /* struct that stores all the user comments */
211
212 vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
213 vorbis_block vb; /* local working space for packet->PCM decode */
214
215 int eos=0;
216 int result;
217
218 U16 num_channels = 0;
219 U32 sample_rate = 0;
220 U32 bits_per_sample = 0;
221
222 S32 format_error = 0;
223 std::string error_msg;
224 if ((format_error = check_for_invalid_wav_formats(in_fname, error_msg)))
225 {
226 llwarns << error_msg << ": " << in_fname << llendl;
227 return(format_error);
228 }
229
230#if 1
231 unsigned char wav_header[44]; /*Flawfinder: ignore*/
232
233 S32 data_left = 0;
234
235 LLAPRFile infile ;
236 infile.open(in_fname,LL_APR_RB, LLAPRFile::global);
237 if (!infile.getFileHandle())
238 {
239 llwarns << "Couldn't open temporary ogg file for writing: " << in_fname
240 << llendl;
241 return(LLVORBISENC_SOURCE_OPEN_ERR);
242 }
243
244 LLAPRFile outfile ;
245 outfile.open(out_fname,LL_APR_WPB, LLAPRFile::global);
246 if (!outfile.getFileHandle())
247 {
248 llwarns << "Couldn't open upload sound file for reading: " << in_fname
249 << llendl;
250 return(LLVORBISENC_DEST_OPEN_ERR);
251 }
252
253 // parse the chunks
254 U32 chunk_length = 0;
255 U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
256
257 while (infile.eof() != APR_EOF)
258 {
259 infile.seek(APR_SET,file_pos);
260 infile.read(wav_header, 44);
261
262 chunk_length = ((U32) wav_header[7] << 24)
263 + ((U32) wav_header[6] << 16)
264 + ((U32) wav_header[5] << 8)
265 + wav_header[4];
266
267// llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
268
269 if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
270 {
271 num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
272 sample_rate = ((U32) wav_header[15] << 24)
273 + ((U32) wav_header[14] << 16)
274 + ((U32) wav_header[13] << 8)
275 + wav_header[12];
276 bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
277 }
278 else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
279 {
280 infile.seek(APR_SET,file_pos+8);
281 // leave the file pointer at the beginning of the data chunk data
282 data_left = chunk_length;
283 break;
284 }
285 file_pos += (chunk_length + 8);
286 chunk_length = 0;
287 }
288
289
290 /********** Encode setup ************/
291
292 /* choose an encoding mode */
293 /* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */
294 vorbis_info_init(&vi);
295
296 // always encode to mono
297
298 // SL-52913 & SL-53779 determined this quality level to be our 'good
299 // enough' general-purpose quality level with a nice low bitrate.
300 // Equivalent to oggenc -q0.5
301 F32 quality = 0.05f;
302// quality = (bitrate==128000 ? 0.4f : 0.1);
303
304// if (vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1))
305 if (vorbis_encode_init_vbr(&vi, /* num_channels */ 1 ,sample_rate, quality))
306// if (vorbis_encode_setup_managed(&vi,1,sample_rate,-1,bitrate,-1) ||
307// vorbis_encode_ctl(&vi,OV_ECTL_RATEMANAGE_AVG,NULL) ||
308// vorbis_encode_setup_init(&vi))
309 {
310 llwarns << "unable to initialize vorbis codec at quality " << quality << llendl;
311 // llwarns << "unable to initialize vorbis codec at bitrate " << bitrate << llendl;
312 return(LLVORBISENC_DEST_OPEN_ERR);
313 }
314
315 /* add a comment */
316 vorbis_comment_init(&vc);
317// vorbis_comment_add(&vc,"Linden");
318
319 /* set up the analysis state and auxiliary encoding storage */
320 vorbis_analysis_init(&vd,&vi);
321 vorbis_block_init(&vd,&vb);
322
323 /* set up our packet->stream encoder */
324 /* pick a random serial number; that way we can more likely build
325 chained streams just by concatenation */
326 ogg_stream_init(&os, ll_rand());
327
328 /* Vorbis streams begin with three headers; the initial header (with
329 most of the codec setup parameters) which is mandated by the Ogg
330 bitstream spec. The second header holds any comment fields. The
331 third header holds the bitstream codebook. We merely need to
332 make the headers, then pass them to libvorbis one at a time;
333 libvorbis handles the additional Ogg bitstream constraints */
334
335 {
336 ogg_packet header;
337 ogg_packet header_comm;
338 ogg_packet header_code;
339
340 vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
341 ogg_stream_packetin(&os,&header); /* automatically placed in its own
342 page */
343 ogg_stream_packetin(&os,&header_comm);
344 ogg_stream_packetin(&os,&header_code);
345
346 /* We don't have to write out here, but doing so makes streaming
347 * much easier, so we do, flushing ALL pages. This ensures the actual
348 * audio data will start on a new page
349 */
350 while(!eos){
351 int result=ogg_stream_flush(&os,&og);
352 if(result==0)break;
353 outfile.write(og.header, og.header_len);
354 outfile.write(og.body, og.body_len);
355 }
356
357 }
358
359
360 while(!eos)
361 {
362 long bytes_per_sample = bits_per_sample/8;
363
364 long bytes=(long)infile.read(readbuffer,llclamp((S32)(READ_BUFFER*num_channels*bytes_per_sample),0,data_left)); /* stereo hardwired here */
365
366 if (bytes==0)
367 {
368 /* end of file. this can be done implicitly in the mainline,
369 but it's easier to see here in non-clever fashion.
370 Tell the library we're at end of stream so that it can handle
371 the last frame and mark end of stream in the output properly */
372
373 vorbis_analysis_wrote(&vd,0);
374// eos = 1;
375
376 }
377 else
378 {
379 long i;
380 long samples;
381 int temp;
382
383 data_left -= bytes;
384 /* data to encode */
385
386 /* expose the buffer to submit data */
387 float **buffer=vorbis_analysis_buffer(&vd,READ_BUFFER);
388
389 i = 0;
390 samples = bytes / (num_channels * bytes_per_sample);
391
392 if (num_channels == 2)
393 {
394 if (bytes_per_sample == 2)
395 {
396 /* uninterleave samples */
397 for(i=0; i<samples ;i++)
398 {
399 temp = ((signed char *)readbuffer)[i*4+1]; /*Flawfinder: ignore*/
400 temp += ((signed char *)readbuffer)[i*4+3]; /*Flawfinder: ignore*/
401 temp <<= 8;
402 temp += readbuffer[i*4];
403 temp += readbuffer[i*4+2];
404
405 buffer[0][i] = ((float)temp) / 65536.f;
406 }
407 }
408 else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
409 {
410 /* uninterleave samples */
411 for(i=0; i<samples ;i++)
412 {
413 temp = readbuffer[i*2+0];
414 temp += readbuffer[i*2+1];
415 temp -= 256;
416 buffer[0][i] = ((float)temp) / 256.f;
417 }
418 }
419 }
420 else if (num_channels == 1)
421 {
422 if (bytes_per_sample == 2)
423 {
424 for(i=0; i < samples ;i++)
425 {
426 temp = ((signed char*)readbuffer)[i*2+1];
427 temp <<= 8;
428 temp += readbuffer[i*2];
429 buffer[0][i] = ((float)temp) / 32768.f;
430 }
431 }
432 else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
433 {
434 for(i=0; i < samples ;i++)
435 {
436 temp = readbuffer[i];
437 temp -= 128;
438 buffer[0][i] = ((float)temp) / 128.f;
439 }
440 }
441 }
442
443 /* tell the library how much we actually submitted */
444 vorbis_analysis_wrote(&vd,i);
445 }
446
447 /* vorbis does some data preanalysis, then divvies up blocks for
448 more involved (potentially parallel) processing. Get a single
449 block for encoding now */
450 while(vorbis_analysis_blockout(&vd,&vb)==1)
451 {
452
453 /* analysis */
454 /* Do the main analysis, creating a packet */
455 vorbis_analysis(&vb, NULL);
456 vorbis_bitrate_addblock(&vb);
457
458 while(vorbis_bitrate_flushpacket(&vd, &op))
459 {
460
461 /* weld the packet into the bitstream */
462 ogg_stream_packetin(&os,&op);
463
464 /* write out pages (if any) */
465 while(!eos)
466 {
467 result = ogg_stream_pageout(&os,&og);
468
469 if(result==0)
470 break;
471
472 outfile.write(og.header, og.header_len);
473 outfile.write(og.body, og.body_len);
474
475 /* this could be set above, but for illustrative purposes, I do
476 it here (to show that vorbis does know where the stream ends) */
477
478 if(ogg_page_eos(&og))
479 eos=1;
480
481 }
482 }
483 }
484 }
485
486
487
488 /* clean up and exit. vorbis_info_clear() must be called last */
489
490 ogg_stream_clear(&os);
491 vorbis_block_clear(&vb);
492 vorbis_dsp_clear(&vd);
493 vorbis_comment_clear(&vc);
494 vorbis_info_clear(&vi);
495
496 /* ogg_page and ogg_packet structs always point to storage in
497 libvorbis. They're never freed or manipulated directly */
498
499// fprintf(stderr,"Vorbis encoding: Done.\n");
500 llinfos << "Vorbis encoding: Done." << llendl;
501
502#endif
503 return(LLVORBISENC_NOERR);
504
505}