LCOV - code coverage report
Current view: top level - Core - OOALSoundDecoder.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 21 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : OOALSoundDecoder.m
       4             : 
       5             : 
       6             : OOALSound - OpenAL sound implementation for Oolite.
       7             : Copyright (C) 2005-2013 Jens Ayton
       8             : 
       9             : Permission is hereby granted, free of charge, to any person obtaining a copy
      10             : of this software and associated documentation files (the "Software"), to deal
      11             : in the Software without restriction, including without limitation the rights
      12             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      13             : copies of the Software, and to permit persons to whom the Software is
      14             : furnished to do so, subject to the following conditions:
      15             : 
      16             : The above copyright notice and this permission notice shall be included in all
      17             : copies or substantial portions of the Software.
      18             : 
      19             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      20             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      21             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      22             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      23             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      24             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      25             : SOFTWARE.
      26             : 
      27             : */
      28             : 
      29             : #import "OOALSoundDecoder.h"
      30             : #import "NSDataOOExtensions.h"
      31             : #import <vorbis/vorbisfile.h>
      32             : #import "OOLogging.h"
      33             : #import "unzip.h"
      34             : 
      35           0 : enum
      36             : {
      37             :         kMaxDecodeSize                  = 1 << 20         // 2^20 frames = 4 MB
      38             : };
      39             : 
      40             : static size_t OOReadOXZVorbis (void *ptr, size_t size, size_t nmemb, void *datasource);
      41             : static 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             : 
      46           0 : @interface OOALSoundVorbisCodec: OOALSoundDecoder
      47             : {
      48           0 :         OggVorbis_File                  _vf;
      49           0 :         NSString                                *_name;
      50           0 :         BOOL                                    _readStarted;
      51           0 :         BOOL                                    _seekableStream;
      52             : @public
      53           0 :         unzFile                                 uf;
      54             : }
      55             : 
      56           0 : - (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             : 
     114             : - (long)sampleRate
     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           0 : - (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           0 : - (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           0 : - (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           0 : - (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           0 : - (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           0 : - (BOOL)isStereo
     448             : {
     449             :         return 1 < ov_info(&_vf, -1)->channels;
     450             : }
     451             : 
     452             : 
     453           0 : - (NSString *)description
     454             : {
     455             :         return [NSString stringWithFormat:@"<%@ %p>{\"%@\", comments=%@}", [self className], self, _name, [self comments]];
     456             : }
     457             : 
     458             : 
     459           0 : - (long)sampleRate
     460             : {
     461             :         return ov_info(&_vf, -1)->rate;
     462             : }
     463             : 
     464             : 
     465             : 
     466           0 : - (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
     478             :         unzOpenCurrentFile(uf);
     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           0 : - (NSString *)name
     493             : {
     494             :         return [[_name retain] autorelease];
     495             : }
     496             : 
     497             : @end
     498             : 
     499             : 
     500           0 : static 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             : 
     520           0 : static int OOCloseOXZVorbis (void *datasource)
     521             : {
     522             : //  doing this prevents replaying
     523             : //      OOALSoundVorbisCodec *src = (OOALSoundVorbisCodec *)datasource;
     524             : //      unzClose(src->uf);
     525             :         return 0;
     526             : }
     527             : 

Generated by: LCOV version 1.14