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");
}
Exemple #2
0
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;
}
Exemple #7
0
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;
}