Uint32 Sound_Decode(Sound_Sample *sample) { Sound_SampleInternal *internal = NULL; Uint32 retval = 0; SNDDBG(("Sound_Decode(%p), buffer_size=%d\n", sample, sample->buffer_size)); /* a boatload of sanity checks... */ BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0); BAIL_IF_MACRO(sample == NULL, ERR_INVALID_ARGUMENT, 0); BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_PREV_ERROR, 0); BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_EOF, ERR_PREV_EOF, 0); internal = (Sound_SampleInternal *) sample->opaque; assert(sample->buffer != NULL); assert(sample->buffer_size > 0); assert(internal->buffer != NULL); assert(internal->buffer_size > 0); /* reset EAGAIN. Decoder can flip it back on if it needs to. */ sample->flags &= ~SOUND_SAMPLEFLAG_EAGAIN; retval = internal->funcs->read(sample); if (retval > 0 && internal->sdlcvt.needed) { internal->sdlcvt.len = retval; Sound_ConvertAudio(&internal->sdlcvt); retval = internal->sdlcvt.len_cvt; } /* if */ SNDDBG(("Sound_Decode return %d, cvt=%d, flags=%x\n", retval, internal->sdlcvt.needed, sample->flags)); return(retval); } /* Sound_Decode */
static int MIDI_open(Sound_Sample *sample, const char *ext) { Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; SDL_RWops *rw = internal->rw; SDL_AudioSpec spec; MidiSong *song; spec.channels = 2; spec.format = AUDIO_S16SYS; spec.freq = 44100; spec.samples = 4096; song = Timidity_LoadSong(rw, &spec); BAIL_IF_MACRO(song == NULL, "MIDI: Not a MIDI file.", 0); Timidity_SetVolume(song, 100); Timidity_Start(song); SNDDBG(("MIDI: Accepting data stream.\n")); internal->decoder_private = (void *) song; sample->actual.channels = 2; sample->actual.rate = 44100; sample->actual.format = AUDIO_S16SYS; sample->flags = SOUND_SAMPLEFLAG_CANSEEK; return(1); /* we'll handle this data. */ } /* MIDI_open */
/* * This is declared in the internal header. */ void __Sound_SetError(const char *str) { ErrMsg *err; if (str == NULL) return; SNDDBG(("__Sound_SetError(\"%s\");%s\n", str, (initialized) ? "" : " [NOT INITIALIZED!]")); if (!initialized) return; err = findErrorForCurrentThread(); if (err == NULL) { err = (ErrMsg *) malloc(sizeof (ErrMsg)); if (err == NULL) return; /* uhh...? */ memset((void *) err, '\0', sizeof (ErrMsg)); err->tid = SDL_ThreadID(); SDL_LockMutex(errorlist_mutex); err->next = error_msgs; error_msgs = err; SDL_UnlockMutex(errorlist_mutex); } /* if */ err->error_available = 1; strncpy(err->error_string, str, sizeof (err->error_string)); err->error_string[sizeof (err->error_string) - 1] = '\0'; } /* __Sound_SetError */
static __inline__ void output_ogg_comments(OggVorbis_File *vf) { #if (defined DEBUG_CHATTER) int i; vorbis_comment *vc = ov_comment(vf, -1); if (vc == NULL) return; SNDDBG(("OGG: vendor == [%s].\n", vc->vendor)); for (i = 0; i < vc->comments; i++) { SNDDBG(("OGG: user comment [%s].\n", vc->user_comments[i])); } /* for */ #endif } /* output_ogg_comments */
/* This is meant to find and open files for reading */ SDL_RWops *open_file(char *name) { SDL_RWops *rw; if (!name || !(*name)) { SNDDBG(("Attempted to open nameless file.\n")); return 0; } /* First try the given name */ SNDDBG(("Trying to open %s\n", name)); if ((rw = SDL_RWFromFile(name, "rb"))) return rw; if (name[0] != PATH_SEP) { char current_filename[1024]; PathList *plp = pathlist; int l; while (plp) /* Try along the path then */ { *current_filename = 0; l = strlen(plp->path); if(l) { strcpy(current_filename, plp->path); if(current_filename[l - 1] != PATH_SEP) { current_filename[l] = PATH_SEP; current_filename[l + 1] = '\0'; } } strcat(current_filename, name); SNDDBG(("Trying to open %s\n", current_filename)); if ((rw = SDL_RWFromFile(current_filename, "rb"))) return rw; plp = plp->next; } } /* Nothing could be opened. */ SNDDBG(("Could not open %s\n", name)); return 0; }
/* This'll allocate memory or die. */ void *safe_malloc(size_t count) { void *p; p = malloc(count); if (p == NULL) SNDDBG(("Sorry. Couldn't malloc %d bytes.\n", count)); return p; }
static int AU_open(Sound_Sample *sample, const char *ext) { Sound_SampleInternal *internal = sample->opaque; SDL_RWops *rw = internal->rw; int skip, hsize, i, bytes_per_second; struct au_file_hdr hdr; struct audec *dec; char c; /* read_au_header() will do byte order swapping. */ BAIL_IF_MACRO(!read_au_header(rw, &hdr), "AU: bad header", 0); dec = malloc(sizeof *dec); BAIL_IF_MACRO(dec == NULL, ERR_OUT_OF_MEMORY, 0); internal->decoder_private = dec; if (hdr.magic == AU_MAGIC) { /* valid magic */ dec->encoding = hdr.encoding; switch(dec->encoding) { case AU_ENC_ULAW_8: /* Convert 8-bit µ-law to 16-bit linear on the fly. This is slightly wasteful if the audio driver must convert them back, but µ-law only devices are rare (mostly _old_ Suns) */ sample->actual.format = AUDIO_S16SYS; break; case AU_ENC_LINEAR_8: sample->actual.format = AUDIO_S8; break; case AU_ENC_LINEAR_16: sample->actual.format = AUDIO_S16MSB; break; default: free(dec); BAIL_MACRO("AU: Unsupported .au encoding", 0); } /* switch */ sample->actual.rate = hdr.sample_rate; sample->actual.channels = hdr.channels; dec->remaining = hdr.data_size; hsize = hdr.hdr_size; /* skip remaining part of header (input may be unseekable) */ for (i = HDR_SIZE; i < hsize; i++) { if (SDL_RWread(rw, &c, 1, 1) != 1) { free(dec); BAIL_MACRO(ERR_IO_ERROR, 0); } /* if */ } /* for */ } /* if */ else if (__Sound_strcasecmp(ext, "au") == 0) { /* * A number of files in the wild have the .au extension but no valid * header; these are traditionally assumed to be 8kHz µ-law. Handle * them here only if the extension is recognized. */ SNDDBG(("AU: Invalid header, assuming raw 8kHz µ-law.\n")); /* if seeking fails, we lose 24 samples. big deal */ SDL_RWseek(rw, -HDR_SIZE, SEEK_CUR); dec->encoding = AU_ENC_ULAW_8; dec->remaining = (Uint32)-1; /* no limit */ sample->actual.format = AUDIO_S16SYS; sample->actual.rate = 8000; sample->actual.channels = 1; } /* else if */ else { free(dec); BAIL_MACRO("AU: Not an .AU stream.", 0); } /* else */ bytes_per_second = ( ( dec->encoding == AU_ENC_LINEAR_16 ) ? 2 : 1 ) * sample->actual.rate * sample->actual.channels ; internal->total_time = ((dec->remaining == -1) ? (-1) : ( ( dec->remaining / bytes_per_second ) * 1000 ) + ( ( dec->remaining % bytes_per_second ) * 1000 / bytes_per_second ) ); sample->flags = SOUND_SAMPLEFLAG_CANSEEK; dec->total = dec->remaining; dec->start_offset = SDL_RWtell(rw); SNDDBG(("AU: Accepting data stream.\n")); return(1); } /* AU_open */
/* * The bulk of the Sound_NewSample() work is done here... * Ask the specified decoder to handle the data in (rw), and if * so, construct the Sound_Sample. Otherwise, try to wind (rw)'s stream * back to where it was, and return false. */ static int init_sample(const Sound_DecoderFunctions *funcs, Sound_Sample *sample, const char *ext, Sound_AudioInfo *_desired) { Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; Sound_AudioInfo desired; int pos = SDL_RWtell(internal->rw); /* fill in the funcs for this decoder... */ sample->decoder = &funcs->info; internal->funcs = funcs; if (!funcs->open(sample, ext)) { SDL_RWseek(internal->rw, pos, SEEK_SET); /* set for next try... */ return(0); } /* if */ /* success; we've got a decoder! */ /* Now we need to set up the conversion buffer... */ memcpy(&desired, (_desired != NULL) ? _desired : &sample->actual, sizeof (Sound_AudioInfo)); if (desired.format == 0) desired.format = sample->actual.format; if (desired.channels == 0) desired.channels = sample->actual.channels; if (desired.rate == 0) desired.rate = sample->actual.rate; if (Sound_BuildAudioCVT(&internal->sdlcvt, sample->actual.format, sample->actual.channels, sample->actual.rate, desired.format, desired.channels, desired.rate, sample->buffer_size) == -1) { __Sound_SetError(SDL_GetError()); funcs->close(sample); SDL_RWseek(internal->rw, pos, SEEK_SET); /* set for next try... */ return(0); } /* if */ if (internal->sdlcvt.len_mult > 1) { void *rc = realloc(sample->buffer, sample->buffer_size * internal->sdlcvt.len_mult); if (rc == NULL) { funcs->close(sample); SDL_RWseek(internal->rw, pos, SEEK_SET); /* set for next try... */ return(0); } /* if */ sample->buffer = rc; } /* if */ /* these pointers are all one and the same. */ memcpy(&sample->desired, &desired, sizeof (Sound_AudioInfo)); internal->sdlcvt.buf = internal->buffer = sample->buffer; internal->buffer_size = sample->buffer_size / internal->sdlcvt.len_mult; internal->sdlcvt.len = internal->buffer_size; /* Prepend our new Sound_Sample to the sample_list... */ SDL_LockMutex(samplelist_mutex); internal->next = sample_list; if (sample_list != NULL) ((Sound_SampleInternal *) sample_list->opaque)->prev = sample; sample_list = sample; SDL_UnlockMutex(samplelist_mutex); SNDDBG(("New sample DESIRED format: %s format, %d rate, %d channels.\n", fmt_to_str(sample->desired.format), sample->desired.rate, sample->desired.channels)); SNDDBG(("New sample ACTUAL format: %s format, %d rate, %d channels.\n", fmt_to_str(sample->actual.format), sample->actual.rate, sample->actual.channels)); SNDDBG(("On-the-fly conversion: %s.\n", internal->sdlcvt.needed ? "ENABLED" : "DISABLED")); return(1); } /* init_sample */
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 = 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_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*)malloc(sizeof(CoreAudioFileContainer)); BAIL_IF_MACRO(core_audio_file_container == NULL, ERR_OUT_OF_MEMORY, 0); audio_file_id = (AudioFileID*)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); free(audio_file_id); 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); free(audio_file_id); 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); free(audio_file_id); 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); free(audio_file_id); 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); free(audio_file_id); 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*)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. */ memcpy(core_audio_file_container->outputFormat, &output_format, sizeof(AudioStreamBasicDescription)); return(1); } /* CoreAudio_open */
static int MPGLIB_open(Sound_Sample *sample, const char *ext) { Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; mpglib_t *mpg = NULL; int rc; /* * If I understand things correctly, MP3 files don't really have any * magic header we can check for. The MP3 player is expected to just * pick the first thing that looks like a valid frame and start * playing from there. * * So here's what we do: If the caller insists that this is really * MP3 we'll take his word for it. Otherwise, use the same test as * SDL_mixer does and check if the stream starts with something that * looks like a frame. * * A frame begins with 11 bits of frame sync (all bits must be set), * followed by a two-bit MPEG Audio version ID: * * 00 - MPEG Version 2.5 (later extension of MPEG 2) * 01 - reserved * 10 - MPEG Version 2 (ISO/IEC 13818-3) * 11 - MPEG Version 1 (ISO/IEC 11172-3) * * Apparently we don't handle MPEG Version 2.5. */ if (__Sound_strcasecmp(ext, "MP3") != 0) { Uint8 mp3_magic[2]; if (SDL_RWread(internal->rw, mp3_magic, sizeof (mp3_magic), 1) != 1) BAIL_MACRO("MPGLIB: Could not read MP3 magic.", 0); if (mp3_magic[0] != 0xFF || (mp3_magic[1] & 0xF0) != 0xF0) BAIL_MACRO("MPGLIB: Not an MP3 stream.", 0); /* If the seek fails, we'll probably miss a frame, but oh well. */ SDL_RWseek(internal->rw, -sizeof (mp3_magic), SEEK_CUR); } /* if */ mpg = (mpglib_t *) malloc(sizeof (mpglib_t)); BAIL_IF_MACRO(mpg == NULL, ERR_OUT_OF_MEMORY, 0); memset(mpg, '\0', sizeof (mpglib_t)); InitMP3(&mpg->mp); rc = SDL_RWread(internal->rw, mpg->inbuf, 1, sizeof (mpg->inbuf)); if (rc <= 0) { free(mpg); BAIL_MACRO("MPGLIB: Failed to read any data at all", 0); } /* if */ if (decodeMP3(&mpg->mp, mpg->inbuf, rc, mpg->outbuf, sizeof (mpg->outbuf), &mpg->outleft) == MP3_ERR) { free(mpg); BAIL_MACRO("MPGLIB: Not an MP3 stream?", 0); } /* if */ SNDDBG(("MPGLIB: Accepting data stream.\n")); internal->decoder_private = mpg; sample->actual.rate = mpglib_freqs[mpg->mp.fr.sampling_frequency]; sample->actual.channels = mpg->mp.fr.stereo; sample->actual.format = AUDIO_S16SYS; sample->flags = SOUND_SAMPLEFLAG_NONE; return(1); /* we'll handle this data. */ } /* MPGLIB_open */
static int MODPLUG_open(Sound_Sample *sample, const char *ext) { Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; ModPlugFile *module; Uint8 *data; size_t size; Uint32 retval; int has_extension = 0; int i; /* * Apparently ModPlug's loaders are too forgiving. They gladly accept * streams that they shouldn't. For now, rely on file extension instead. */ for (i = 0; extensions_modplug[i] != NULL; i++) { if (__Sound_strcasecmp(ext, extensions_modplug[i]) == 0) { has_extension = 1; break; } /* if */ } /* for */ if (!has_extension) { SNDDBG(("MODPLUG: Unrecognized file type: %s\n", ext)); BAIL_MACRO("MODPLUG: Not a module file.", 0); } /* if */ /* * ModPlug needs the entire stream in one big chunk. I don't like it, * but I don't think there's any way around it. */ data = (Uint8 *) malloc(CHUNK_SIZE); BAIL_IF_MACRO(data == NULL, ERR_OUT_OF_MEMORY, 0); size = 0; do { retval = SDL_RWread(internal->rw, &data[size], 1, CHUNK_SIZE); size += retval; if (retval == CHUNK_SIZE) { data = (Uint8 *) realloc(data, size + CHUNK_SIZE); BAIL_IF_MACRO(data == NULL, ERR_OUT_OF_MEMORY, 0); } /* if */ } while (retval > 0); /* * It's only safe to change these settings when there're * no other mods being decoded... */ if (modplug_mutex != NULL) SDL_LockMutex(modplug_mutex); if (total_mods_decoding > 0) { /* other mods decoding: use the same settings they are. */ memcpy(&sample->actual, ¤t_audioinfo, sizeof (Sound_AudioInfo)); } /* if */ else { /* no other mods decoding: define the new ModPlug output settings. */ memcpy(&sample->actual, &sample->desired, sizeof (Sound_AudioInfo)); if (sample->actual.rate == 0) sample->actual.rate = 44100; if (sample->actual.channels == 0) sample->actual.channels = 2; if (sample->actual.format == 0) sample->actual.format = AUDIO_S16SYS; memcpy(¤t_audioinfo, &sample->actual, sizeof (Sound_AudioInfo)); settings.mChannels=sample->actual.channels; settings.mFrequency=sample->actual.rate; settings.mBits = sample->actual.format & 0xFF; ModPlug_SetSettings(&settings); } /* else */ /* * The buffer may be a bit too large, but that doesn't matter. I think * it's safe to free it as soon as ModPlug_Load() is finished anyway. */ module = ModPlug_Load((void *) data, size); free(data); if (module == NULL) { if (modplug_mutex != NULL) SDL_UnlockMutex(modplug_mutex); BAIL_MACRO("MODPLUG: Not a module file.", 0); } /* if */ total_mods_decoding++; if (modplug_mutex != NULL) SDL_UnlockMutex(modplug_mutex); SNDDBG(("MODPLUG: [%d ms] %s\n", ModPlug_GetLength(module), ModPlug_GetName(module))); internal->decoder_private = (void *) module; sample->flags = SOUND_SAMPLEFLAG_CANSEEK; SNDDBG(("MODPLUG: Accepting data stream\n")); return(1); /* we'll handle this data. */ } /* MODPLUG_open */
static int OGG_open(Sound_Sample *sample, const char *ext) { int rc; double total_time; OggVorbis_File *vf; vorbis_info *info; Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; vf = (OggVorbis_File *) malloc(sizeof (OggVorbis_File)); BAIL_IF_MACRO(vf == NULL, ERR_OUT_OF_MEMORY, 0); rc = ov_open_callbacks(internal->rw, vf, NULL, 0, RWops_ogg_callbacks); if (rc != 0) { #if (defined DEBUG_CHATTER) SNDDBG(("OGG: can't grok data. reason: [%s].\n", ogg_error(rc))); #endif free(vf); BAIL_MACRO("OGG: Not valid Ogg Vorbis data.", 0); } /* if */ info = ov_info(vf, -1); if (info == NULL) { ov_clear(vf); free(vf); BAIL_MACRO("OGG: failed to retrieve bitstream info", 0); } /* if */ output_ogg_comments(vf); SNDDBG(("OGG: bitstream version == (%d).\n", info->version)); SNDDBG(("OGG: bitstream channels == (%d).\n", info->channels)); SNDDBG(("OGG: bitstream sampling rate == (%ld).\n", info->rate)); SNDDBG(("OGG: seekable == {%s}.\n", ov_seekable(vf) ? "TRUE" : "FALSE")); SNDDBG(("OGG: number of logical bitstreams == (%ld).\n", ov_streams(vf))); SNDDBG(("OGG: serial number == (%ld).\n", ov_serialnumber(vf, -1))); SNDDBG(("OGG: total seconds of sample == (%f).\n", ov_time_total(vf, -1))); internal->decoder_private = vf; sample->flags = SOUND_SAMPLEFLAG_CANSEEK; sample->actual.rate = (uint32_t) info->rate; sample->actual.channels = (uint8_t) info->channels; total_time = ov_time_total(vf, -1); if (OV_EINVAL == total_time) internal->total_time = -1; else internal->total_time = (int32_t)(total_time); /* * Since we might have more than one logical bitstream in the OGG file, * and these bitstreams may be in different formats, we might be * converting two or three times: once in vorbisfile, once again in * SDL_sound, and perhaps a third time to get it to the sound device's * format. That's wickedly inefficient. * * To combat this a little, if the user specified a desired format, we * claim that to be the "actual" format of the collection of logical * bitstreams. This means that VorbisFile will do a conversion as * necessary, and SDL_sound will not. If the user didn't specify a * desired format, then we pretend the "actual" format is something that * OGG files are apparently commonly encoded in. */ sample->actual.format = (sample->desired.format == 0) ? AUDIO_S16SYS : sample->desired.format; return(1); } /* OGG_open */