void SoundSource::Mix(int* dest, unsigned samples, int mixRate, bool stereo, bool interpolation)
{
    if (!position_ || (!sound_ && !soundStream_) || !IsEnabledEffective())
        return;

    int streamFilledSize, outBytes;

    if (soundStream_ && streamBuffer_)
    {
        int streamBufferSize = streamBuffer_->GetDataSize();
        // Calculate how many bytes of stream sound data is needed
        int neededSize = (int)((float)samples * frequency_ / (float)mixRate);
        // Add a little safety buffer. Subtract previous unused data
        neededSize += STREAM_SAFETY_SAMPLES;
        neededSize *= soundStream_->GetSampleSize();
        neededSize -= unusedStreamSize_;
        neededSize = Clamp(neededSize, 0, streamBufferSize - unusedStreamSize_);

        // Always start play position at the beginning of the stream buffer
        position_ = streamBuffer_->GetStart();

        // Request new data from the stream
        signed char* destination = streamBuffer_->GetStart() + unusedStreamSize_;
        outBytes = neededSize ? soundStream_->GetData(destination, (unsigned)neededSize) : 0;
        destination += outBytes;
        // Zero-fill rest if stream did not produce enough data
        if (outBytes < neededSize)
            memset(destination, 0, (size_t)(neededSize - outBytes));

        // Calculate amount of total bytes of data in stream buffer now, to know how much went unused after mixing
        streamFilledSize = neededSize + unusedStreamSize_;
    }

    // If streaming, play the stream buffer. Otherwise play the original sound
    Sound* sound = soundStream_ ? streamBuffer_ : sound_;
    if (!sound)
        return;

    // Choose the correct mixing routine
    if (!sound->IsStereo())
    {
        if (interpolation)
        {
            if (stereo)
                MixMonoToStereoIP(sound, dest, samples, mixRate);
            else
                MixMonoToMonoIP(sound, dest, samples, mixRate);
        }
        else
        {
            if (stereo)
                MixMonoToStereo(sound, dest, samples, mixRate);
            else
                MixMonoToMono(sound, dest, samples, mixRate);
        }
    }
    else
    {
        if (interpolation)
        {
            if (stereo)
                MixStereoToStereoIP(sound, dest, samples, mixRate);
            else
                MixStereoToMonoIP(sound, dest, samples, mixRate);
        }
        else
        {
            if (stereo)
                MixStereoToStereo(sound, dest, samples, mixRate);
            else
                MixStereoToMono(sound, dest, samples, mixRate);
        }
    }

    // Update the time position. In stream mode, copy unused data back to the beginning of the stream buffer
    if (soundStream_)
    {
        timePosition_ += ((float)samples / (float)mixRate) * frequency_ / soundStream_->GetFrequency();

        unusedStreamSize_ = Max(streamFilledSize - (int)(size_t)(position_ - streamBuffer_->GetStart()), 0);
        if (unusedStreamSize_)
            memcpy(streamBuffer_->GetStart(), (const void*)position_, (size_t)unusedStreamSize_);

        // If stream did not produce any data, stop if applicable
        if (!outBytes && soundStream_->GetStopAtEnd())
        {
            position_ = 0;
            return;
        }
    }
    else if (sound_)
        timePosition_ = ((float)(int)(size_t)(position_ - sound_->GetStart())) / (sound_->GetSampleSize() * sound_->GetFrequency());
}
예제 #2
0
void SoundSource::Mix(int* dest, unsigned samples, int mixRate, bool stereo, bool interpolation)
{
    if (!position_ || !sound_)
        return;
    
    if (sound_->IsCompressed())
    {
        if (decoder_)
        {
            // If Decoder already exists, Decode new compressed audio
            bool eof = false;
            unsigned currentPos = position_ - decodeBuffer_->GetStart();
            if (currentPos != decodePosition_)
            {
                // If buffer has wrapped, Decode first to the end
                if (currentPos < decodePosition_)
                {
                    unsigned bytes = decodeBuffer_->GetDataSize() - decodePosition_;
                    unsigned outBytes = sound_->Decode(decoder_, decodeBuffer_->GetStart() + decodePosition_, bytes);
                    // If produced less output, end of sound encountered. Fill rest with zero
                    if (outBytes < bytes)
                    {
                        memset(decodeBuffer_->GetStart() + decodePosition_ + outBytes, 0, bytes - outBytes);
                        eof = true;
                    }
                    decodePosition_ = 0;
                }
                if (currentPos > decodePosition_)
                {
                    unsigned bytes = currentPos - decodePosition_;
                    unsigned outBytes = sound_->Decode(decoder_, decodeBuffer_->GetStart() + decodePosition_, bytes);
                    // If produced less output, end of sound encountered. Fill rest with zero
                    if (outBytes < bytes)
                    {
                        memset(decodeBuffer_->GetStart() + decodePosition_ + outBytes, 0, bytes - outBytes);
                        if (sound_->IsLooped())
                            eof = true;
                    }
                    
                    // If wrote to buffer start, correct interpolation wraparound
                    if (!decodePosition_)
                        decodeBuffer_->FixInterpolation();
                }
            }
            
            // If end of stream encountered, check whether we should rewind or stop
            if (eof)
            {
                if (sound_->IsLooped())
                {
                    sound_->RewindDecoder(decoder_);
                    timePosition_ = 0.0f;
                }
                else
                    decodeBuffer_->SetLooped(false); // Stop after the current decode buffer has been played
            }
            
            decodePosition_ = currentPos;
        }
        else
        {
            // Setup the decoder and decode buffer
            decoder_ = sound_->AllocateDecoder();
            unsigned sampleSize = sound_->GetSampleSize();
            unsigned DecodeBufferSize = sampleSize * sound_->GetIntFrequency() * DECODE_BUFFER_LENGTH / 1000;
            decodeBuffer_ = new Sound(context_);
            decodeBuffer_->SetSize(DecodeBufferSize);
            decodeBuffer_->SetFormat(sound_->GetIntFrequency(), true, sound_->IsStereo());
            
            // Clear the decode buffer, then fill with initial audio data and set it to loop
            memset(decodeBuffer_->GetStart(), 0, DecodeBufferSize);
            sound_->Decode(decoder_, decodeBuffer_->GetStart(), DecodeBufferSize);
            decodeBuffer_->SetLooped(true);
            decodePosition_ = 0;
            
            // Start playing the decode buffer
            position_ = decodeBuffer_->GetStart();
            fractPosition_ = 0;
        }
    }
    
    // If compressed, play the decode buffer. Otherwise play the original sound
    Sound* sound = sound_->IsCompressed() ? decodeBuffer_ : sound_;
    if (!sound)
        return;
    
    // Choose the correct mixing routine
    if (!sound->IsStereo())
    {
        if (interpolation)
        {
            if (stereo)
                MixMonoToStereoIP(sound, dest, samples, mixRate);
            else
                MixMonoToMonoIP(sound, dest, samples, mixRate);
        }
        else
        {
            if (stereo)
                MixMonoToStereo(sound, dest, samples, mixRate);
            else
                MixMonoToMono(sound, dest, samples, mixRate);
        }
    }
    else
    {
        if (interpolation)
        {
            if (stereo)
                MixStereoToStereoIP(sound, dest, samples, mixRate);
            else
                MixStereoToMonoIP(sound, dest, samples, mixRate);
        }
        else
        {
            if (stereo)
                MixStereoToStereo(sound, dest, samples, mixRate);
            else
                MixStereoToMono(sound, dest, samples, mixRate);
        }
    }
    
    // Update the time position
    if (!sound_->IsCompressed())
        timePosition_ = (float)((int)position_ - (int)sound_->GetStart()) / (sound_->GetSampleSize() * sound_->GetFrequency());
    else
        timePosition_ += ((float)samples / (float)mixRate) * frequency_ / sound_->GetFrequency();
}