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()); }
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(); }