kwlError kwlEventInstance_createFreeformEventFromFile(kwlEventInstance** event, const char* const audioFilePath, kwlEventType type, int streamFromDisk) { KWL_ASSERT(streamFromDisk == 0 && "stream flag not supported yet"); /*try to load the audio file data*/ kwlAudioData* audioData = (kwlAudioData*)KWL_MALLOC(sizeof(kwlAudioData), "freeform event audio data struct"); kwlMemset(audioData, 0, sizeof(kwlAudioData)); kwlError error = kwlLoadAudioFile(audioFilePath, audioData, KWL_CONVERT_TO_INT16_OR_FAIL); if (error != KWL_NO_ERROR) { KWL_FREE(audioData); return error; } if (type == KWL_POSITIONAL && audioData->numChannels != 1) { kwlAudioData_free(audioData); KWL_FREE(audioData); return KWL_POSITIONAL_EVENT_MUST_BE_MONO; } return kwlEventInstance_createFreeformEventFromAudioData(event, audioData, type, "freeform event"); }
kwlMixBus* kwlMixBus_alloc() { kwlMixBus* newMixBus = (kwlMixBus*)KWL_MALLOC(sizeof(kwlMixBus), "kwlMixBus_alloc"); kwlMemset(newMixBus, 0, sizeof(kwlMixBus)); kwlMixBus_init(newMixBus); return newMixBus; }
kwlError kwlEventInstance_createFreeformEventFromBuffer(kwlEventInstance** event, kwlPCMBuffer* buffer, kwlEventType type) { if (buffer->numFrames < 1 || buffer->numChannels < 1 || buffer->numChannels > 2 || buffer->pcmData == NULL) { return KWL_INVALID_PARAMETER_VALUE; } kwlAudioData* audioData = (kwlAudioData*)KWL_MALLOC(sizeof(kwlAudioData), "freeform event audio data struct"); kwlMemset(audioData, 0, sizeof(kwlAudioData)); audioData->numChannels = buffer->numChannels; audioData->numFrames = buffer->numFrames; audioData->numBytes = buffer->numFrames * buffer->numChannels * 2;/*2 bytes per 16 bit sample*/ audioData->bytes = buffer->pcmData; audioData->encoding = KWL_ENCODING_SIGNED_16BIT_PCM; /*The id "freeform buffer event" is used later to indicate that the sample data should not be released. This should really be handled in a better way.*/ return kwlEventInstance_createFreeformEventFromAudioData(event, audioData, type, "freeform buffer event"); }
kwlError kwlWaveBank_verifyWaveBankBinary(kwlEngine* engine, const char* const waveBankPath, kwlWaveBank** waveBank) { /*Open the file...*/ kwlInputStream stream; kwlError result = kwlInputStream_initWithFile(&stream, waveBankPath); if (result != KWL_NO_ERROR) { kwlInputStream_close(&stream); return result; } /*... and check the wave bank file identifier.*/ int i; for (i = 0; i < KWL_WAVE_BANK_BINARY_FILE_IDENTIFIER_LENGTH; i++) { const char identifierChari = kwlInputStream_readChar(&stream); if (identifierChari != KWL_WAVE_BANK_BINARY_FILE_IDENTIFIER[i]) { /* Not the file identifier we expected. */ kwlInputStream_close(&stream); return KWL_UNKNOWN_FILE_FORMAT; } } /*Read the ID from the wave bank binary file and find a matching wave bank struct.*/ const char* waveBankToLoadId = kwlInputStream_readASCIIString(&stream); const int waveBankToLoadnumAudioDataEntries = kwlInputStream_readIntBE(&stream); const int numWaveBanks = engine->engineData.numWaveBanks; kwlWaveBank* matchingWaveBank = NULL; for (i = 0; i < numWaveBanks; i++) { if (strcmp(waveBankToLoadId, engine->engineData.waveBanks[i].id) == 0) { matchingWaveBank = &engine->engineData.waveBanks[i]; } } KWL_FREE((void*)waveBankToLoadId); if (matchingWaveBank == NULL) { /*No matching bank was found. Close the file stream and return an error.*/ kwlInputStream_close(&stream); return KWL_NO_MATCHING_WAVE_BANK; } else if (waveBankToLoadnumAudioDataEntries != matchingWaveBank->numAudioDataEntries) { /*A matching wave bank was found but the number of audio data entries does not match the binary wave bank data.*/ kwlInputStream_close(&stream); return KWL_WAVE_BANK_ENTRY_MISMATCH; } else if (matchingWaveBank->isLoaded != 0) { /*The wave bank is already loaded, just set the handle and do nothing.*/ *waveBank = matchingWaveBank; kwlInputStream_close(&stream); return KWL_NO_ERROR; } /*Store the path the wave bank was loaded from (used when streaming from disk).*/ const int pathLen = strlen(waveBankPath); matchingWaveBank->waveBankFilePath = (char*)KWL_MALLOC((pathLen + 1) * sizeof(char), "wave bank path string"); strcpy(matchingWaveBank->waveBankFilePath, waveBankPath); /*Make sure that the entries of the wave bank to load and the wave bank struct line up.*/ for (i = 0; i < waveBankToLoadnumAudioDataEntries; i++) { const char* filePathi = kwlInputStream_readASCIIString(&stream); int matchingEntryIndex = -1; int j = 0; for (j = 0; j < waveBankToLoadnumAudioDataEntries; j++) { if (strcmp(matchingWaveBank->audioDataItems[j].filePath, filePathi) == 0) { matchingEntryIndex = j; break; } } KWL_FREE((void*)filePathi); if (matchingEntryIndex < 0) { /* This wave bank entry has no corresponding waveform slot. Abort loading.*/ kwlInputStream_close(&stream); return KWL_WAVE_BANK_ENTRY_MISMATCH; } /*skip to the next wave data entry*/ /*const int encoding = */kwlInputStream_readIntBE(&stream); /*const int streamFromDisk = */kwlInputStream_readIntBE(&stream); const int numChannels = kwlInputStream_readIntBE(&stream); KWL_ASSERT((numChannels == 0 || numChannels == 1 || numChannels == 2) && "invalid num channels"); const int numBytes = kwlInputStream_readIntBE(&stream); KWL_ASSERT(numBytes > 0); kwlInputStream_skip(&stream, numBytes); } /* Reading went well. */ kwlInputStream_close(&stream); *waveBank = matchingWaveBank; return KWL_NO_ERROR; }
kwlError kwlWaveBank_loadAudioDataItems(kwlWaveBank* waveBank, kwlInputStream* stream) { /*The input stream is assumed to be valid, so move the read position to the first audio data entry.*/ kwlInputStream_reset(stream); kwlInputStream_skip(stream, KWL_WAVE_BANK_BINARY_FILE_IDENTIFIER_LENGTH); const int strLen = kwlInputStream_readIntBE(stream); kwlInputStream_skip(stream, strLen); /*int numEntries = */kwlInputStream_readIntBE(stream); const int waveBankToLoadnumAudioDataEntries = waveBank->numAudioDataEntries; for (int i = 0; i < waveBankToLoadnumAudioDataEntries; i++) { char* const waveEntryIdi = kwlInputStream_readASCIIString(stream); kwlAudioData* matchingAudioData = NULL; int j; for (int j = 0; j < waveBankToLoadnumAudioDataEntries; j++) { kwlAudioData* entryj = &waveBank->audioDataItems[j]; if (strcmp(entryj->filePath, waveEntryIdi) == 0) { matchingAudioData = entryj; break; } } //printf(" loading %s\n", waveEntryIdi); KWL_FREE(waveEntryIdi); const kwlAudioEncoding encoding = (kwlAudioEncoding)kwlInputStream_readIntBE(stream); const int streamFromDisk = kwlInputStream_readIntBE(stream); const int numChannels = kwlInputStream_readIntBE(stream); const int numBytes = kwlInputStream_readIntBE(stream); const int numFrames = numBytes / 2 * numChannels; /* Check that the audio meta data makes sense */ if (numBytes <= 0) { KWL_ASSERT(0 && "the number of audio data bytes must be positive"); return KWL_CORRUPT_BINARY_DATA; } if (matchingAudioData == NULL) { KWL_ASSERT(0 && "no matching wave bank entry"); return KWL_CORRUPT_BINARY_DATA; } if (numChannels != 0 && numChannels != 1 && numChannels != 2) { KWL_ASSERT(0 && "invalid number of channels"); return KWL_CORRUPT_BINARY_DATA; } /*free any old data*/ kwlAudioData_free(matchingAudioData); /*Store audio meta data.*/ matchingAudioData->numFrames = numFrames; matchingAudioData->numChannels = numChannels; matchingAudioData->numBytes = numBytes; matchingAudioData->encoding = (kwlAudioEncoding)encoding; matchingAudioData->streamFromDisk = streamFromDisk; matchingAudioData->isLoaded = 1; matchingAudioData->bytes = NULL; if (streamFromDisk == 0) { /*This entry should not be streamed, so allocate audio data up front.*/ matchingAudioData->bytes = KWL_MALLOC(numBytes, "kwlEngine_loadWaveBank"); int bytesRead = kwlInputStream_read(stream, (signed char*)matchingAudioData->bytes, numBytes); if (bytesRead != numBytes) { KWL_ASSERT(0 && "error reading wave bank audio data bytes"); return KWL_CORRUPT_BINARY_DATA; } } else { /*Store the offset into the wave bank binary files for streaming entries.*/ matchingAudioData->fileOffset = kwlInputStream_tell(stream); kwlInputStream_skip(stream, numBytes); } } waveBank->isLoaded = 1; return KWL_NO_ERROR; }
kwlError kwlEventInstance_createFreeformEventFromAudioData(kwlEventInstance** event, kwlAudioData* audioData, kwlEventType type, const char* eventId) { /*create the event. as opposed to a data driven event, a freeform event does not reference sounds and event definitions in the engine, but own its local data that is freed when the event is released.*/ kwlEventInstance* createdEvent = (kwlEventInstance*)KWL_MALLOC(sizeof(kwlEventInstance), "freeform event instance"); kwlEventInstance_init(createdEvent); kwlSound* sound = NULL; kwlAudioData* streamAudioData = NULL; /*create a sound if we loaded a PCM file.*/ if (audioData->encoding == KWL_ENCODING_SIGNED_16BIT_PCM) { sound = (kwlSound*)KWL_MALLOC(sizeof(kwlSound), "freeform event: sound"); kwlSound_init(sound); sound->audioDataEntries = (kwlAudioData**)KWL_MALLOC(sizeof(kwlAudioData*), "freeform event: sound audio data array list"); sound->audioDataEntries[0] = audioData; sound->numAudioDataEntries = 1; sound->playbackMode = KWL_SEQUENTIAL; sound->playbackCount = 1; sound->deferStop = 0; sound->gain = 1.0f; sound->pitch = 1.0f; sound->pitchVariation = 0.0f; sound->gainVariation = 0.0f; } else { KWL_ASSERT(0 && "TODO: support creating non-pcm events"); } /*create an event definition*/ kwlEventDefinition* eventDefinition = (kwlEventDefinition*)KWL_MALLOC(sizeof(kwlEventDefinition), "freeform event definition"); kwlEventDefinition_init(eventDefinition); eventDefinition->id = eventId; eventDefinition->instanceCount = 1; eventDefinition->isPositional = type == KWL_POSITIONAL ? 1 : 0; eventDefinition->gain = 1.0f; eventDefinition->pitch = 1.0f; eventDefinition->innerConeCosAngle = 1.0f; eventDefinition->outerConeCosAngle = -1.0f; eventDefinition->outerConeGain = 1.0f; eventDefinition->retriggerMode = KWL_RETRIGGER; eventDefinition->stealingMode = KWL_DONT_STEAL; eventDefinition->streamAudioData = streamAudioData; eventDefinition->sound = sound; eventDefinition->numReferencedWaveBanks = 0; eventDefinition->referencedWaveBanks = NULL; /*Set the mix bus to NULL. This is how the mixer knows this is a freeform event. TODO: solve this in some better way?*/ eventDefinition->mixBus = NULL; createdEvent->definition_mixer = eventDefinition; createdEvent->definition_engine = eventDefinition; *event = createdEvent; return KWL_NO_ERROR; }
kwlError kwlDecoder_init(kwlDecoder* decoder, kwlEventInstance* event) { kwlAudioData* audioData = event->definition_engine->streamAudioData; /*reset the decoder struct.*/ kwlMemset(decoder, 0, sizeof(kwlDecoder)); decoder->loop = event->definition_engine->loopIfStreaming; /* * Hook up audio data, that could either be from a file or from an already loaded buffer */ if (audioData->streamFromDisk != 0) { KWL_ASSERT(audioData->fileOffset >= 0); kwlError result = kwlInputStream_initWithFileRegion(&decoder->audioDataStream, audioData->waveBank->waveBankFilePath, audioData->fileOffset, audioData->numBytes); KWL_ASSERT(result == KWL_NO_ERROR); } else { kwlInputStream_initWithBuffer(&decoder->audioDataStream, audioData->bytes, 0, audioData->numBytes); } /* * do codec specific initialization. */ kwlError result = KWL_UNSUPPORTED_ENCODING; if (audioData->encoding == KWL_ENCODING_IMA_ADPCM) { result = kwlInitDecoderIMAADPCM(decoder); } else if (audioData->encoding == KWL_ENCODING_VORBIS) { result = kwlInitDecoderOggVorbis(decoder); } else if (kwlAudioData_isLinearPCM(audioData)) { result = kwlInitDecoderPCM(decoder); } #ifdef KWL_IPHONE else if (audioData->encoding == KWL_ENCODING_UNKNOWN) { /*try the iphone decoder*/ result = kwlInitDecoderIPhone(decoder); } #endif /*KWL_IPHONE*/ if (result != KWL_NO_ERROR) { decoder->deinit(decoder); return result; } KWL_ASSERT(decoder->numChannels > 0); decoder->currentDecodedBuffer = (short*)KWL_MALLOC(sizeof(short) * decoder->maxDecodedBufferSize, "decoder back buffer"); decoder->currentDecodedBufferFront = (short*)KWL_MALLOC(sizeof(short) * decoder->maxDecodedBufferSize, "decoder front buffer"); decoder->currentDecodedBufferSizeInBytes = 0; /* * Before starting the decoding thread, call the decode function * synchronously to get the first buffer of decoded samples. */ int decodingResult = decoder->decodeBuffer(decoder); kwlDecoder_swapBuffers(decoder); /*TODO: check the decoding result. the event could be done playing here.*/ event->currentPCMFrameIndex = 0; event->currentPCMBuffer = decoder->currentDecodedBufferFront; event->currentPCMBufferSize = decoder->currentDecodedBufferSizeInBytes / (2 * decoder->numChannels); event->currentNumChannels = decoder->numChannels; /*Create a semaphore with a unique name based on the addess of the decoder*/ sprintf(decoder->semaphoreName, "decoder%d", (int)decoder); decoder->semaphore = kwlSemaphoreOpen(decoder->semaphoreName); kwlSemaphorePost(decoder->semaphore); /*Fire up the decoding thread.*/ kwlThreadCreate(&decoder->decodingThread, kwlDecoder_decodingLoop, decoder); return result; }