Esempio n. 1
0
void kwlMixBus_render(kwlMixBus* mixBus, 
                      void* mixerVoid, //TODO: made this a void* to get things to compile. should be kwlMixer*
                      int numOutChannels,
                      int numFrames, 
                      float* busScratchBuffer,
                      float* eventScratchBuffer,
                      float* outBuffer,
                      float accumulatedPitch,
                      float accumulatedGainLeft,
                      float accumulatedGainRight)
{
    kwlMixer* mixer = (kwlMixer*)mixerVoid;
    const int numSubBuses = mixBus->numSubBuses;
    
    /* Render sub buses recursively. */
    for (int i = 0; i < numSubBuses; i++)
    {
        kwlMixBus* busi = mixBus->subBuses[i];
        kwlMixBus_render(busi, 
                         mixer,
                         numOutChannels, 
                         numFrames,
                         busScratchBuffer,
                         eventScratchBuffer,
                         outBuffer,
                         busi->totalPitch.valueMixer * accumulatedPitch,
                         busi->totalGainLeft.valueMixer * accumulatedGainLeft,
                         busi->totalGainRight.valueMixer *accumulatedGainRight);
    }
    
    /* Mix the events of this bus into the out buffer. */
    kwlClearFloatBuffer(busScratchBuffer, numOutChannels * numFrames);
    kwlEventInstance* event = mixBus->eventList;
    int numEventsInBus = 0;    
    
    while (event != NULL)
    {
        int eventFinishedPlaying = kwlEventInstance_render(event, 
                                                   eventScratchBuffer, 
                                                   numOutChannels,
                                                   numFrames,
                                                   accumulatedPitch);
                
        /*mix event temp buffer into mixbus temp buffer*/
        kwlMixFloatBuffer(eventScratchBuffer, 
                          busScratchBuffer,
                          numOutChannels * numFrames);
        
        numEventsInBus++;
            
        /*Get the next event in the linked list and
          rearrange the list if the current event
          just stopped playing*/
        if (eventFinishedPlaying)
        {
            kwlMixer_sendEventStoppedMessage(mixer, event);
            /* Risky business: we're manipulating the linked list we're iterating over.
               Cache the next event in the bus because nextEvent_mixer gets reset
               when removing the event from the bus.
             */
            kwlEventInstance* nextEvent = event->nextEvent_mixer;
            kwlMixBus_removeEvent(mixBus, event);
            event = nextEvent;
        }
        else
        {
            event = event->nextEvent_mixer;
        }
    }
    
    /*Feed the bus output through the DSP unit if any.*/
    if (mixBus->dspUnit.valueMixer)
    {
        kwlDSPUnit* dspUnit = (kwlDSPUnit*)mixBus->dspUnit.valueMixer;
        /*process and replace mixbus temp buffer*/
        (*dspUnit->dspCallback)(busScratchBuffer,
                                numOutChannels,
                                numFrames, 
                                dspUnit->data);
    }

    /*if we have mixed any events for this bus, 
      mix the result into the output buffer*/
    if (numEventsInBus > 0)
    {
        /*... and then mix the bus buffer into the out buffer, applying
          the mix bus gain.*/
        for (int ch = 0; ch < numOutChannels; ch++)
        {
            kwlMixFloatBufferWithGain(busScratchBuffer, 
                                      outBuffer, 
                                      numOutChannels * numFrames, 
                                      ch, 
                                      numOutChannels, 
                                      //TODO: propagate gain values
                                      ch == 0 ? accumulatedGainLeft :
                                                accumulatedGainRight);
        }
    }
}
int kwlEventInstance_render(kwlEventInstance* event, 
                    float* outBuffer,
                    const int numOutChannels,
                    const int numFrames,
                    const float accumulatedBusPitch)
{
    /* initial playback logic checks */
    {
        if (event->playbackState == KWL_STOP_AND_UNLOAD_REQUESTED)
        {
            return 1;
        }
        else if (event->playbackState == KWL_STOP_REQUESTED)
        {
            /*if the event has been requested to stop and if the
              sound (if any) permits stopping mid-buffer, return 1 to indicate
              that the event should be removed from the mixer.*/
            int allowsImmediateStop = 
                event->definition_mixer->sound != NULL ? 
                event->definition_mixer->sound->deferStop == 0 : 1;
            if (allowsImmediateStop != 0)
            {
                return 1;
            }
        }
        else if (event->playbackState == KWL_PLAY_LAST_BUFFER_AND_STOP_REQUESTED)
        {
            int allowsImmediateStop = 
                event->definition_mixer->sound != NULL ? 
                event->definition_mixer->sound->deferStop == 0 : 1;
            if (allowsImmediateStop != 0)
            {
                kwlSound_pickNextBufferForEvent(event->definition_mixer->sound, 
                                                event, 0);
            }
        }        
        else if (event->isPaused != 0)
        {
            kwlClearFloatBuffer(outBuffer, numFrames * numOutChannels);
            return 0;
        }
        else if (event->pitch.valueMixer < PITCH_EPSILON)
        {
            /*Don't allow too low pitch values.*/
            event->pitch.valueMixer = PITCH_EPSILON;
        }
    }
    
    /*Update fade progress*/
    {
        event->fadeGain += event->fadeGainIncrPerFrame * numFrames;
        if (event->fadeGain > 1.0f)
        {
            event->fadeGain = 1.0f;
        }
        else if (event->fadeGain < 0.0f)
        {
            event->fadeGain = 0.0f;
            /** The fade out just finished, signal that the event should be stopped.*/
            return 1;
        }
    }
    
    /*gets set to a non-zero value when the out buffer has been completely filled*/
    int endOfOutBufferReached = 0;
    /*the index of the current frame in the out buffer*/
    int outFrameIdx = 0;
    /*gets set to a non-zero value when the end of the current source buffer is reached*/
    int endOfSourceBufferReached = 0;
    
    /* 
       During this loop, the output buffer is filled with samples from 
       either a sound or a decoder.     
     */
    int donePlaying = 0;
    while (!endOfOutBufferReached)
    {
        /*if the event pitch is close enough to 1, pitch shifting is not applied.*/
        float effectivePitch = event->pitch.valueMixer * event->soundPitch * accumulatedBusPitch;
        if (effectivePitch < PITCH_EPSILON)
        {
            effectivePitch = PITCH_EPSILON;
        }
        
        int unitPitch = isUnitPitch(effectivePitch);
        
        /*Check if we have enough source frames to fill the output buffer. */
        int numOutFramesLeft = kwlEventInstance_getNumRemainingOutFrames(event, effectivePitch);
        int maxOutFrameIdx = numFrames;
        if (numOutFramesLeft < numFrames - outFrameIdx) 
        {
            maxOutFrameIdx = outFrameIdx + numOutFramesLeft + (unitPitch == 0 ? 1 : 0);/*TODO: ugly*/
            endOfSourceBufferReached = 1;
        }
        
        int outSampleIdx = 0;
        int srcSampleIdx = 0;
        float pitchAccumulator = 0;
        
        const float soundGain = event->definition_mixer->sound != NULL ? 
                                event->definition_mixer->sound->gain : 1.0f;
        
        /*This loop is where the actual mixing takes place.*/
        //printf("about to mix event buffer, event->currentPCMFrameIndex %d, ep %f\n", event->currentPCMFrameIndex, effectivePitch);
        int ch;
        for (ch = 0; ch < numOutChannels; ch++)
        { 
            outSampleIdx = outFrameIdx * numOutChannels + ch;
            const int maxOutSampleIdx = maxOutFrameIdx * numOutChannels + ch;
            srcSampleIdx = event->currentPCMFrameIndex * event->currentNumChannels + ch;
            pitchAccumulator = event->pitchAccumulator;
            
            if (unitPitch)
            {
                /*a simplified mix loop without pitch shifting*/
                kwlInt16ToFloatWithGain(event->currentPCMBuffer, 
                                        outBuffer,
                                        maxOutSampleIdx,                    
                                        &srcSampleIdx,
                                        event->currentNumChannels,
                                        &outSampleIdx, 
                                        numOutChannels, 
                                        soundGain);
                KWL_ASSERT(srcSampleIdx >= 0);
            }
            else
            {
                kwlInt16ToFloatWithGainAndPitch(event->currentPCMBuffer, 
                                                outBuffer,
                                                maxOutSampleIdx,                    
                                                &srcSampleIdx,
                                                event->currentNumChannels,
                                                &outSampleIdx, 
                                                numOutChannels, 
                                                soundGain,
                                                effectivePitch,
                                                &pitchAccumulator);
            }
            
            /*There are 4 possible combinations of input and output channel counts to consider:*/

            /*1. mono in, stereo out: copy left out to right out and break the loop after the first of two channels*/
            if (event->currentNumChannels == 1 && numOutChannels == 2)
            {
                int i;
                for (i = outFrameIdx * numOutChannels + ch; i < maxOutSampleIdx;)
                {
                    outBuffer[i + 1] = outBuffer[i];
                    i += 2;
                }
                break;
            }
                        
            /*2. stereo in, mono out*/
            /*If the input is stereo, its right channel gets ignored.*/
            
            /*3. mono in, mono out*/
            /*Requires no special handling.*/
            
            /*4. stereo in, stereo out*/
            /*Requires no special handling.*/
        }
        
        KWL_ASSERT(srcSampleIdx >= 0);
        outFrameIdx = outSampleIdx / numOutChannels;
        event->pitchAccumulator = pitchAccumulator;
        event->currentPCMFrameIndex = srcSampleIdx / event->currentNumChannels;
        
        /* Perform playback logic checks if the end of the current source buffer was reached.*/ 
        if (endOfSourceBufferReached != 0)
        {
            event->numBuffersPlayed++;
            donePlaying = 0;
            if (event->definition_mixer == NULL && event->decoder == NULL)
            {
                /*this is a PCM event created in code. we're done playing.*/
                donePlaying = 1;
            }
            else if (event->decoder != NULL)
            {
                /*decode the next buffer*/
                donePlaying = kwlDecoder_decodeNewBufferForEvent(event->decoder, event);
            }
            else
            {
                /*get another pcm buffer from the event's sound*/
                donePlaying = kwlSound_pickNextBufferForEvent(event->definition_mixer->sound, event, 0);
            }
            
            if (donePlaying != 0)
            {
                /*the event finished playing, fill the remainder of the out buffer with zeros*/
                kwlClearFloatBuffer(&outBuffer[outFrameIdx * numOutChannels], 
                                    (numFrames - outFrameIdx) * numOutChannels);
                
                break;
            }
            else
            {
                /*A new src buffer was just picked.*/
                endOfSourceBufferReached = 0;
            }
        }
        else
        {
            /* if we made it here the end of the out buffer must have been reached. */
            KWL_ASSERT(outFrameIdx == numFrames);
            endOfOutBufferReached = 1;
        }
    }
    
    /*Feed final event output through the event DSP unit, if any.*/
    kwlDSPUnit* dspUnit = (kwlDSPUnit*)event->dspUnit.valueMixer;
    if (dspUnit != NULL)
    {
        (*dspUnit->dspCallback)(outBuffer,
                                numOutChannels,
                                numFrames, 
                                dspUnit->data);
    }
    
    /* Apply per buffer gain with ramps if necessary*/
    {
        float effectiveGain[2] = 
        {
            event->fadeGain * event->gainLeft.valueMixer,
            event->fadeGain * event->gainRight.valueMixer
        };
        
        if (event->prevEffectiveGain[0] < 0.0f)
        {
            event->prevEffectiveGain[0] = effectiveGain[0];
            event->prevEffectiveGain[1] = effectiveGain[1];
        }
        
        kwlApplyGainRamp(outBuffer, 
                         numOutChannels, 
                         numFrames, 
                         event->prevEffectiveGain, 
                         effectiveGain);
        
        event->prevEffectiveGain[0] = effectiveGain[0];
        event->prevEffectiveGain[1] = effectiveGain[1];
    }
    
    return donePlaying;
}