/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ * Copyright (c) 2009-2016, The OpenClonk Team and contributors * * Distributed under the terms of the ISC license; see accompanying file * "COPYING" for details. * * "Clonk" is a registered trademark of Matthes Bender, used with permission. * See accompanying file "TRADEMARK" for details. * * To redistribute this file separately, substitute the full license texts * for the above references. */ #include "C4Include.h" #include "platform/C4SoundLoaders.h" #include "game/C4Application.h" #include "platform/C4MusicFile.h" using namespace C4SoundLoaders; SoundLoader* SoundLoader::first_loader(nullptr); #if AUDIO_TK == AUDIO_TK_OPENAL && defined(__APPLE__) namespace { static OSStatus AudioToolBoxReadCallback(void* context, SInt64 inPosition, UInt32 requestCount, void* buffer, UInt32* actualCount) { CFDataRef audio = (CFDataRef)context; CFIndex audio_length = CFDataGetLength(audio); int requestOffset = inPosition + requestCount; int availableBytes = audio_length - inPosition; if (requestOffset > audio_length) { *actualCount = availableBytes; } else { *actualCount = requestCount; } CFRange range; range.location = inPosition; range.length = *actualCount; CFDataGetBytes(audio, range, (UInt8*)buffer); return noErr; } static SInt64 AudioToolBoxGetSizeProc(void* context) { return CFDataGetLength((CFDataRef)context); } } bool AppleSoundLoader::ReadInfo(SoundInfo* result, BYTE* data, size_t data_length, uint32_t) { CFDataRef data_container = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, data, data_length, kCFAllocatorNull); AudioFileID sound_file; OSStatus err = AudioFileOpenWithCallbacks((void*)data_container, AudioToolBoxReadCallback, nullptr, AudioToolBoxGetSizeProc, nullptr, 0, &sound_file ); if (err == noErr) { UInt32 property_size; uint64_t sound_data_size = 0; property_size = sizeof(sound_data_size); AudioFileGetProperty(sound_file, kAudioFilePropertyAudioDataByteCount, &property_size, &sound_data_size); result->sample_length = -1; property_size = sizeof(result->sample_length); AudioFileGetProperty(sound_file, kAudioFilePropertyEstimatedDuration, &property_size, &result->sample_length); UInt32 sound_data_size_32 = sound_data_size; result->sound_data.resize(sound_data_size); AudioFileReadBytes(sound_file, false, 0, &sound_data_size_32, &result->sound_data[0]); AudioStreamBasicDescription desc; property_size = sizeof(desc); AudioFileGetProperty(sound_file, kAudioFilePropertyDataFormat, &property_size, &desc); result->sample_rate = desc.mSampleRate; switch (desc.mChannelsPerFrame) { case 1: result->format = desc.mBitsPerChannel == 16 ? AL_FORMAT_MONO16 : AL_FORMAT_MONO8; break; case 2: result->format = desc.mBitsPerChannel == 16 ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO8; break; default: result->format = 0; } } CFRelease(data_container); return err == noErr; } AppleSoundLoader AppleSoundLoader::singleton; #endif #if AUDIO_TK == AUDIO_TK_OPENAL size_t VorbisLoader::mem_read_func(void* ptr, size_t byte_size, size_t size_to_read, void* datasource) { size_t spaceToEOF; size_t actualSizeToRead; CompressedData* data = (CompressedData*)datasource; spaceToEOF = data->data_length - data->data_pos; if (size_to_read*byte_size < spaceToEOF) actualSizeToRead = size_to_read*byte_size; else actualSizeToRead = spaceToEOF; if (actualSizeToRead) { memcpy(ptr, (char*)data->data + data->data_pos, actualSizeToRead); data->data_pos += actualSizeToRead; } return actualSizeToRead; } int VorbisLoader::mem_seek_func(void* datasource, ogg_int64_t offset, int whence) { CompressedData* data = (CompressedData*)datasource; switch (whence) { case SEEK_SET: data->data_pos = static_cast(offset) < data->data_length ? static_cast(offset) : data->data_length; break; case SEEK_CUR: data->data_pos += static_cast(offset) < data->data_length - data->data_pos ? static_cast(offset) : data->data_length - data->data_pos; break; case SEEK_END: data->data_pos = data->data_length+1; break; } return 0; } int VorbisLoader::mem_close_func(void* datasource) { return 1; } long VorbisLoader::mem_tell_func(void* datasource) { return ((CompressedData*)datasource)->data_pos; } size_t VorbisLoader::file_read_func(void* ptr, size_t byte_size, size_t size_to_read, void* datasource) { C4MusicFileOgg *ogg = static_cast(datasource); size_t bytes_to_read = size_to_read*byte_size; size_t bytes_read = 0u; ogg->source_file.Read(ptr, bytes_to_read, &bytes_read); return bytes_read / byte_size; } int VorbisLoader::file_seek_func(void* datasource, ogg_int64_t offset, int whence) { C4MusicFileOgg *ogg = static_cast(datasource); return ogg->source_file.Seek(static_cast(offset), whence); } int VorbisLoader::file_close_func(void* datasource) { C4MusicFileOgg *ogg = static_cast(datasource); ogg->source_file.Close(); return 1; } long VorbisLoader::file_tell_func(void* datasource) { C4MusicFileOgg *ogg = static_cast(datasource); return ogg->source_file.Tell(); } bool VorbisLoader::ReadInfo(SoundInfo* result, BYTE* data, size_t data_length, uint32_t) { CompressedData compressed(data, data_length); int endian = 0; vorbis_info* info; OggVorbis_File ogg_file; memset(&ogg_file, 0, sizeof(ogg_file)); ov_callbacks callbacks; callbacks.read_func = &mem_read_func; callbacks.seek_func = &mem_seek_func; callbacks.close_func = &mem_close_func; callbacks.tell_func = &mem_tell_func; // open using callbacks if (ov_open_callbacks(&compressed, &ogg_file, nullptr, 0, callbacks) != 0) { ov_clear(&ogg_file); return false; } // get information about sound info = ov_info(&ogg_file, -1); if (info->channels == 1) result->format = AL_FORMAT_MONO16; else result->format = AL_FORMAT_STEREO16; result->sample_rate = info->rate; result->sample_length = ov_time_total(&ogg_file, -1)/1000.0; // Compute the total buffer size const unsigned long total_size = static_cast(result->sample_rate * result->sample_length * 1000.0 * info->channels * 2 + 0.5); const unsigned long extra_size = 1024 * 8; // read: try to read the whole track in one go, and if there is more data // than we predicted in total_size, read the additional data in 8K blocks. unsigned long buffer_size = 0; long bytes_read; do { const int chunk_size = total_size + extra_size - std::min(total_size, buffer_size); int bitStream; if (buffer_size+chunk_size > result->sound_data.size()) result->sound_data.resize(buffer_size+chunk_size); bytes_read = ov_read(&ogg_file, (char*)&result->sound_data[buffer_size], chunk_size*sizeof(BYTE), endian, 2, 1, &bitStream); buffer_size += bytes_read; } while (bytes_read > 0); result->sound_data.resize(buffer_size); // clear ogg file ov_clear(&ogg_file); return true; } VorbisLoader VorbisLoader::singleton; #ifndef __APPLE__ bool WavLoader::ReadInfo(SoundInfo* result, BYTE* data, size_t data_length, uint32_t) { // load WAV resource Application.MusicSystem.SelectContext(); ALuint wav = alutCreateBufferFromFileImage((const ALvoid *)data, data_length); if (wav == AL_NONE) { return false; } // get information about sound ALint freq, chans, bits, size; alGetBufferi(wav, AL_FREQUENCY, &freq); result->sample_rate = freq; alGetBufferi(wav, AL_CHANNELS, &chans); alGetBufferi(wav, AL_BITS, &bits); alGetBufferi(wav, AL_SIZE, &size); if (chans == 1) if (bits == 8) result->format = AL_FORMAT_MONO8; else result->format = AL_FORMAT_MONO16; else if (bits == 8) result->format = AL_FORMAT_STEREO8; else result->format = AL_FORMAT_STEREO16; // can't find any function to determine sample length // just calc ourselves result->sample_length = double(size) / double(bits*chans*freq/8); // buffer loaded result->final_handle = wav; return true; } WavLoader WavLoader::singleton; #endif #elif AUDIO_TK == AUDIO_TK_SDL_MIXER #define USE_RWOPS #include bool SDLMixerSoundLoader::ReadInfo(SoundInfo* result, BYTE* data, size_t data_length, uint32_t) { // Be paranoid about SDL_Mixer initialisation if (!Application.MusicSystem.IsMODInitialized()) { return false; } SDL_RWops * rwops = SDL_RWFromConstMem(data, data_length); // work around double free in SDL_Mixer by passing 0 here result->final_handle = Mix_LoadWAV_RW(rwops, 0); SDL_RWclose(rwops); if (!result->final_handle) { return false; } //FIXME: Is this actually correct? result->sample_length = result->final_handle->alen / (44100 * 2); result->sample_rate = 0; return true; } SDLMixerSoundLoader SDLMixerSoundLoader::singleton; #endif