mkxp-z/SDL2_sound/SDL_sound_coreaudio.c

747 lines
24 KiB
C

/**
* SDL_sound; An abstract sound format decoding API.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Eric Wing.
*/
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
#if SOUND_SUPPORTS_COREAUDIO
#include <stddef.h> /* NULL */
#include <arpa/inet.h> /* htonl */
#include <AudioToolbox/AudioToolbox.h>
typedef struct CoreAudioFileContainer
{
AudioFileID* audioFileID;
ExtAudioFileRef extAudioFileRef;
AudioStreamBasicDescription* outputFormat;
} CoreAudioFileContainer;
static int CoreAudio_init(void)
{
return 1; /* always succeeds. */
} /* CoreAudio_init */
static void CoreAudio_quit(void)
{
/* it's a no-op. */
} /* CoreAudio_quit */
/*
https://developer.apple.com/library/ios/#documentation/MusicAudio/Reference/AudioFileConvertRef/Reference/reference.html
kAudioFileAIFFType = 'AIFF',
kAudioFileAIFCType = 'AIFC',
kAudioFileWAVEType = 'WAVE',
kAudioFileSoundDesigner2Type = 'Sd2f',
kAudioFileNextType = 'NeXT',
kAudioFileMP3Type = 'MPG3',
kAudioFileMP2Type = 'MPG2',
kAudioFileMP1Type = 'MPG1',
kAudioFileAC3Type = 'ac-3',
kAudioFileAAC_ADTSType = 'adts',
kAudioFileMPEG4Type = 'mp4f',
kAudioFileM4AType = 'm4af',
kAudioFileCAFType = 'caff',
kAudioFile3GPType = '3gpp',
kAudioFile3GP2Type = '3gp2',
kAudioFileAMRType = 'amrf'
*/
static AudioFileTypeID CoreAudio_GetAudioTypeForExtension(const char* file_extension)
{
if( (SDL_strcasecmp(file_extension, "aif") == 0)
|| (SDL_strcasecmp(file_extension, "aiff") == 0)
|| (SDL_strcasecmp(file_extension, "aifc") == 0)
)
{
return kAudioFileAIFCType;
}
else if( (SDL_strcasecmp(file_extension, "wav") == 0)
|| (SDL_strcasecmp(file_extension, "wave") == 0)
)
{
return kAudioFileWAVEType;
}
else if( (SDL_strcasecmp(file_extension, "mp3") == 0)
)
{
return kAudioFileMP3Type;
}
else if( (SDL_strcasecmp(file_extension, "mp4") == 0)
)
{
return kAudioFileMPEG4Type;
}
else if( (SDL_strcasecmp(file_extension, "m4a") == 0)
)
{
return kAudioFileM4AType;
}
else if( (SDL_strcasecmp(file_extension, "aac") == 0)
)
{
return kAudioFileAAC_ADTSType;
}
else if( (SDL_strcasecmp(file_extension, "aac") == 0)
)
{
return kAudioFileAAC_ADTSType;
}
else if( (SDL_strcasecmp(file_extension, "caf") == 0)
|| (SDL_strcasecmp(file_extension, "caff") == 0)
)
{
return kAudioFileCAFType;
}
else if( (SDL_strcasecmp(file_extension, "Sd2f") == 0)
|| (SDL_strcasecmp(file_extension, "sd2") == 0)
)
{
return kAudioFileSoundDesigner2Type;
}
else if( (SDL_strcasecmp(file_extension, "au") == 0)
|| (SDL_strcasecmp(file_extension, "next") == 0)
)
{
return kAudioFileNextType;
}
else if( (SDL_strcasecmp(file_extension, "mp2") == 0)
)
{
return kAudioFileMP2Type;
}
else if( (SDL_strcasecmp(file_extension, "mp1") == 0)
)
{
return kAudioFileMP1Type;
}
else if( (SDL_strcasecmp(file_extension, "ac3") == 0)
)
{
return kAudioFileAC3Type;
}
else if( (SDL_strcasecmp(file_extension, "3gpp") == 0)
)
{
return kAudioFile3GPType;
}
else if( (SDL_strcasecmp(file_extension, "3gp2") == 0)
)
{
return kAudioFile3GP2Type;
}
else if( (SDL_strcasecmp(file_extension, "amrf") == 0)
|| (SDL_strcasecmp(file_extension, "amr") == 0)
)
{
return kAudioFileAMRType;
}
else if( (SDL_strcasecmp(file_extension, "ima4") == 0)
|| (SDL_strcasecmp(file_extension, "ima") == 0)
)
{
/* not sure about this one */
return kAudioFileCAFType;
}
else
{
return 0;
}
}
static const char* CoreAudio_FourCCToString(int32_t error_code)
{
static char return_string[16];
uint32_t big_endian_code = htonl(error_code);
char* big_endian_str = (char*)&big_endian_code;
// see if it appears to be a 4-char-code
if(isprint(big_endian_str[0])
&& isprint(big_endian_str[1])
&& isprint(big_endian_str[2])
&& isprint (big_endian_str[3]))
{
return_string[0] = '\'';
return_string[1] = big_endian_str[0];
return_string[2] = big_endian_str[1];
return_string[3] = big_endian_str[2];
return_string[4] = big_endian_str[3];
return_string[5] = '\'';
return_string[6] = '\0';
}
else if(error_code > -200000 && error_code < 200000)
{
// no, format it as an integer
snprintf(return_string, 16, "%d", error_code);
}
else
{
// no, format it as an integer but in hex
snprintf(return_string, 16, "0x%x", error_code);
}
return return_string;
}
SInt64 CoreAudio_SizeCallback(void* inClientData)
{
SDL_RWops* rw_ops = (SDL_RWops*)inClientData;
SInt64 current_position = SDL_RWtell(rw_ops);
SInt64 end_position = SDL_RWseek(rw_ops, 0, SEEK_END);
SDL_RWseek(rw_ops, current_position, SEEK_SET);
// fprintf(stderr, "CoreAudio_SizeCallback:%d\n", end_position);
return end_position;
}
OSStatus CoreAudio_ReadCallback(
void* inClientData,
SInt64 inPosition,
UInt32 requestCount,
void* data_buffer,
UInt32* actualCount
)
{
SDL_RWops* rw_ops = (SDL_RWops*)inClientData;
SDL_RWseek(rw_ops, inPosition, SEEK_SET);
size_t bytes_actually_read = SDL_RWread(rw_ops, data_buffer, 1, requestCount);
// Not sure how to test for a read error with SDL_RWops
// fprintf(stderr, "CoreAudio_ReadCallback:%d, %d\n", requestCount, bytes_actually_read);
*actualCount = bytes_actually_read;
return noErr;
}
static int CoreAudio_open(Sound_Sample *sample, const char *ext)
{
CoreAudioFileContainer* core_audio_file_container;
AudioFileID* audio_file_id;
OSStatus error_result;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
AudioStreamBasicDescription actual_format;
AudioStreamBasicDescription output_format;
Float64 estimated_duration;
UInt32 format_size;
core_audio_file_container = (CoreAudioFileContainer*)SDL_malloc(sizeof(CoreAudioFileContainer));
BAIL_IF_MACRO(core_audio_file_container == NULL, ERR_OUT_OF_MEMORY, 0);
audio_file_id = (AudioFileID*)SDL_malloc(sizeof(AudioFileID));
BAIL_IF_MACRO(audio_file_id == NULL, ERR_OUT_OF_MEMORY, 0);
error_result = AudioFileOpenWithCallbacks(
internal->rw,
CoreAudio_ReadCallback,
NULL,
CoreAudio_SizeCallback,
NULL,
CoreAudio_GetAudioTypeForExtension(ext),
audio_file_id
);
if (error_result != noErr)
{
AudioFileClose(*audio_file_id);
SDL_free(audio_file_id);
SDL_free(core_audio_file_container);
SNDDBG(("Core Audio: can't grok data. reason: [%s].\n", CoreAudio_FourCCToString(error_result)));
BAIL_MACRO("Core Audio: Not valid audio data.", 0);
} /* if */
format_size = sizeof(actual_format);
error_result = AudioFileGetProperty(
*audio_file_id,
kAudioFilePropertyDataFormat,
&format_size,
&actual_format
);
if (error_result != noErr)
{
AudioFileClose(*audio_file_id);
SDL_free(audio_file_id);
SDL_free(core_audio_file_container);
SNDDBG(("Core Audio: AudioFileGetProperty failed. reason: [%s]", CoreAudio_FourCCToString(error_result)));
BAIL_MACRO("Core Audio: Not valid audio data.", 0);
} /* if */
format_size = sizeof(estimated_duration);
error_result = AudioFileGetProperty(
*audio_file_id,
kAudioFilePropertyEstimatedDuration,
&format_size,
&estimated_duration
);
if (error_result != noErr)
{
AudioFileClose(*audio_file_id);
SDL_free(audio_file_id);
SDL_free(core_audio_file_container);
SNDDBG(("Core Audio: AudioFileGetProperty failed. reason: [%s].\n", CoreAudio_FourCCToString(error_result)));
BAIL_MACRO("Core Audio: Not valid audio data.", 0);
} /* if */
core_audio_file_container->audioFileID = audio_file_id;
internal->decoder_private = core_audio_file_container;
sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
sample->actual.rate = (UInt32) actual_format.mSampleRate;
sample->actual.channels = (UInt8)actual_format.mChannelsPerFrame;
internal->total_time = (SInt32)(estimated_duration * 1000.0 + 0.5);
#if 0
/* FIXME: Both Core Audio and SDL 1.3 support float and 32-bit formats */
if(actual_format.mFormatFlags & kAudioFormatFlagIsBigEndian)
{
if(16 == actual_format.mBitsPerChannel)
{
if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags)
{
sample->actual.format = AUDIO_S16MSB;
}
else
{
sample->actual.format = AUDIO_U16MSB;
}
}
else if(8 == actual_format.mBitsPerChannel)
{
if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags)
{
sample->actual.format = AUDIO_S8;
}
else
{
sample->actual.format = AUDIO_U8;
}
}
else // might be 0 for undefined?
{
// This case seems to come up a lot for me. Maybe for file types like .m4a?
sample->actual.format = AUDIO_S16SYS;
SNDDBG(("Core Audio: Unsupported actual_format.mBitsPerChannel: [%d].\n", actual_format.mBitsPerChannel));
}
}
else // little endian
{
if(16 == actual_format.mBitsPerChannel)
{
if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags)
{
sample->actual.format = AUDIO_S16LSB;
}
else
{
sample->actual.format = AUDIO_U16LSB;
}
}
else if(8 == actual_format.mBitsPerChannel)
{
if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags)
{
sample->actual.format = AUDIO_S8;
}
else
{
sample->actual.format = AUDIO_U8;
}
}
else // might be 0 for undefined?
{
sample->actual.format = AUDIO_S16SYS;
SNDDBG(("Core Audio: Unsupported actual_format.mBitsPerChannel: [%d].\n", actual_format.mBitsPerChannel));
}
}
#else
/*
* I want to use Core Audio to do conversion and decoding for performance reasons.
* This is particularly important on mobile devices like iOS.
* Taking from the Ogg Vorbis decode, I pretend the "actual" format is the same
* as the desired format.
*/
sample->actual.format = (sample->desired.format == 0) ?
AUDIO_S16SYS : sample->desired.format;
#endif
SNDDBG(("CoreAudio: channels == (%d).\n", sample->actual.channels));
SNDDBG(("CoreAudio: sampling rate == (%d).\n",sample->actual.rate));
SNDDBG(("CoreAudio: total seconds of sample == (%d).\n", internal->total_time));
SNDDBG(("CoreAudio: sample->actual.format == (%d).\n", sample->actual.format));
error_result = ExtAudioFileWrapAudioFileID(*audio_file_id,
false, // set to false for read-only
&core_audio_file_container->extAudioFileRef
);
if(error_result != noErr)
{
AudioFileClose(*audio_file_id);
SDL_free(audio_file_id);
SDL_free(core_audio_file_container);
SNDDBG(("Core Audio: can't wrap data. reason: [%s].\n", CoreAudio_FourCCToString(error_result)));
BAIL_MACRO("Core Audio: Failed to wrap data.", 0);
} /* if */
/* The output format must be linear PCM because that's the only type OpenAL knows how to deal with.
* Set the client format to 16 bit signed integer (native-endian) data because that is the most
* optimal format on iPhone/iPod Touch hardware.
* Maintain the channel count and sample rate of the original source format.
*/
output_format.mSampleRate = actual_format.mSampleRate; // preserve the original sample rate
output_format.mChannelsPerFrame = actual_format.mChannelsPerFrame; // preserve the number of channels
output_format.mFormatID = kAudioFormatLinearPCM; // We want linear PCM data
output_format.mFramesPerPacket = 1; // We know for linear PCM, the definition is 1 frame per packet
if(sample->desired.format == 0)
{
// do AUDIO_S16SYS
output_format.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; // I seem to read failures problems without kAudioFormatFlagIsPacked. From a mailing list post, this seems to be a Core Audio bug.
output_format.mBitsPerChannel = 16; // We know we want 16-bit
}
else
{
output_format.mFormatFlags = 0; // clear flags
output_format.mFormatFlags |= kAudioFormatFlagIsPacked; // I seem to read failures problems without kAudioFormatFlagIsPacked. From a mailing list post, this seems to be a Core Audio bug.
// Mask against bitsize
if(0xFF & sample->desired.format)
{
output_format.mBitsPerChannel = 16; /* 16-bit */
}
else
{
output_format.mBitsPerChannel = 8; /* 8-bit */
}
// Mask for signed/unsigned
if((1<<15) & sample->desired.format)
{
output_format.mFormatFlags = output_format.mFormatFlags | kAudioFormatFlagIsSignedInteger;
}
else
{
// no flag set for unsigned
}
// Mask for big/little endian
if((1<<12) & sample->desired.format)
{
output_format.mFormatFlags = output_format.mFormatFlags | kAudioFormatFlagIsBigEndian;
}
else
{
// no flag set for little endian
}
}
output_format.mBytesPerPacket = output_format.mBitsPerChannel/8 * output_format.mChannelsPerFrame; // e.g. 16-bits/8 * channels => so 2-bytes per channel per frame
output_format.mBytesPerFrame = output_format.mBitsPerChannel/8 * output_format.mChannelsPerFrame; // For PCM, since 1 frame is 1 packet, it is the same as mBytesPerPacket
/*
output_format.mSampleRate = actual_format.mSampleRate; // preserve the original sample rate
output_format.mChannelsPerFrame = actual_format.mChannelsPerFrame; // preserve the number of channels
output_format.mFormatID = kAudioFormatLinearPCM; // We want linear PCM data
// output_format.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
output_format.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger;
output_format.mFramesPerPacket = 1; // We know for linear PCM, the definition is 1 frame per packet
output_format.mBitsPerChannel = 16; // We know we want 16-bit
output_format.mBytesPerPacket = 2 * output_format.mChannelsPerFrame; // We know we are using 16-bit, so 2-bytes per channel per frame
output_format.mBytesPerFrame = 2 * output_format.mChannelsPerFrame; // For PCM, since 1 frame is 1 packet, it is the same as mBytesPerPacket
*/
SNDDBG(("output_format: mSampleRate: %lf\n", output_format.mSampleRate));
SNDDBG(("output_format: mChannelsPerFrame: %d\n", output_format.mChannelsPerFrame));
SNDDBG(("output_format: mFormatID: %d\n", output_format.mFormatID));
SNDDBG(("output_format: mFormatFlags: %d\n", output_format.mFormatFlags));
SNDDBG(("output_format: mFramesPerPacket: %d\n", output_format.mFramesPerPacket));
SNDDBG(("output_format: mBitsPerChannel: %d\n", output_format.mBitsPerChannel));
SNDDBG(("output_format: mBytesPerPacket: %d\n", output_format.mBytesPerPacket));
SNDDBG(("output_format: mBytesPerFrame: %d\n", output_format.mBytesPerFrame));
/* Set the desired client (output) data format */
error_result = ExtAudioFileSetProperty(core_audio_file_container->extAudioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(output_format), &output_format);
if(noErr != error_result)
{
ExtAudioFileDispose(core_audio_file_container->extAudioFileRef);
AudioFileClose(*audio_file_id);
SDL_free(audio_file_id);
SDL_free(core_audio_file_container);
SNDDBG(("Core Audio: ExtAudioFileSetProperty(kExtAudioFileProperty_ClientDataFormat) failed, reason: [%s].\n", CoreAudio_FourCCToString(error_result)));
BAIL_MACRO("Core Audio: Not valid audio data.", 0);
}
core_audio_file_container->outputFormat = (AudioStreamBasicDescription*)SDL_malloc(sizeof(AudioStreamBasicDescription));
BAIL_IF_MACRO(core_audio_file_container->outputFormat == NULL, ERR_OUT_OF_MEMORY, 0);
/* Copy the output format to the audio_description that was passed in so the
* info will be returned to the user.
*/
SDL_memcpy(core_audio_file_container->outputFormat, &output_format, sizeof(AudioStreamBasicDescription));
return 1;
} /* CoreAudio_open */
static void CoreAudio_close(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private;
SDL_free(core_audio_file_container->outputFormat);
ExtAudioFileDispose(core_audio_file_container->extAudioFileRef);
AudioFileClose(*core_audio_file_container->audioFileID);
SDL_free(core_audio_file_container->audioFileID);
SDL_free(core_audio_file_container);
} /* CoreAudio_close */
static Uint32 CoreAudio_read(Sound_Sample *sample)
{
OSStatus error_result = noErr;
/* Documentation/example shows SInt64, but is problematic for big endian
* on 32-bit cast for ExtAudioFileRead() because it takes the upper
* bits which turn to 0.
*/
UInt32 buffer_size_in_frames = 0;
UInt32 buffer_size_in_frames_remaining = 0;
UInt32 total_frames_read = 0;
UInt32 data_buffer_size = 0;
UInt32 bytes_remaining = 0;
size_t total_bytes_read = 0;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private;
UInt32 max_buffer_size = internal->buffer_size;
// printf("internal->buffer_size=%d, internal->buffer=0x%x, sample->buffer_size=%d\n", internal->buffer_size, internal->buffer, sample->buffer_size);
// printf("internal->max_buffer_size=%d\n", max_buffer_size);
/* Compute how many frames will fit into our max buffer size */
/* Warning: If this is not evenly divisible, the buffer will not be completely filled which violates the SDL_sound assumption. */
buffer_size_in_frames = max_buffer_size / core_audio_file_container->outputFormat->mBytesPerFrame;
// printf("buffer_size_in_frames=%ld, internal->buffer_size=%d, internal->buffer=0x%x outputFormat->mBytesPerFrame=%d, sample->buffer_size=%d\n", buffer_size_in_frames, internal->buffer_size, internal->buffer, core_audio_file_container->outputFormat->mBytesPerFrame, sample->buffer_size);
// void* temp_buffer = SDL_malloc(max_buffer_size);
AudioBufferList audio_buffer_list;
audio_buffer_list.mNumberBuffers = 1;
audio_buffer_list.mBuffers[0].mDataByteSize = max_buffer_size;
audio_buffer_list.mBuffers[0].mNumberChannels = core_audio_file_container->outputFormat->mChannelsPerFrame;
audio_buffer_list.mBuffers[0].mData = internal->buffer;
bytes_remaining = max_buffer_size;
buffer_size_in_frames_remaining = buffer_size_in_frames;
// oops. Due to the kAudioFormatFlagIsPacked bug,
// I was misled to believe that Core Audio
// was not always filling my entire requested buffer.
// So this while-loop might be unnecessary.
// However, I have not exhaustively tested all formats,
// so maybe it is possible this loop is useful.
// It might also handle the not-evenly disvisible case above.
while(buffer_size_in_frames_remaining > 0 && !(sample->flags & SOUND_SAMPLEFLAG_EOF))
{
data_buffer_size = (UInt32)(buffer_size_in_frames * core_audio_file_container->outputFormat->mBytesPerFrame);
// printf("data_buffer_size=%d\n", data_buffer_size);
buffer_size_in_frames = buffer_size_in_frames_remaining;
// printf("reading buffer_size_in_frames=%"PRId64"\n", buffer_size_in_frames);
audio_buffer_list.mBuffers[0].mDataByteSize = bytes_remaining;
audio_buffer_list.mBuffers[0].mData = &(((UInt8*)internal->buffer)[total_bytes_read]);
/* Read the data into an AudioBufferList */
error_result = ExtAudioFileRead(core_audio_file_container->extAudioFileRef, &buffer_size_in_frames, &audio_buffer_list);
if(error_result == noErr)
{
/* Success */
total_frames_read += buffer_size_in_frames;
buffer_size_in_frames_remaining = buffer_size_in_frames_remaining - buffer_size_in_frames;
// printf("read buffer_size_in_frames=%"PRId64", buffer_size_in_frames_remaining=%"PRId64"\n", buffer_size_in_frames, buffer_size_in_frames_remaining);
/* ExtAudioFileRead returns the number of frames actually read. Need to convert back to bytes. */
data_buffer_size = (UInt32)(buffer_size_in_frames * core_audio_file_container->outputFormat->mBytesPerFrame);
// printf("data_buffer_size=%d\n", data_buffer_size);
total_bytes_read += data_buffer_size;
bytes_remaining = bytes_remaining - data_buffer_size;
/* Note: 0 == buffer_size_in_frames is a legitimate value meaning we are EOF. */
if(0 == buffer_size_in_frames)
{
sample->flags |= SOUND_SAMPLEFLAG_EOF;
}
}
else
{
SNDDBG(("Core Audio: ExtAudioFileReadfailed, reason: [%s].\n", CoreAudio_FourCCToString(error_result)));
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
break;
}
}
if( (!(sample->flags & SOUND_SAMPLEFLAG_EOF)) && (total_bytes_read < max_buffer_size))
{
SNDDBG(("Core Audio: ExtAudioFileReadfailed SOUND_SAMPLEFLAG_EAGAIN, reason: [total_bytes_read < max_buffer_size], %d, %d.\n", total_bytes_read , max_buffer_size));
/* Don't know what to do here. */
sample->flags |= SOUND_SAMPLEFLAG_EAGAIN;
}
return total_bytes_read;
} /* CoreAudio_read */
static int CoreAudio_rewind(Sound_Sample *sample)
{
OSStatus error_result = noErr;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private;
error_result = ExtAudioFileSeek(core_audio_file_container->extAudioFileRef, 0);
if(error_result != noErr)
{
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
}
return 1;
} /* CoreAudio_rewind */
/* Note: I found this tech note:
https://developer.apple.com/library/mac/#qa/qa2009/qa1678.html
I don't know if this applies to us. So far, I haven't noticed the problem,
so I haven't applied any of the techniques.
*/
static int CoreAudio_seek(Sound_Sample *sample, Uint32 ms)
{
OSStatus error_result = noErr;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private;
SInt64 frame_offset = 0;
AudioStreamBasicDescription actual_format;
UInt32 format_size;
/* I'm confused. The Apple documentation says this:
"Seek position is specified in the sample rate and frame count of the file’s audio data format
— not your application’s audio data format."
My interpretation is that I want to get the "actual format of the file and compute the frame offset.
But when I try that, seeking goes to the wrong place.
When I use outputFormat, things seem to work correctly.
I must be misinterpreting the documentation or doing something wrong.
*/
#if 0 /* not working */
format_size = sizeof(AudioStreamBasicDescription);
error_result = AudioFileGetProperty(
*core_audio_file_container->audioFileID,
kAudioFilePropertyDataFormat,
&format_size,
&actual_format
);
if(error_result != noErr)
{
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
BAIL_MACRO("Core Audio: Could not GetProperty for kAudioFilePropertyDataFormat.", 0);
} /* if */
// packetIndex = (pos * sampleRate) / framesPerPacket
// frame_offset = (SInt64)((ms/1000.0 * actual_format.mSampleRate) / actual_format.mFramesPerPacket);
#else /* seems to work, but I'm confused */
// packetIndex = (pos * sampleRate) / framesPerPacket
frame_offset = (SInt64)((ms/1000.0 * core_audio_file_container->outputFormat->mSampleRate) / core_audio_file_container->outputFormat->mFramesPerPacket);
#endif
// computed against actual format and not the client format
error_result = ExtAudioFileSeek(core_audio_file_container->extAudioFileRef, frame_offset);
if(error_result != noErr)
{
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
}
return 1;
} /* CoreAudio_seek */
static const char *extensions_coreaudio[] =
{
"aif",
"aiff",
"aifc",
"wav",
"wave",
"mp3",
"mp4",
"m4a",
"aac",
"caf",
"Sd2f",
"Sd2",
"au",
"next",
"mp2",
"mp1",
"ac3",
"3gpp",
"3gp2",
"amrf",
"amr",
"ima4",
"ima",
NULL
};
const Sound_DecoderFunctions __Sound_DecoderFunctions_CoreAudio =
{
{
extensions_coreaudio,
"Decode audio through Core Audio through",
"Eric Wing <ewing . public @ playcontrol.net>",
"https://playcontrol.net"
},
CoreAudio_init, /* init() method */
CoreAudio_quit, /* quit() method */
CoreAudio_open, /* open() method */
CoreAudio_close, /* close() method */
CoreAudio_read, /* read() method */
CoreAudio_rewind, /* rewind() method */
CoreAudio_seek /* seek() method */
};
#endif /* SOUND_SUPPORTS_COREAUDIO */
/* end of SDL_sound_coreaudio.c ... */