diff options
Diffstat (limited to 'linden/indra/llaudio/llvorbisencode.cpp')
-rw-r--r-- | linden/indra/llaudio/llvorbisencode.cpp | 505 |
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 | |||
75 | S32 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 | |||
200 | S32 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 | } | ||