Ogg vs Caf for audio

Page 1 of 2 1, 2  Next

View previous topic View next topic Go down

Ogg vs Caf for audio

Post  uprise78 on Mon Nov 10, 2008 9:09 am

First off, in Tutorial09 you used .ogg files for all audio. Are .caf files supported as well?

What is the preferred audio file format? I may be mistaken, but I thought the iPhone can play .caf files natively and that it was the file format Apple was recommending for all audio. Don't .ogg files require a bit of processor time to make them playable? What are the pros/cons of using .ogg vs .caf?

Thanks for an awesome engine by the way. I am liking SIO2 more and more every day that i use it.

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  sio2interactive on Mon Nov 10, 2008 3:13 pm

Yeah OGG require some processing to be playable (and a little bit way too much I think...), Im really not familiar with CAF but i'll check it out...

Tks for the tip!

Cheers,
avatar
sio2interactive

Posts : 1526
Join date : 2008-08-26
Age : 38
Location : Shanghai

View user profile http://sio2interactive.com

Back to top Go down

Re: Ogg vs Caf for audio

Post  uprise78 on Mon Nov 10, 2008 6:08 pm

Here is a bit of info about .caf files:

  • For uncompressed audio, use 16-bit, little-endian, linear PCM audio packaged in a CAF file
  • If you need to play multiple compressed sounds simultaneously, use the IMA/ADPCM audio (IMA4) format.

The command I have been using to convert audio files for other iPhone projects is:
Code:
/usr/bin/afconvert -f caff -d LEI16 {INPUT} {OUTPUT}
This command will give you all of the possible options for afconvert:
Code:
afconvert -h

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  sio2interactive on Mon Nov 10, 2008 6:24 pm

Yeah that's all great... but what Im expecting for its a loader to plug the binary data to OpenAL Wink
avatar
sio2interactive

Posts : 1526
Join date : 2008-08-26
Age : 38
Location : Shanghai

View user profile http://sio2interactive.com

Back to top Go down

Re: Ogg vs Caf for audio

Post  uprise78 on Mon Nov 10, 2008 7:42 pm

I can give creating the loader a shot. Do you have any pointers on where to start such as the location of the ogg to openal code?

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  matt on Tue Nov 11, 2008 8:52 am

The Sound_Engine.cpp which comes with the CrashLander SDK example should be a good starting point. It uses CAF files and feeds them into OpenAL.

Best,
Matt

matt

Posts : 155
Join date : 2008-09-30

View user profile http://elfrun.net

Back to top Go down

Re: Ogg vs Caf for audio

Post  zzajin on Tue Nov 11, 2008 9:44 am

I had some problems with Sound_Engine.cpp that I never resolved in my initial testing of it. In particular is this puzzling line of code located in LoadFileData function.

Code:

         if (!TestAudioFormatNativeEndian(theFileFormat) && (theFileFormat.mBitsPerChannel > 8))
            return kSoundEngineErrInvalidFileFormat;


It seems to limit sound files to only 8bits. Changing this to 16 didn't work, but maybe my file wasn't compatible (afconvert is a great tip! ). When I asked someone about this, they told me to use some code from WWDC which was better and they had no problems with 16 bit sounds.

zzajin

Posts : 81
Join date : 2008-10-14

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  uprise78 on Tue Nov 11, 2008 9:56 am

zzajin wrote:
When I asked someone about this, they told me to use some code from WWDC which was better and they had no problems with 16 bit sounds.

What code from WWDC did they recommend? I went and have access to all of the code and SoundEngine is what I have been using without issue.

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  zzajin on Tue Nov 11, 2008 10:25 am

They didn't say. I assume it was the TouchFighter (game?) code people seem to want access to.

zzajin

Posts : 81
Join date : 2008-10-14

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  uprise78 on Tue Nov 11, 2008 10:53 am

Looks like TouchFighter uses the same SoundEngine class.

Has anyone dug into SIO2's audio playback code enough to let me know where I need to plugin the caf loader? SoundEngine is a bit heavy to plugin completely as it would completely replace the current SIO2 audio playback and openAL management code so knowing where to plugin caf loading and playback would be great. This would definitely be a worthwhile addition to SIO2.

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  zzajin on Tue Nov 11, 2008 11:12 am

My guess is that loading soundbufffers would be similar to the way images are done. Probably what is in sio2SoundBufferLoad would get moved to a sio2SoundBufferLoadOGG and a switch statement would select that or a sio2SoundBufferLoadCAF function based on the stream's filename. Checkout sio2ImageLoad in sio_image.

zzajin

Posts : 81
Join date : 2008-10-14

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  uprise78 on Tue Nov 11, 2008 2:18 pm

I have basic .caf file playback working but it is not without issues. There is an odd sound artifiact that plays for a split second when the sound loops. Also missing is automatic detection of frequency and format.

Modify sio2_soundbuffer.cc:

Code:
// Replace the old sio2SoundBufferLoad function with this one
void sio2SoundBufferLoad( SIO2soundbuffer *_SIO2soundbuffer,
                    SIO2stream     *_SIO2stream )
{
   if( strstr( _SIO2stream->fname, ".ogg" ) ||
       strstr( _SIO2stream->fname, ".OGG" ))
   {
      sio2SoundBufferLoadOGG( _SIO2soundbuffer, _SIO2stream );
   }
   else if( strstr( _SIO2stream->fname, ".caf" ) ||
          strstr( _SIO2stream->fname, ".CAF" ))
   {
      sio2SoundBufferLoadCAF( _SIO2soundbuffer, _SIO2stream );
   }
}

// This is a new function.  Put it in the header also.
void sio2SoundBufferLoadOGG( SIO2soundbuffer *_SIO2soundbuffer,
                      SIO2stream     *_SIO2stream )
{
   OggVorbis_File ovfile;
   vorbis_info *info;
   
   ov_callbacks callbacks;
   
   int count,
   bitstream;
   
   char *cursor;
   
   callbacks.read_func  = sio2SoundBufferRead;
   callbacks.seek_func  = sio2SoundBufferSeek;
   callbacks.tell_func  = sio2SoundBufferTell;
   callbacks.close_func = sio2SoundBufferClose;
   
   ov_open_callbacks( _SIO2stream, &ovfile, NULL, 0, callbacks );
   
   info = ov_info( &ovfile, -1 );
   
   if( info->channels == 1 )
   { _SIO2soundbuffer->format = AL_FORMAT_MONO16; }
   else
   { _SIO2soundbuffer->format = AL_FORMAT_STEREO16; }
   
   _SIO2soundbuffer->size = ( ov_pcm_total( &ovfile, -1 ) * info->channels << 1 );
   _SIO2soundbuffer->data = ( char * ) malloc( _SIO2soundbuffer->size );
   _SIO2soundbuffer->freq = info->rate;
   
   cursor = _SIO2soundbuffer->data;
   
   while( ( count = ov_read( &ovfile,
                      cursor,
                      SIO2_SOUND_BUFFER_SIZE,
                      0, 2, 1,
                      &bitstream ) ) > 0 )
   { cursor += count; }
   
   ov_clear( &ovfile );
}

// This is a new function.  Put it in the header also.
void sio2SoundBufferLoadCAF( SIO2soundbuffer *_SIO2soundbuffer,
                      SIO2stream     *_SIO2stream )
{
   _SIO2soundbuffer->format = AL_FORMAT_STEREO16;
   
   _SIO2soundbuffer->size = ( _SIO2stream->size );
   _SIO2soundbuffer->freq = 44100;
   _SIO2soundbuffer->data = ( char * ) malloc( _SIO2soundbuffer->size );
   
   sio2StreamRead( _SIO2stream, &_SIO2soundbuffer->data[ 0 ], _SIO2stream->size - 1 );
}

In order to get the exporter to let you export a CAF file from blender you need to modify the exporter python script. Replace the following two lines (238, 240):
Code:
            ogg_uc_pos = t_name.upper().rfind('.OGG' )

            if( ogg_uc_pos > -1 ):

with these 2 lines:
Code:
if( t_name.upper().endswith('.OGG') or t_name.upper().endswith('.CAF') ):

That should get you going. If anyone can give me a clue as to why the sound artifact is there I would be happy to look into it to the best of my abilities.

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  zzajin on Wed Nov 12, 2008 7:48 am

It looks like caf files have at least 2 "chunks" an audio description and an audio data chunk.

http://developer.apple.com/documentation/MusicAudio/Reference/CAFSpec/CAF_overview/chapter_2_section_3.html#//apple_ref/doc/uid/TP40001862-CH209-TPXREF102


Perhaps there is some noise because the audio description is sent to the OpenAL buffer. Doesn't it need only the data chunk?

zzajin

Posts : 81
Join date : 2008-10-14

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  uprise78 on Wed Nov 12, 2008 8:27 am

good call zzajin. The clicking artifact is most definetely the header and description chunk. I'll try parsing them put and I might as well grab the correct file format data as well.

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  uprise78 on Wed Nov 12, 2008 11:43 am

Parsing out the header did the trick. Caf files are working flawlessly now. Here is the code for sio2_soundbuffer.cc:
Code:
void sio2SoundBufferLoad( SIO2soundbuffer *_SIO2soundbuffer,
                    SIO2stream     *_SIO2stream )
{
   if( strstr( _SIO2stream->fname, ".ogg" ) ||
       strstr( _SIO2stream->fname, ".OGG" ))
   {
      sio2SoundBufferLoadOGG( _SIO2soundbuffer, _SIO2stream );
   }
   else if( strstr( _SIO2stream->fname, ".caf" ) ||
          strstr( _SIO2stream->fname, ".CAF" ))
   {
      sio2SoundBufferLoadCAF( _SIO2soundbuffer, _SIO2stream );
   }
}


void sio2SoundBufferLoadOGG( SIO2soundbuffer *_SIO2soundbuffer,
                      SIO2stream     *_SIO2stream )
{
   OggVorbis_File ovfile;
   vorbis_info *info;
   
   ov_callbacks callbacks;
   
   int count,
   bitstream;
   
   char *cursor;
   
   callbacks.read_func  = sio2SoundBufferRead;
   callbacks.seek_func  = sio2SoundBufferSeek;
   callbacks.tell_func  = sio2SoundBufferTell;
   callbacks.close_func = sio2SoundBufferClose;
   
   ov_open_callbacks( _SIO2stream, &ovfile, NULL, 0, callbacks );
   
   info = ov_info( &ovfile, -1 );
   
   if( info->channels == 1 )
   { _SIO2soundbuffer->format = AL_FORMAT_MONO16; }
   else
   { _SIO2soundbuffer->format = AL_FORMAT_STEREO16; }
   
   _SIO2soundbuffer->size = ( ov_pcm_total( &ovfile, -1 ) * info->channels << 1 );
   _SIO2soundbuffer->data = ( char * ) malloc( _SIO2soundbuffer->size );
   _SIO2soundbuffer->freq = info->rate;
   
   cursor = _SIO2soundbuffer->data;
   
   while( ( count = ov_read( &ovfile,
                      cursor,
                      SIO2_SOUND_BUFFER_SIZE,
                      0, 2, 1,
                      &bitstream ) ) > 0 )
   { cursor += count; }
   
   ov_clear( &ovfile );
}

void sio2SoundBufferLoadCAF( SIO2soundbuffer *_SIO2soundbuffer,
                      SIO2stream     *_SIO2stream )
{
   // TODO: add error checking on the OSStatus return values
   // Open the audio file stream and parse it
   AudioFileStreamID afID;
   OSStatus result = AudioFileStreamOpen(_SIO2soundbuffer, sio2CAFAudioPropertyListener, sio2CAFProcessAudioStreamPackets, kAudioFileCAFType, &afID);
   result = AudioFileStreamParseBytes(afID, (UInt32)_SIO2stream->size, _SIO2stream->buf, NULL);
   
   AudioFileStreamClose(afID);
}

void sio2CAFAudioPropertyListener( void                      *_source,
                          AudioFileStreamID          inAudioFileStream,
                          AudioFileStreamPropertyID  inPropertyID,
                          UInt32                      *ioFlags )
{
   // We only really care about the dataFormat
   if(inPropertyID == kAudioFilePropertyDataFormat)
   {
      AudioStreamBasicDescription      audioFormat;
      UInt32 sizeOfPlaybackFormatASBDStruct = sizeof(audioFormat);
      AudioFileStreamGetProperty(inAudioFileStream, kAudioFilePropertyDataFormat, &sizeOfPlaybackFormatASBDStruct, &audioFormat);
      
      // Copy relevant data to the soundBuffer
      SIO2soundbuffer *_SIO2soundbuffer = ( SIO2soundbuffer *)_source;
      
      if( audioFormat.mChannelsPerFrame == 1 )
         _SIO2soundbuffer->format = AL_FORMAT_MONO16;
      else
         _SIO2soundbuffer->format = AL_FORMAT_STEREO16;
      
      _SIO2soundbuffer->freq = audioFormat.mSampleRate;
   }
}

void sio2CAFProcessAudioStreamPackets( void                          *_source,
                             UInt32                        inNumberBytes,
                             UInt32                        inNumberPackets,
                             const void                    *inInputData,
                             AudioStreamPacketDescription  *inPacketDescriptions )
{
   SIO2soundbuffer *_SIO2soundbuffer = ( SIO2soundbuffer *)_source;
   
   _SIO2soundbuffer->size = inNumberBytes;
   _SIO2soundbuffer->data = ( char * ) malloc( inNumberBytes );

   memcpy(_SIO2soundbuffer->data, inInputData, inNumberBytes);
}

You will need to make the change to the exporter as shown above. You will also need to include the AudioToolbox.framework in your project and add one line to sio2.h:
Code:
#include #include <AudioToolbox/AudioFileStream.h>

If anyone can write a CAF parser the AudioToolbox/AudioFileStream.h requirement can be removed though that probably isn't all that important. This solution should work with any CAF files that are supported by the iPhones openAL implementation.

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  uprise78 on Fri Nov 14, 2008 9:22 am

I am working on adding support for IMA4 compressions as well and that should wrap up Caf file loading. Will report back with final code when it's done.

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  sio2interactive on Sat Nov 15, 2008 4:35 pm

Cool Im looking forward to it!

Cheers,
avatar
sio2interactive

Posts : 1526
Join date : 2008-08-26
Age : 38
Location : Shanghai

View user profile http://sio2interactive.com

Back to top Go down

Re: Ogg vs Caf for audio

Post  uprise78 on Mon Nov 17, 2008 12:10 pm

UPDATE:

I've got caf's working for everything except one small little item that someone who is a bit more experienced in C should be able to help me out with pretty quickly. The issue is the call to sio2IMAdecompress: it takes in a buffer to write the decompressed audio to (int16_t* pDst). I need the decompressed audio to be written to _SIO2soundbuffer->data which is a char *. If someone could clue me in on how to get this working caf file support will be done and ready for use.

Code:
void sio2CAFProcessAudioStreamPackets( void                          *_source,
                             UInt32                        inNumberBytes,
                             UInt32                        inNumberPackets,
                             const void                    *inInputData,
                             AudioStreamPacketDescription  *inPacketDescriptions )
{   
   SIO2soundbuffer *_SIO2soundbuffer = ( SIO2soundbuffer *)_source;

   // Decompress CAF if it is ima4
   if( _SIO2soundbuffer->cafType == 'ima4')
   {
      _SIO2soundbuffer->data = ( char * ) malloc( _SIO2soundbuffer->size );
      sio2IMAdecompress(inNumberPackets, _SIO2soundbuffer->format, (uint8_t*)inInputData, (int16_t*)_SIO2soundbuffer->data );
   }
   else
   {
      _SIO2soundbuffer->size = inNumberBytes;
      _SIO2soundbuffer->data = ( char * ) malloc( inNumberBytes );   
   }

   memcpy( _SIO2soundbuffer->data, inInputData, _SIO2soundbuffer->size );
}

static int32_t ima_index_table[16] =
{
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8
};

static int32_t ima_step_table[89] =
{
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350,  22385, 24623, 27086, 29794, 32767
};

void sio2IMAdecompress_packet( const uint8_t* pSrc, int16_t* pDst, int32_t stride )
{
    // Read packet header
    uint16_t value  = *(uint16_t*)pSrc;
    uint16_t header = ( value >> 8 ) | ( value << 8 );
    int32_t predictor  = header & 0xff80;
    int32_t step_index = header & 0x007f;
    int32_t step, nibble, diff;
   
    // Sign extend predictor
    if( predictor & 0x8000 )
        predictor |= 0xffff0000;
   
    // Skip header
    pSrc += 2;
   
    // Read 64 nibbles, 2 at a time
    UInt32 byteCount = 32;
    while( byteCount-- )
    {
        // Read 2 nibbles
        uint8_t byte = *pSrc++;
      
        // Process low nibble
        nibble = byte & 0x0f;
        if( step_index < 0 ) step_index = 0;
        else if( step_index > 88 ) step_index = 88;
        step = ima_step_table[ step_index ];
        step_index += ima_index_table[ nibble ];
        diff = step >> 3;
        if (nibble & 4) diff += step;
        if (nibble & 2) diff += (step >> 1);
        if (nibble & 1) diff += (step >> 2);
        if (nibble & 8) predictor -= diff;
        else predictor += diff;
        if( predictor < -32768 ) predictor = -32768;
        else if( predictor > 32767 ) predictor = 32767;
        *pDst = predictor;
        pDst += stride;
      
        // Process high nibble
        nibble = byte >> 4;
        if( step_index < 0 ) step_index = 0;
        else if( step_index > 88 ) step_index = 88;
        step = ima_step_table[ step_index ];
        step_index += ima_index_table[ nibble ];
        diff = step >> 3;
        if (nibble & 4) diff += step;
        if (nibble & 2) diff += (step >> 1);
        if (nibble & 1) diff += (step >> 2);
        if (nibble & 8) predictor -= diff;
        else predictor += diff;
        if( predictor < -32768 ) predictor = -32768;
        else if( predictor > 32767 ) predictor = 32767;
        *pDst = predictor;
        pDst += stride;
    }
}

void sio2IMAdecompress( uint32_t packetCount, const unsigned int format, const uint8_t* pSrc, int16_t* pDst )
{
    // Stereo?
    if( format == AL_FORMAT_STEREO16 )
    {
        // Decompress all stereo packets
        while( packetCount > 0 )
        {
            // Decompress channel0 and channel1 interleaved
            sio2IMAdecompress_packet( &pSrc[0],  &pDst[0], 2 );
            sio2IMAdecompress_packet( &pSrc[34], &pDst[1], 2 );
         
            // Next 2 channel packets
            pSrc += 34*2;
            pDst += 64*2;
            packetCount -= 2;
        }
    }
    else
    {
        // Decompress all mono packets
        while( packetCount-- )
        {
            // Decompress single channel
            sio2IMAdecompress_packet( pSrc, pDst, 1 );
         
            // Next channel packet
            pSrc += 34;
            pDst += 64;
        }
    }
}

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  uprise78 on Thu Nov 20, 2008 9:26 am

UPDATE

I finally got uncompressed cafs and compressed cafs (IMA4) working. After a bit of code cleanup and testing, I will post code here.

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  uprise78 on Fri Nov 21, 2008 11:59 am

Instructions for getting Caf files working with SIO2:

sio2_soundbuffer.h - Add this line to the SIO2soundbuffer struct
Code:
   unsigned int   cafType;

sio2_soundbuffer.cc:
Code:
// Replace the old sio2SoundBufferLoad function with this one
void sio2SoundBufferLoad( SIO2soundbuffer *_SIO2soundbuffer,
                    SIO2stream    *_SIO2stream )
{
  if( strstr( _SIO2stream->fname, ".ogg" ) ||
      strstr( _SIO2stream->fname, ".OGG" ))
  {
      sio2SoundBufferLoadOGG( _SIO2soundbuffer, _SIO2stream );
  }
  else if( strstr( _SIO2stream->fname, ".caf" ) ||
          strstr( _SIO2stream->fname, ".CAF" ))
  {
      sio2SoundBufferLoadCAF( _SIO2soundbuffer, _SIO2stream );
  }
}

// This is a new function.  Put it in the header also.
void sio2SoundBufferLoadOGG( SIO2soundbuffer *_SIO2soundbuffer,
                      SIO2stream    *_SIO2stream )
{
  OggVorbis_File ovfile;
  vorbis_info *info;
 
  ov_callbacks callbacks;
 
  int count,
  bitstream;
 
  char *cursor;
 
  callbacks.read_func  = sio2SoundBufferRead;
  callbacks.seek_func  = sio2SoundBufferSeek;
  callbacks.tell_func  = sio2SoundBufferTell;
  callbacks.close_func = sio2SoundBufferClose;
 
  ov_open_callbacks( _SIO2stream, &ovfile, NULL, 0, callbacks );
 
  info = ov_info( &ovfile, -1 );
 
  if( info->channels == 1 )
  { _SIO2soundbuffer->format = AL_FORMAT_MONO16; }
  else
  { _SIO2soundbuffer->format = AL_FORMAT_STEREO16; }
 
  _SIO2soundbuffer->size = ( ov_pcm_total( &ovfile, -1 ) * info->channels << 1 );
  _SIO2soundbuffer->data = ( char * ) malloc( _SIO2soundbuffer->size );
  _SIO2soundbuffer->freq = info->rate;
 
  cursor = _SIO2soundbuffer->data;
 
  while( ( count = ov_read( &ovfile,
                      cursor,
                      SIO2_SOUND_BUFFER_SIZE,
                      0, 2, 1,
                      &bitstream ) ) > 0 )
  { cursor += count; }
 
  ov_clear( &ovfile );
}

// This is a new function.  Put it in the header also.
void sio2SoundBufferLoadCAF( SIO2soundbuffer *_SIO2soundbuffer,
                      SIO2stream     *_SIO2stream )
{
   // TODO: add error checking on the OSStatus return values
   // Open the audio file stream and parse it
   AudioFileStreamID afID;
   OSStatus result = AudioFileStreamOpen(_SIO2soundbuffer, sio2CAFAudioPropertyListener, sio2CAFProcessAudioStreamPackets, kAudioFileCAFType, &afID);
   result = AudioFileStreamParseBytes(afID, (UInt32)_SIO2stream->size, _SIO2stream->buf, NULL);
   
   AudioFileStreamClose(afID);
}

void sio2CAFAudioPropertyListener( void                      *_source,
                          AudioFileStreamID          inAudioFileStream,
                          AudioFileStreamPropertyID  inPropertyID,
                          UInt32                      *ioFlags )
{
   // When we have kAudioFilePropertyAudioDataByteCount all data is in so we can grab the relevant data and calculate our total decompressed buffer size for compressed ima4 files
   if(inPropertyID == kAudioFilePropertyAudioDataByteCount)
   {
      // Copy relevant data to the soundBuffer
      SIO2soundbuffer *_SIO2soundbuffer = ( SIO2soundbuffer *)_source;
      
      AudioStreamBasicDescription      audioFormat;
      UInt32 sizeOfPlaybackFormatASBDStruct = sizeof(audioFormat);
      AudioFileStreamGetProperty(inAudioFileStream, kAudioFilePropertyDataFormat, &sizeOfPlaybackFormatASBDStruct, &audioFormat);
      
      // IMA4 files require decompression before sending them off to openAL.  We save out the type so we can deal with it later
      _SIO2soundbuffer->cafType = audioFormat.mFormatID;
      
      if( audioFormat.mChannelsPerFrame == 1 )
         _SIO2soundbuffer->format = AL_FORMAT_MONO16;
      else
         _SIO2soundbuffer->format = AL_FORMAT_STEREO16;
      
      _SIO2soundbuffer->freq = audioFormat.mSampleRate;
   }
}

void sio2CAFProcessAudioStreamPackets( void                          *_source,
                             UInt32                        inNumberBytes,
                             UInt32                        inNumberPackets,
                             const void                    *inInputData,
                             AudioStreamPacketDescription  *inPacketDescriptions )
{   
   SIO2soundbuffer *_SIO2soundbuffer = ( SIO2soundbuffer *)_source;

   // Decompress CAF if it is ima4
   if( _SIO2soundbuffer->cafType == 'ima4')
   {
      _SIO2soundbuffer->size = inNumberPackets * 128;
      
      _SIO2soundbuffer->data = ( char * ) malloc( _SIO2soundbuffer->size );
      sio2IMAdecompress( inNumberPackets, _SIO2soundbuffer->format, (uint8_t*)inInputData, (int16_t*)_SIO2soundbuffer->data );
   }
   else
   {
      _SIO2soundbuffer->size = inNumberBytes;
      _SIO2soundbuffer->data = ( char * ) malloc( inNumberBytes );
      
      memcpy( _SIO2soundbuffer->data, inInputData, _SIO2soundbuffer->size );
   }
}

static int32_t ima_index_table[16] =
{
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8
};

static int32_t ima_step_table[89] =
{
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350,  22385, 24623, 27086, 29794, 32767
};

void sio2IMAdecompress_packet( const uint8_t* pSrc, int16_t* pDst, int32_t stride )
{
    // Read packet header
    uint16_t value  = *(uint16_t*)pSrc;
    uint16_t header = ( value >> 8 ) | ( value << 8 );
    int32_t predictor  = header & 0xff80;
    int32_t step_index = header & 0x007f;
    int32_t step, nibble, diff;
   
    // Sign extend predictor
    if( predictor & 0x8000 )
        predictor |= 0xffff0000;
   
    // Skip header
    pSrc += 2;
   
    // Read 64 nibbles, 2 at a time
    UInt32 byteCount = 32;
    while( byteCount-- )
    {
        // Read 2 nibbles
        uint8_t byte = *pSrc++;
      
        // Process low nibble
        nibble = byte & 0x0f;
        if( step_index < 0 ) step_index = 0;
        else if( step_index > 88 ) step_index = 88;
        step = ima_step_table[ step_index ];
        step_index += ima_index_table[ nibble ];
        diff = step >> 3;
        if (nibble & 4) diff += step;
        if (nibble & 2) diff += (step >> 1);
        if (nibble & 1) diff += (step >> 2);
        if (nibble & 8) predictor -= diff;
        else predictor += diff;
        if( predictor < -32768 ) predictor = -32768;
        else if( predictor > 32767 ) predictor = 32767;
        *pDst = predictor;
        pDst += stride;
      
        // Process high nibble
        nibble = byte >> 4;
        if( step_index < 0 ) step_index = 0;
        else if( step_index > 88 ) step_index = 88;
        step = ima_step_table[ step_index ];
        step_index += ima_index_table[ nibble ];
        diff = step >> 3;
        if (nibble & 4) diff += step;
        if (nibble & 2) diff += (step >> 1);
        if (nibble & 1) diff += (step >> 2);
        if (nibble & 8) predictor -= diff;
        else predictor += diff;
        if( predictor < -32768 ) predictor = -32768;
        else if( predictor > 32767 ) predictor = 32767;
        *pDst = predictor;
        pDst += stride;
    }
}

void sio2IMAdecompress( int32_t packetCount, const unsigned int format, const uint8_t* pSrc, int16_t* pDst )
{
    // Stereo?
    if( format == AL_FORMAT_STEREO16 )
    {
        // Decompress all stereo packets
        while( packetCount > 0 )
        {
            // Decompress channel0 and channel1 interleaved
            sio2IMAdecompress_packet( &pSrc[0],  &pDst[0], 2 );
            sio2IMAdecompress_packet( &pSrc[34], &pDst[1], 2 );
         
            // Next 2 channel packets
            pSrc += 34*2;
            pDst += 64*2;
            packetCount -= 2;
        }
    }
    else
    {
        // Decompress all mono packets
        while( packetCount-- )
        {
            // Decompress single channel
            sio2IMAdecompress_packet( pSrc, pDst, 1 );
         
            // Next channel packet
            pSrc += 34;
            pDst += 64;
        }
    }
}

In order to get the exporter to let you export a CAF file from blender you need to modify the exporter python script. Replace the following two lines (possibly lines 238, 240 but I have the prv decompressor so it is different line numbers):
Code:
            ogg_uc_pos = t_name.upper().rfind('.OGG' )

            if( ogg_uc_pos > -1 ):

with this line to allow caf export:
Code:
if( t_name.upper().endswith('.OGG') or t_name.upper().endswith('.CAF') ):

You will also need to include the AudioToolbox.framework in your project (until I can figure out caf header parsing) and add one line to sio2.h:
Code:
#include <AudioToolbox/AudioFileStream.h>

That should do it. Just add caf files (either uncompressed or ima4 compressed) as usual, export and enjoy faster audio load times.

I will be out of the country for 2 weeks so if someone wants to write a caf header parser it would finalize caf audio playback with no dependencies Smile.

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  zzajin on Fri Nov 21, 2008 1:16 pm

Cool! Thanks for sharing. I wish I wasn't preoccupied with other things for the next 2 1/2 weeks and could try this out Sad

zzajin

Posts : 81
Join date : 2008-10-14

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  sio2interactive on Fri Nov 21, 2008 5:13 pm

Hehe, same here Wink
avatar
sio2interactive

Posts : 1526
Join date : 2008-08-26
Age : 38
Location : Shanghai

View user profile http://sio2interactive.com

Back to top Go down

Re: Ogg vs Caf for audio

Post  sio2interactive on Fri Nov 21, 2008 5:14 pm

Hehe, same here Wink

Can you post also some benchmark concerning the loading time on the device... you something like file size, loading time in MS etc...

Cheers,
avatar
sio2interactive

Posts : 1526
Join date : 2008-08-26
Age : 38
Location : Shanghai

View user profile http://sio2interactive.com

Back to top Go down

Re: Ogg vs Caf for audio

Post  uprise78 on Fri Nov 21, 2008 5:28 pm

I will be out of the country for 2 weeks but when I get back I can certainly get a little benchmark together. If you don't see it on here shortly after that stick a post in this thread and it will send me a reminder email. Smile

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  uprise78 on Tue Dec 09, 2008 9:15 am

I ran an informal benchmark of an IMA4 compressed audio file and an OGG file. Obviously, the regular CAF files (uncompressed) load nearly instantly so I didn't bother adding them to the test due to the file size of an uncompressed CAF. Load times were checked before and after sio2SoundBufferLoad( _SIO2soundbuffer, _SIO2stream ).

The test file was converted using Audacity for OGG and afconvert for IMA4. The file sizes are:

OGG (rate 44100) - 4.7 meg
OGG (rate 22050) - 2.7 meg
CAF - 3.9 meg

OGG (rate 44100)
(1228838433) Start load (sound/testOGG44.ogg)
(1228838638) Done load (sound/testOGG44.ogg)

OGG (rate 22050)
(1228839027) Start load (sound/testOGG22.ogg)
(1228839126) Done load (sound/testOGG22.ogg)

CAF
(1228838426) Start load (sound/TestIMA4.caf)
(1228838428) Done load (sound/TestIMA4.caf)

It's pretty much no contest between ogg and caf on the device.

uprise78

Posts : 228
Join date : 2008-10-31

View user profile

Back to top Go down

Re: Ogg vs Caf for audio

Post  Sponsored content


Sponsored content


Back to top Go down

Page 1 of 2 1, 2  Next

View previous topic View next topic Back to top

- Similar topics

 
Permissions in this forum:
You cannot reply to topics in this forum