Oolite 1.91.0.7646-241128-10e222e
Loading...
Searching...
No Matches
OOALSoundDecoder.m
Go to the documentation of this file.
1/*
2
3OOALSoundDecoder.m
4
5
6OOALSound - OpenAL sound implementation for Oolite.
7Copyright (C) 2005-2013 Jens Ayton
8
9Permission is hereby granted, free of charge, to any person obtaining a copy
10of this software and associated documentation files (the "Software"), to deal
11in the Software without restriction, including without limitation the rights
12to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13copies of the Software, and to permit persons to whom the Software is
14furnished to do so, subject to the following conditions:
15
16The above copyright notice and this permission notice shall be included in all
17copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25SOFTWARE.
26
27*/
28
29#import "OOALSoundDecoder.h"
31#import <vorbis/vorbisfile.h>
32#import "OOLogging.h"
33#import "unzip.h"
34
35enum
36{
37 kMaxDecodeSize = 1 << 20 // 2^20 frames = 4 MB
38};
39
40static size_t OOReadOXZVorbis (void *ptr, size_t size, size_t nmemb, void *datasource);
41static int OOCloseOXZVorbis (void *datasource);
42// not practical to implement these
43//static int OOSeekOXZVorbis (void *datasource, ogg_int64_t offset, int whence);
44//static long OOTellOXZVorbis (void *datasource);
45
47{
48 OggVorbis_File _vf;
49 NSString *_name;
52@public
54}
55
56- (NSDictionary *)comments;
57
58@end
59
60
61@implementation OOALSoundDecoder
62
63- (id)initWithPath:(NSString *)inPath
64{
65 [self release];
66 self = nil;
67
68 if ([[inPath pathExtension] isEqual:@"ogg"])
69 {
70 self = [[OOALSoundVorbisCodec alloc] initWithPath:inPath];
71 }
72
73 return self;
74}
75
76
77+ (OOALSoundDecoder *)codecWithPath:(NSString *)inPath
78{
79 if ([[inPath pathExtension] isEqual:@"ogg"])
80 {
81 return [[[OOALSoundVorbisCodec alloc] initWithPath:inPath] autorelease];
82 }
83 return nil;
84}
85
86
87- (size_t)streamToBuffer:(char *)ioBuffer
88{
89 return 0;
90}
91
92
93- (BOOL)readCreatingBuffer:(char **)outBuffer withFrameCount:(size_t *)outSize
94{
95 if (NULL != outBuffer) *outBuffer = NULL;
96 if (NULL != outSize) *outSize = 0;
97
98 return NO;
99}
100
101
102- (size_t)sizeAsBuffer
103{
104 return 0;
105}
106
107
108- (BOOL)isStereo
109{
110 return NO;
111}
112
113
115{
116 return 0;
117}
118
119
120- (void) reset
121{
122 // nothing
123}
124
125
126- (NSString *)name
127{
128 return @"";
129}
130
131@end
132
133
134@implementation OOALSoundVorbisCodec
135
136- (id)initWithPath:(NSString *)path
137{
138 if ((self = [super init]))
139 {
140 BOOL OK = NO;
141
142 _name = [[path lastPathComponent] retain];
143
144 NSUInteger i, cl;
145 NSArray *components = [path pathComponents];
146 cl = [components count];
147 for (i = 0 ; i < cl ; i++)
148 {
149 NSString *component = [components objectAtIndex:i];
150 if ([[[component pathExtension] lowercaseString] isEqualToString:@"oxz"])
151 {
152 break;
153 }
154 }
155 // if i == cl then the path is entirely uncompressed
156 if (i == cl)
157 {
158 /* Get vorbis data from a standard file stream */
159 int err;
160 FILE *file;
161
162 _seekableStream = YES;
163
164 if (nil != path)
165 {
166 file = fopen([path UTF8String], "rb");
167 if (NULL != file)
168 {
169 err = ov_open_callbacks(file, &_vf, NULL, 0, OV_CALLBACKS_DEFAULT);
170 if (0 == err)
171 {
172 OK = YES;
173 }
174 }
175 }
176
177 if (!OK)
178 {
179 [self release];
180 self = nil;
181 }
182
183 }
184 else
185 {
186 _seekableStream = NO;
187
188 NSRange range;
189 range.location = 0; range.length = i+1;
190 NSString *zipFile = [NSString pathWithComponents:[components subarrayWithRange:range]];
191 range.location = i+1; range.length = cl-(i+1);
192 NSString *containedFile = [NSString pathWithComponents:[components subarrayWithRange:range]];
193
194
195 const char* zipname = [zipFile UTF8String];
196 if (zipname != NULL)
197 {
198 uf = unzOpen64(zipname);
199 }
200 if (uf == NULL)
201 {
202 OOLog(kOOLogFileNotFound, @"Could not unzip OXZ at %@", zipFile);
203 [self release];
204 self = nil;
205 }
206 else
207 {
208 const char* filename = [containedFile UTF8String];
209 // unzLocateFile(*, *, 1) = case-sensitive extract
210 if (unzLocateFile(uf, filename, 1) != UNZ_OK)
211 {
212 unzClose(uf);
213 [self release];
214 self = nil;
215 }
216 else
217 {
218 int err = UNZ_OK;
219 unz_file_info64 file_info = {0};
220 err = unzGetCurrentFileInfo64(uf, &file_info, NULL, 0, NULL, 0, NULL, 0);
221 if (err != UNZ_OK)
222 {
223 unzClose(uf);
224 OOLog(kOOLogFileNotFound, @"Could not get properties of %@ within OXZ at %@", containedFile, zipFile);
225 [self release];
226 self = nil;
227 }
228 else
229 {
230 err = unzOpenCurrentFile(uf);
231 if (err != UNZ_OK)
232 {
233 unzClose(uf);
234 OOLog(kOOLogFileNotFound, @"Could not read %@ within OXZ at %@", containedFile, zipFile);
235 [self release];
236 self = nil;
237 }
238 else
239 {
240 ov_callbacks _callbacks = {
241 OOReadOXZVorbis, // read sequentially
242 NULL, // no seek
243 OOCloseOXZVorbis, // close file
244 NULL, // no tell
245 };
246 err = ov_open_callbacks(self, &_vf, NULL, 0, _callbacks);
247 if (0 == err)
248 {
249 OK = YES;
250 _readStarted = NO;
251 }
252 if (!OK)
253 {
254 unzClose(uf);
255 [self release];
256 self = nil;
257 }
258 }
259 }
260 }
261 }
262 }
263 }
264#ifdef OOLITE_DEBUG_SOUND_FILE_OPENING
265 if (self != nil)
266 {
267 OOLog(@"sound.retain",@"%@",_name);
268 }
269#endif
270 return self;
271}
272
273
274- (void)dealloc
275{
276#ifdef OOLITE_DEBUG_SOUND_FILE_OPENING
277 if (self != nil)
278 {
279 OOLog(@"sound.release",@"%@",_name);
280 }
281#endif
282
283 [_name release];
284 ov_clear(&_vf);
285 unzClose(uf);
286
287 [super dealloc];
288}
289
290
291- (NSDictionary *)comments
292{
293 vorbis_comment *comments;
294 unsigned i, count;
295 NSMutableDictionary *result = nil;
296 NSString *comment, *key, *value;
297 NSRange range;
298
299 comments = ov_comment(&_vf, -1);
300 if (NULL != comments)
301 {
302 count = comments->comments;
303 if (0 != count)
304 {
305 result = [NSMutableDictionary dictionaryWithCapacity:count];
306 for (i = 0; i != count; ++i)
307 {
308 comment = [[NSString alloc] initWithBytesNoCopy:comments->user_comments[i] length:comments->comment_lengths[i] encoding:NSUTF8StringEncoding freeWhenDone:NO];
309 range = [comment rangeOfString:@"="];
310 if (0 != range.length)
311 {
312 key = [comment substringToIndex:range.location];
313 value = [comment substringFromIndex:range.location + 1];
314 }
315 else
316 {
317 key = comment;
318 value = @"";
319 }
320 [result setObject:value forKey:key];
321
322 [comment release];
323 }
324 }
325 }
326
327 return result;
328}
329
330
331- (BOOL)readCreatingBuffer:(char **)outBuffer withFrameCount:(size_t *)outSize
332{
333 char *buffer = NULL, *dst;
334 size_t sizeInFrames = 0;
335 int remaining;
336 long framesRead;
337 // 16-bit samples, either two or one track
338 int frameSize = [self isStereo] ? 4 : 2;
339 ogg_int64_t totalSizeInFrames;
340 BOOL OK = YES;
341
342 if (NULL != outBuffer) *outBuffer = NULL;
343 if (NULL != outSize) *outSize = 0;
344 if (NULL == outBuffer || NULL == outSize) OK = NO;
345
346 if (OK)
347 {
348 totalSizeInFrames = ov_pcm_total(&_vf, -1);
349 assert ((uint64_t)totalSizeInFrames < (uint64_t)SIZE_MAX); // Should have been checked by caller
350 sizeInFrames = (size_t)totalSizeInFrames;
351 }
352
353 if (OK)
354 {
355 buffer = malloc(sizeof (char) * frameSize * sizeInFrames);
356 if (!buffer) OK = NO;
357 }
358
359 if (OK && sizeInFrames)
360 {
361 remaining = (int)MIN(frameSize * sizeInFrames, (size_t)INT_MAX);
362 dst = buffer;
363
364 char pcmout[4096];
365
366 do
367 {
368 int toRead = sizeof(pcmout);
369 if (remaining < toRead)
370 {
371 toRead = remaining;
372 }
373 framesRead = ov_read(&_vf, pcmout, toRead, 0, 2, 1, NULL);
374 if (framesRead <= 0)
375 {
376 if (OV_HOLE == framesRead) continue;
377 //else:
378 break;
379 }
380
381 memcpy(dst, &pcmout, sizeof (char) * framesRead);
382
383 remaining -= framesRead;
384 dst += framesRead;
385 } while (0 < remaining);
386
387 sizeInFrames -= remaining; // In case we stopped at an error
388 }
389
390 if (OK)
391 {
392 *outBuffer = buffer;
393 *outSize = sizeInFrames*frameSize;
394 }
395 else
396 {
397 if (buffer) free(buffer);
398 }
399 return OK;
400}
401
402
403- (size_t)streamToBuffer:(char *)buffer
404{
405
406 int remaining = OOAL_STREAM_CHUNK_SIZE;
407 size_t streamed = 0;
408 long framesRead;
409
410 char *dst = buffer;
411 char pcmout[4096];
412 _readStarted = YES;
413 do
414 {
415 int toRead = sizeof(pcmout);
416 if (remaining < toRead)
417 {
418 toRead = remaining;
419 }
420 framesRead = ov_read(&_vf, pcmout, toRead, 0, 2, 1, NULL);
421 if (framesRead <= 0)
422 {
423 if (OV_HOLE == framesRead) continue;
424 //else:
425 break;
426 }
427 memcpy(dst, &pcmout, sizeof (char) * framesRead);
428 remaining -= sizeof(char) * framesRead;
429 dst += sizeof(char) * framesRead;
430 streamed += sizeof(char) * framesRead;
431 } while (0 < remaining);
432
433 return streamed;
434}
435
436
437- (size_t)sizeAsBuffer
438{
439 ogg_int64_t size;
440 size = ov_pcm_total(&_vf, -1);
441 size *= sizeof(char) * ([self isStereo] ? 4 : 2);
442 if ((uint64_t)SIZE_MAX < (uint64_t)size) size = (ogg_int64_t)SIZE_MAX;
443 return (size_t)size;
444}
445
446
447- (BOOL)isStereo
448{
449 return 1 < ov_info(&_vf, -1)->channels;
450}
451
452
453- (NSString *)description
454{
455 return [NSString stringWithFormat:@"<%@ %p>{\"%@\", comments=%@}", [self className], self, _name, [self comments]];
456}
457
458
460{
461 return ov_info(&_vf, -1)->rate;
462}
463
464
465
466- (void) reset
467{
468 if (!_readStarted)
469 {
470 return; // don't need to do anything
471 }
472 if (_seekableStream)
473 {
474 ov_pcm_seek(&_vf, 0);
475 return;
476 }
477 // reset current file pointer in OXZ
479 // reopen OGG streamer
480 ov_clear(&_vf);
481 ov_callbacks _callbacks = {
482 OOReadOXZVorbis, // read sequentially
483 NULL, // no seek
484 OOCloseOXZVorbis, // close file
485 NULL, // no tell
486 };
487 ov_open_callbacks(self, &_vf, NULL, 0, _callbacks);
488 _readStarted = NO;
489}
490
491
492- (NSString *)name
493{
494 return [[_name retain] autorelease];
495}
496
497@end
498
499
500static size_t OOReadOXZVorbis (void *ptr, size_t size, size_t nmemb, void *datasource)
501{
502 OOALSoundVorbisCodec *src = (OOALSoundVorbisCodec *)datasource;
503 size_t toRead = size*nmemb;
504 void *buf = (void*)malloc(toRead);
505 int err = UNZ_OK;
506 err = unzReadCurrentFile(src->uf, buf, toRead);
507// OOLog(@"sound.replay",@"Read %d blocks, got %d",toRead,err);
508 if (err > 0)
509 {
510 memcpy(ptr, buf, err);
511 }
512 if (err < 0)
513 {
514 return OV_EREAD;
515 }
516 return err;
517}
518
519
520static int OOCloseOXZVorbis (void *datasource)
521{
522// doing this prevents replaying
523// OOALSoundVorbisCodec *src = (OOALSoundVorbisCodec *)datasource;
524// unzClose(src->uf);
525 return 0;
526}
527
#define OOAL_STREAM_CHUNK_SIZE
@ kMaxDecodeSize
static size_t OOReadOXZVorbis(void *ptr, size_t size, size_t nmemb, void *datasource)
static int OOCloseOXZVorbis(void *datasource)
#define OOLog(class, format,...)
Definition OOLogging.h:88
NSString *const kOOLogFileNotFound
Definition OOLogging.m:652
#define MIN(A, B)
Definition OOMaths.h:111
unsigned count
return nil
NSDictionary * comments()
voidpf void uLong size
Definition ioapi.h:134
const char * filename
Definition ioapi.h:133
typedef long(ZCALLBACK *tell_file_func) OF((voidpf opaque
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
voidpf void * buf
Definition ioapi.h:134
int ZEXPORT unzLocateFile(unzFile file, const char *szFileName, int iCaseSensitivity)
Definition unzip.c:1238
int ZEXPORT unzGetCurrentFileInfo64(unzFile file, unz_file_info64 *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)
Definition unzip.c:1130
int ZEXPORT unzOpenCurrentFile(unzFile file)
Definition unzip.c:1653
int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, unsigned len)
Definition unzip.c:1696
unzFile ZEXPORT unzOpen64(const void *path)
Definition unzip.c:801
int ZEXPORT unzClose(unzFile file)
Definition unzip.c:811
voidp unzFile
Definition unzip.h:70
#define UNZ_OK
Definition unzip.h:74