| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287 |
- //
- // Copyright (c) 2008-2017 the Urho3D project.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- #include "../Precompiled.h"
- #include "../Audio/Audio.h"
- #include "../Audio/AudioEvents.h"
- #include "../Audio/Sound.h"
- #include "../Audio/SoundSource.h"
- #include "../Audio/SoundStream.h"
- #include "../Core/Context.h"
- #include "../IO/Log.h"
- #include "../Resource/ResourceCache.h"
- #include "../Scene/Node.h"
- #include "../Scene/ReplicationState.h"
- #include "../DebugNew.h"
- namespace Urho3D
- {
- #define INC_POS_LOOPED() \
- pos += intAdd; \
- fractPos += fractAdd; \
- if (fractPos > 65535) \
- { \
- fractPos &= 65535; \
- ++pos; \
- } \
- while (pos >= end) \
- pos -= (end - repeat); \
- #define INC_POS_ONESHOT() \
- pos += intAdd; \
- fractPos += fractAdd; \
- if (fractPos > 65535) \
- { \
- fractPos &= 65535; \
- ++pos; \
- } \
- if (pos >= end) \
- { \
- pos = 0; \
- break; \
- } \
- #define INC_POS_STEREO_LOOPED() \
- pos += (intAdd << 1); \
- fractPos += fractAdd; \
- if (fractPos > 65535) \
- { \
- fractPos &= 65535; \
- pos += 2; \
- } \
- while (pos >= end) \
- pos -= (end - repeat); \
- #define INC_POS_STEREO_ONESHOT() \
- pos += (intAdd << 1); \
- fractPos += fractAdd; \
- if (fractPos > 65535) \
- { \
- fractPos &= 65535; \
- pos += 2; \
- } \
- if (pos >= end) \
- { \
- pos = 0; \
- break; \
- } \
- #define GET_IP_SAMPLE() (((((int)pos[1] - (int)pos[0]) * fractPos) / 65536) + (int)pos[0])
- #define GET_IP_SAMPLE_LEFT() (((((int)pos[2] - (int)pos[0]) * fractPos) / 65536) + (int)pos[0])
- #define GET_IP_SAMPLE_RIGHT() (((((int)pos[3] - (int)pos[1]) * fractPos) / 65536) + (int)pos[1])
- static const int STREAM_SAFETY_SAMPLES = 4;
- extern const char* AUDIO_CATEGORY;
- extern const char* autoRemoveModeNames[];
- SoundSource::SoundSource(Context* context) :
- Component(context),
- soundType_(SOUND_EFFECT),
- frequency_(0.0f),
- gain_(1.0f),
- attenuation_(1.0f),
- panning_(0.0f),
- sendFinishedEvent_(false),
- autoRemove_(REMOVE_DISABLED),
- position_(nullptr),
- fractPosition_(0),
- timePosition_(0.0f),
- unusedStreamSize_(0)
- {
- audio_ = GetSubsystem<Audio>();
- if (audio_)
- audio_->AddSoundSource(this);
- UpdateMasterGain();
- }
- SoundSource::~SoundSource()
- {
- if (audio_)
- audio_->RemoveSoundSource(this);
- }
- void SoundSource::RegisterObject(Context* context)
- {
- context->RegisterFactory<SoundSource>(AUDIO_CATEGORY);
- URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
- URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Sound", GetSoundAttr, SetSoundAttr, ResourceRef, ResourceRef(Sound::GetTypeStatic()), AM_DEFAULT);
- URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Type", GetSoundType, SetSoundType, String, SOUND_EFFECT, AM_DEFAULT);
- URHO3D_ATTRIBUTE("Frequency", float, frequency_, 0.0f, AM_DEFAULT);
- URHO3D_ATTRIBUTE("Gain", float, gain_, 1.0f, AM_DEFAULT);
- URHO3D_ATTRIBUTE("Attenuation", float, attenuation_, 1.0f, AM_DEFAULT);
- URHO3D_ATTRIBUTE("Panning", float, panning_, 0.0f, AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("Is Playing", IsPlaying, SetPlayingAttr, bool, false, AM_DEFAULT);
- URHO3D_ENUM_ATTRIBUTE("Autoremove Mode", autoRemove_, autoRemoveModeNames, REMOVE_DISABLED, AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("Play Position", GetPositionAttr, SetPositionAttr, int, 0, AM_FILE);
- }
- void SoundSource::Seek(float seekTime)
- {
- // Ignore buffered sound stream
- if (!audio_ || !sound_ || (soundStream_ && !sound_->IsCompressed()))
- return;
- // Set to valid range
- seekTime = Clamp(seekTime, 0.0f, sound_->GetLength());
- if (!soundStream_)
- {
- // Raw or wav format
- SetPositionAttr((int)(seekTime * (sound_->GetSampleSize() * sound_->GetFrequency())));
- }
- else
- {
- // Ogg format
- if (soundStream_->Seek((unsigned)(seekTime * soundStream_->GetFrequency())))
- {
- timePosition_ = seekTime;
- }
- }
- }
- void SoundSource::Play(Sound* sound)
- {
- if (!audio_)
- return;
- // If no frequency set yet, set from the sound's default
- if (frequency_ == 0.0f && sound)
- SetFrequency(sound->GetFrequency());
- // If sound source is currently playing, have to lock the audio mutex
- if (position_)
- {
- MutexLock lock(audio_->GetMutex());
- PlayLockless(sound);
- }
- else
- PlayLockless(sound);
- // Forget the Sound & Is Playing attribute previous values so that they will be sent again, triggering
- // the sound correctly on network clients even after the initial playback
- if (networkState_ && networkState_->attributes_ && networkState_->previousValues_.Size())
- {
- for (unsigned i = 1; i < networkState_->previousValues_.Size(); ++i)
- {
- // The indexing is different for SoundSource & SoundSource3D, as SoundSource3D removes two attributes,
- // so go by attribute types
- VariantType type = networkState_->attributes_->At(i).type_;
- if (type == VAR_RESOURCEREF || type == VAR_BOOL)
- networkState_->previousValues_[i] = Variant::EMPTY;
- }
- }
- MarkNetworkUpdate();
- }
- void SoundSource::Play(Sound* sound, float frequency)
- {
- SetFrequency(frequency);
- Play(sound);
- }
- void SoundSource::Play(Sound* sound, float frequency, float gain)
- {
- SetFrequency(frequency);
- SetGain(gain);
- Play(sound);
- }
- void SoundSource::Play(Sound* sound, float frequency, float gain, float panning)
- {
- SetFrequency(frequency);
- SetGain(gain);
- SetPanning(panning);
- Play(sound);
- }
- void SoundSource::Play(SoundStream* stream)
- {
- if (!audio_)
- return;
- // If no frequency set yet, set from the stream's default
- if (frequency_ == 0.0f && stream)
- SetFrequency(stream->GetFrequency());
- SharedPtr<SoundStream> streamPtr(stream);
- // If sound source is currently playing, have to lock the audio mutex. When stream playback is explicitly
- // requested, clear the existing sound if any
- if (position_)
- {
- MutexLock lock(audio_->GetMutex());
- sound_.Reset();
- PlayLockless(streamPtr);
- }
- else
- {
- sound_.Reset();
- PlayLockless(streamPtr);
- }
- // Stream playback is not supported for network replication, no need to mark network dirty
- }
- void SoundSource::Stop()
- {
- if (!audio_)
- return;
- // If sound source is currently playing, have to lock the audio mutex
- if (position_)
- {
- MutexLock lock(audio_->GetMutex());
- StopLockless();
- }
- else
- StopLockless();
- MarkNetworkUpdate();
- }
- void SoundSource::SetSoundType(const String& type)
- {
- if (type == SOUND_MASTER)
- return;
- soundType_ = type;
- soundTypeHash_ = StringHash(type);
- UpdateMasterGain();
- MarkNetworkUpdate();
- }
- void SoundSource::SetFrequency(float frequency)
- {
- frequency_ = Clamp(frequency, 0.0f, 535232.0f);
- MarkNetworkUpdate();
- }
- void SoundSource::SetGain(float gain)
- {
- gain_ = Max(gain, 0.0f);
- MarkNetworkUpdate();
- }
- void SoundSource::SetAttenuation(float attenuation)
- {
- attenuation_ = Clamp(attenuation, 0.0f, 1.0f);
- MarkNetworkUpdate();
- }
- void SoundSource::SetPanning(float panning)
- {
- panning_ = Clamp(panning, -1.0f, 1.0f);
- MarkNetworkUpdate();
- }
- void SoundSource::SetAutoRemoveMode(AutoRemoveMode mode)
- {
- autoRemove_ = mode;
- MarkNetworkUpdate();
- }
- bool SoundSource::IsPlaying() const
- {
- return (sound_ || soundStream_) && position_ != nullptr;
- }
- void SoundSource::SetPlayPosition(signed char* pos)
- {
- // Setting play position on a stream is not supported
- if (!audio_ || !sound_ || soundStream_)
- return;
- MutexLock lock(audio_->GetMutex());
- SetPlayPositionLockless(pos);
- }
- void SoundSource::Update(float timeStep)
- {
- if (!audio_ || !IsEnabledEffective())
- return;
- // If there is no actual audio output, perform fake mixing into a nonexistent buffer to check stopping/looping
- if (!audio_->IsInitialized())
- MixNull(timeStep);
- // Free the stream if playback has stopped
- if (soundStream_ && !position_)
- StopLockless();
- bool playing = IsPlaying();
- if (!playing && sendFinishedEvent_)
- {
- sendFinishedEvent_ = false;
- // Make a weak pointer to self to check for destruction during event handling
- WeakPtr<SoundSource> self(this);
- using namespace SoundFinished;
- VariantMap& eventData = context_->GetEventDataMap();
- eventData[P_NODE] = node_;
- eventData[P_SOUNDSOURCE] = this;
- eventData[P_SOUND] = sound_;
- node_->SendEvent(E_SOUNDFINISHED, eventData);
- if (self.Expired())
- return;
- DoAutoRemove(autoRemove_);
- }
- }
- 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
- auto 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_ = nullptr;
- return;
- }
- }
- else if (sound_)
- timePosition_ = ((float)(int)(size_t)(position_ - sound_->GetStart())) / (sound_->GetSampleSize() * sound_->GetFrequency());
- }
- void SoundSource::UpdateMasterGain()
- {
- if (audio_)
- masterGain_ = audio_->GetSoundSourceMasterGain(soundType_);
- }
- void SoundSource::SetSoundAttr(const ResourceRef& value)
- {
- auto* cache = GetSubsystem<ResourceCache>();
- auto* newSound = cache->GetResource<Sound>(value.name_);
- if (IsPlaying())
- Play(newSound);
- else
- {
- // When changing the sound and not playing, free previous sound stream and stream buffer (if any)
- soundStream_.Reset();
- streamBuffer_.Reset();
- sound_ = newSound;
- }
- }
- void SoundSource::SetPlayingAttr(bool value)
- {
- if (value)
- {
- if (!IsPlaying())
- Play(sound_);
- }
- else
- Stop();
- }
- void SoundSource::SetPositionAttr(int value)
- {
- if (sound_)
- SetPlayPosition(sound_->GetStart() + value);
- }
- ResourceRef SoundSource::GetSoundAttr() const
- {
- return GetResourceRef(sound_, Sound::GetTypeStatic());
- }
- int SoundSource::GetPositionAttr() const
- {
- if (sound_ && position_)
- return (int)(GetPlayPosition() - sound_->GetStart());
- else
- return 0;
- }
- void SoundSource::PlayLockless(Sound* sound)
- {
- // Reset the time position in any case
- timePosition_ = 0.0f;
- if (sound)
- {
- if (!sound->IsCompressed())
- {
- // Uncompressed sound start
- signed char* start = sound->GetStart();
- if (start)
- {
- // Free existing stream & stream buffer if any
- soundStream_.Reset();
- streamBuffer_.Reset();
- sound_ = sound;
- position_ = start;
- fractPosition_ = 0;
- sendFinishedEvent_ = true;
- return;
- }
- }
- else
- {
- // Compressed sound start
- PlayLockless(sound->GetDecoderStream());
- sound_ = sound;
- return;
- }
- }
- // If sound pointer is null or if sound has no data, stop playback
- StopLockless();
- sound_.Reset();
- }
- void SoundSource::PlayLockless(SharedPtr<SoundStream> stream)
- {
- // Reset the time position in any case
- timePosition_ = 0.0f;
- if (stream)
- {
- // Setup the stream buffer
- unsigned sampleSize = stream->GetSampleSize();
- unsigned streamBufferSize = sampleSize * stream->GetIntFrequency() * STREAM_BUFFER_LENGTH / 1000;
- streamBuffer_ = new Sound(context_);
- streamBuffer_->SetSize(streamBufferSize);
- streamBuffer_->SetFormat(stream->GetIntFrequency(), stream->IsSixteenBit(), stream->IsStereo());
- streamBuffer_->SetLooped(true);
- soundStream_ = stream;
- unusedStreamSize_ = 0;
- position_ = streamBuffer_->GetStart();
- fractPosition_ = 0;
- sendFinishedEvent_ = true;
- return;
- }
- // If stream pointer is null, stop playback
- StopLockless();
- }
- void SoundSource::StopLockless()
- {
- position_ = nullptr;
- timePosition_ = 0.0f;
- // Free the sound stream and decode buffer if a stream was playing
- soundStream_.Reset();
- streamBuffer_.Reset();
- }
- void SoundSource::SetPlayPositionLockless(signed char* pos)
- {
- // Setting position on a stream is not supported
- if (!sound_ || soundStream_)
- return;
- signed char* start = sound_->GetStart();
- signed char* end = sound_->GetEnd();
- if (pos < start)
- pos = start;
- if (sound_->IsSixteenBit() && (pos - start) & 1)
- ++pos;
- if (pos > end)
- pos = end;
- position_ = pos;
- timePosition_ = ((float)(int)(size_t)(pos - sound_->GetStart())) / (sound_->GetSampleSize() * sound_->GetFrequency());
- }
- void SoundSource::MixMonoToMono(Sound* sound, int* dest, unsigned samples, int mixRate)
- {
- float totalGain = masterGain_ * attenuation_ * gain_;
- auto vol = (int)(256.0f * totalGain + 0.5f);
- if (!vol)
- {
- MixZeroVolume(sound, samples, mixRate);
- return;
- }
- float add = frequency_ / (float)mixRate;
- auto intAdd = (int)add;
- auto fractAdd = (int)((add - floorf(add)) * 65536.0f);
- int fractPos = fractPosition_;
- if (sound->IsSixteenBit())
- {
- auto* pos = (short*)position_;
- auto* end = (short*)sound->GetEnd();
- auto* repeat = (short*)sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- *dest = *dest + (*pos * vol) / 256;
- ++dest;
- INC_POS_LOOPED();
- }
- position_ = (signed char*)pos;
- }
- else
- {
- while (samples--)
- {
- *dest = *dest + (*pos * vol) / 256;
- ++dest;
- INC_POS_ONESHOT();
- }
- position_ = (signed char*)pos;
- }
- }
- else
- {
- auto* pos = (signed char*)position_;
- signed char* end = sound->GetEnd();
- signed char* repeat = sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- *dest = *dest + *pos * vol;
- ++dest;
- INC_POS_LOOPED();
- }
- position_ = pos;
- }
- else
- {
- while (samples--)
- {
- *dest = *dest + *pos * vol;
- ++dest;
- INC_POS_ONESHOT();
- }
- position_ = pos;
- }
- }
- fractPosition_ = fractPos;
- }
- void SoundSource::MixMonoToStereo(Sound* sound, int* dest, unsigned samples, int mixRate)
- {
- float totalGain = masterGain_ * attenuation_ * gain_;
- auto leftVol = (int)((-panning_ + 1.0f) * (256.0f * totalGain + 0.5f));
- auto rightVol = (int)((panning_ + 1.0f) * (256.0f * totalGain + 0.5f));
- if (!leftVol && !rightVol)
- {
- MixZeroVolume(sound, samples, mixRate);
- return;
- }
- float add = frequency_ / (float)mixRate;
- auto intAdd = (int)add;
- auto fractAdd = (int)((add - floorf(add)) * 65536.0f);
- int fractPos = fractPosition_;
- if (sound->IsSixteenBit())
- {
- auto* pos = (short*)position_;
- auto* end = (short*)sound->GetEnd();
- auto* repeat = (short*)sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- *dest = *dest + (*pos * leftVol) / 256;
- ++dest;
- *dest = *dest + (*pos * rightVol) / 256;
- ++dest;
- INC_POS_LOOPED();
- }
- position_ = (signed char*)pos;
- }
- else
- {
- while (samples--)
- {
- *dest = *dest + (*pos * leftVol) / 256;
- ++dest;
- *dest = *dest + (*pos * rightVol) / 256;
- ++dest;
- INC_POS_ONESHOT();
- }
- position_ = (signed char*)pos;
- }
- }
- else
- {
- auto* pos = (signed char*)position_;
- signed char* end = sound->GetEnd();
- signed char* repeat = sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- *dest = *dest + *pos * leftVol;
- ++dest;
- *dest = *dest + *pos * rightVol;
- ++dest;
- INC_POS_LOOPED();
- }
- position_ = pos;
- }
- else
- {
- while (samples--)
- {
- *dest = *dest + *pos * leftVol;
- ++dest;
- *dest = *dest + *pos * rightVol;
- ++dest;
- INC_POS_ONESHOT();
- }
- position_ = pos;
- }
- }
- fractPosition_ = fractPos;
- }
- void SoundSource::MixMonoToMonoIP(Sound* sound, int* dest, unsigned samples, int mixRate)
- {
- float totalGain = masterGain_ * attenuation_ * gain_;
- auto vol = (int)(256.0f * totalGain + 0.5f);
- if (!vol)
- {
- MixZeroVolume(sound, samples, mixRate);
- return;
- }
- float add = frequency_ / (float)mixRate;
- auto intAdd = (int)add;
- auto fractAdd = (int)((add - floorf(add)) * 65536.0f);
- int fractPos = fractPosition_;
- if (sound->IsSixteenBit())
- {
- auto* pos = (short*)position_;
- auto* end = (short*)sound->GetEnd();
- auto* repeat = (short*)sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- *dest = *dest + (GET_IP_SAMPLE() * vol) / 256;
- ++dest;
- INC_POS_LOOPED();
- }
- position_ = (signed char*)pos;
- }
- else
- {
- while (samples--)
- {
- *dest = *dest + (GET_IP_SAMPLE() * vol) / 256;
- ++dest;
- INC_POS_ONESHOT();
- }
- position_ = (signed char*)pos;
- }
- }
- else
- {
- auto* pos = (signed char*)position_;
- signed char* end = sound->GetEnd();
- signed char* repeat = sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- *dest = *dest + GET_IP_SAMPLE() * vol;
- ++dest;
- INC_POS_LOOPED();
- }
- position_ = pos;
- }
- else
- {
- while (samples--)
- {
- *dest = *dest + GET_IP_SAMPLE() * vol;
- ++dest;
- INC_POS_ONESHOT();
- }
- position_ = pos;
- }
- }
- fractPosition_ = fractPos;
- }
- void SoundSource::MixMonoToStereoIP(Sound* sound, int* dest, unsigned samples, int mixRate)
- {
- float totalGain = masterGain_ * attenuation_ * gain_;
- auto leftVol = (int)((-panning_ + 1.0f) * (256.0f * totalGain + 0.5f));
- auto rightVol = (int)((panning_ + 1.0f) * (256.0f * totalGain + 0.5f));
- if (!leftVol && !rightVol)
- {
- MixZeroVolume(sound, samples, mixRate);
- return;
- }
- float add = frequency_ / (float)mixRate;
- auto intAdd = (int)add;
- auto fractAdd = (int)((add - floorf(add)) * 65536.0f);
- int fractPos = fractPosition_;
- if (sound->IsSixteenBit())
- {
- auto* pos = (short*)position_;
- auto* end = (short*)sound->GetEnd();
- auto* repeat = (short*)sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- int s = GET_IP_SAMPLE();
- *dest = *dest + (s * leftVol) / 256;
- ++dest;
- *dest = *dest + (s * rightVol) / 256;
- ++dest;
- INC_POS_LOOPED();
- }
- position_ = (signed char*)pos;
- }
- else
- {
- while (samples--)
- {
- int s = GET_IP_SAMPLE();
- *dest = *dest + (s * leftVol) / 256;
- ++dest;
- *dest = *dest + (s * rightVol) / 256;
- ++dest;
- INC_POS_ONESHOT();
- }
- position_ = (signed char*)pos;
- }
- }
- else
- {
- auto* pos = (signed char*)position_;
- signed char* end = sound->GetEnd();
- signed char* repeat = sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- int s = GET_IP_SAMPLE();
- *dest = *dest + s * leftVol;
- ++dest;
- *dest = *dest + s * rightVol;
- ++dest;
- INC_POS_LOOPED();
- }
- position_ = pos;
- }
- else
- {
- while (samples--)
- {
- int s = GET_IP_SAMPLE();
- *dest = *dest + s * leftVol;
- ++dest;
- *dest = *dest + s * rightVol;
- ++dest;
- INC_POS_ONESHOT();
- }
- position_ = pos;
- }
- }
- fractPosition_ = fractPos;
- }
- void SoundSource::MixStereoToMono(Sound* sound, int* dest, unsigned samples, int mixRate)
- {
- float totalGain = masterGain_ * attenuation_ * gain_;
- auto vol = (int)(256.0f * totalGain + 0.5f);
- if (!vol)
- {
- MixZeroVolume(sound, samples, mixRate);
- return;
- }
- float add = frequency_ / (float)mixRate;
- auto intAdd = (int)add;
- auto fractAdd = (int)((add - floorf(add)) * 65536.0f);
- int fractPos = fractPosition_;
- if (sound->IsSixteenBit())
- {
- auto* pos = (short*)position_;
- auto* end = (short*)sound->GetEnd();
- auto* repeat = (short*)sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- int s = ((int)pos[0] + (int)pos[1]) / 2;
- *dest = *dest + (s * vol) / 256;
- ++dest;
- INC_POS_STEREO_LOOPED();
- }
- position_ = (signed char*)pos;
- }
- else
- {
- while (samples--)
- {
- int s = ((int)pos[0] + (int)pos[1]) / 2;
- *dest = *dest + (s * vol) / 256;
- ++dest;
- INC_POS_STEREO_ONESHOT();
- }
- position_ = (signed char*)pos;
- }
- }
- else
- {
- auto* pos = (signed char*)position_;
- signed char* end = sound->GetEnd();
- signed char* repeat = sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- int s = ((int)pos[0] + (int)pos[1]) / 2;
- *dest = *dest + s * vol;
- ++dest;
- INC_POS_STEREO_LOOPED();
- }
- position_ = pos;
- }
- else
- {
- while (samples--)
- {
- int s = ((int)pos[0] + (int)pos[1]) / 2;
- *dest = *dest + s * vol;
- ++dest;
- INC_POS_STEREO_ONESHOT();
- }
- position_ = pos;
- }
- }
- fractPosition_ = fractPos;
- }
- void SoundSource::MixStereoToStereo(Sound* sound, int* dest, unsigned samples, int mixRate)
- {
- float totalGain = masterGain_ * attenuation_ * gain_;
- auto vol = (int)(256.0f * totalGain + 0.5f);
- if (!vol)
- {
- MixZeroVolume(sound, samples, mixRate);
- return;
- }
- float add = frequency_ / (float)mixRate;
- auto intAdd = (int)add;
- auto fractAdd = (int)((add - floorf(add)) * 65536.0f);
- int fractPos = fractPosition_;
- if (sound->IsSixteenBit())
- {
- auto* pos = (short*)position_;
- auto* end = (short*)sound->GetEnd();
- auto* repeat = (short*)sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- *dest = *dest + (pos[0] * vol) / 256;
- ++dest;
- *dest = *dest + (pos[1] * vol) / 256;
- ++dest;
- INC_POS_STEREO_LOOPED();
- }
- position_ = (signed char*)pos;
- }
- else
- {
- while (samples--)
- {
- *dest = *dest + (pos[0] * vol) / 256;
- ++dest;
- *dest = *dest + (pos[1] * vol) / 256;
- ++dest;
- INC_POS_STEREO_ONESHOT();
- }
- position_ = (signed char*)pos;
- }
- }
- else
- {
- auto* pos = (signed char*)position_;
- signed char* end = sound->GetEnd();
- signed char* repeat = sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- *dest = *dest + pos[0] * vol;
- ++dest;
- *dest = *dest + pos[1] * vol;
- ++dest;
- INC_POS_STEREO_LOOPED();
- }
- position_ = pos;
- }
- else
- {
- while (samples--)
- {
- *dest = *dest + pos[0] * vol;
- ++dest;
- *dest = *dest + pos[1] * vol;
- ++dest;
- INC_POS_STEREO_ONESHOT();
- }
- position_ = pos;
- }
- }
- fractPosition_ = fractPos;
- }
- void SoundSource::MixStereoToMonoIP(Sound* sound, int* dest, unsigned samples, int mixRate)
- {
- float totalGain = masterGain_ * attenuation_ * gain_;
- auto vol = (int)(256.0f * totalGain + 0.5f);
- if (!vol)
- {
- MixZeroVolume(sound, samples, mixRate);
- return;
- }
- float add = frequency_ / (float)mixRate;
- auto intAdd = (int)add;
- auto fractAdd = (int)((add - floorf(add)) * 65536.0f);
- int fractPos = fractPosition_;
- if (sound->IsSixteenBit())
- {
- auto* pos = (short*)position_;
- auto* end = (short*)sound->GetEnd();
- auto* repeat = (short*)sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- int s = (GET_IP_SAMPLE_LEFT() + GET_IP_SAMPLE_RIGHT()) / 2;
- *dest = *dest + (s * vol) / 256;
- ++dest;
- INC_POS_STEREO_LOOPED();
- }
- position_ = (signed char*)pos;
- }
- else
- {
- while (samples--)
- {
- int s = (GET_IP_SAMPLE_LEFT() + GET_IP_SAMPLE_RIGHT()) / 2;
- *dest = *dest + (s * vol) / 256;
- ++dest;
- INC_POS_STEREO_ONESHOT();
- }
- position_ = (signed char*)pos;
- }
- }
- else
- {
- auto* pos = (signed char*)position_;
- signed char* end = sound->GetEnd();
- signed char* repeat = sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- int s = (GET_IP_SAMPLE_LEFT() + GET_IP_SAMPLE_RIGHT()) / 2;
- *dest = *dest + s * vol;
- ++dest;
- INC_POS_STEREO_LOOPED();
- }
- position_ = pos;
- }
- else
- {
- while (samples--)
- {
- int s = (GET_IP_SAMPLE_LEFT() + GET_IP_SAMPLE_RIGHT()) / 2;
- *dest = *dest + s * vol;
- ++dest;
- INC_POS_STEREO_ONESHOT();
- }
- position_ = pos;
- }
- }
- fractPosition_ = fractPos;
- }
- void SoundSource::MixStereoToStereoIP(Sound* sound, int* dest, unsigned samples, int mixRate)
- {
- float totalGain = masterGain_ * attenuation_ * gain_;
- auto vol = (int)(256.0f * totalGain + 0.5f);
- if (!vol)
- {
- MixZeroVolume(sound, samples, mixRate);
- return;
- }
- float add = frequency_ / (float)mixRate;
- auto intAdd = (int)add;
- auto fractAdd = (int)((add - floorf(add)) * 65536.0f);
- int fractPos = fractPosition_;
- if (sound->IsSixteenBit())
- {
- auto* pos = (short*)position_;
- auto* end = (short*)sound->GetEnd();
- auto* repeat = (short*)sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- *dest = *dest + (GET_IP_SAMPLE_LEFT() * vol) / 256;
- ++dest;
- *dest = *dest + (GET_IP_SAMPLE_RIGHT() * vol) / 256;
- ++dest;
- INC_POS_STEREO_LOOPED();
- }
- position_ = (signed char*)pos;
- }
- else
- {
- while (samples--)
- {
- *dest = *dest + (GET_IP_SAMPLE_LEFT() * vol) / 256;
- ++dest;
- *dest = *dest + (GET_IP_SAMPLE_RIGHT() * vol) / 256;
- ++dest;
- INC_POS_STEREO_ONESHOT();
- }
- position_ = (signed char*)pos;
- }
- }
- else
- {
- auto* pos = (signed char*)position_;
- signed char* end = sound->GetEnd();
- signed char* repeat = sound->GetRepeat();
- if (sound->IsLooped())
- {
- while (samples--)
- {
- *dest = *dest + GET_IP_SAMPLE_LEFT() * vol;
- ++dest;
- *dest = *dest + GET_IP_SAMPLE_RIGHT() * vol;
- ++dest;
- INC_POS_STEREO_LOOPED();
- }
- position_ = pos;
- }
- else
- {
- while (samples--)
- {
- *dest = *dest + GET_IP_SAMPLE_LEFT() * vol;
- ++dest;
- *dest = *dest + GET_IP_SAMPLE_RIGHT() * vol;
- ++dest;
- INC_POS_STEREO_ONESHOT();
- }
- position_ = pos;
- }
- }
- fractPosition_ = fractPos;
- }
- void SoundSource::MixZeroVolume(Sound* sound, unsigned samples, int mixRate)
- {
- float add = frequency_ * (float)samples / (float)mixRate;
- auto intAdd = (int)add;
- auto fractAdd = (int)((add - floorf(add)) * 65536.0f);
- unsigned sampleSize = sound->GetSampleSize();
- fractPosition_ += fractAdd;
- if (fractPosition_ > 65535)
- {
- fractPosition_ &= 65535;
- position_ += sampleSize;
- }
- position_ += intAdd * sampleSize;
- if (position_ > sound->GetEnd())
- {
- if (sound->IsLooped())
- {
- while (position_ >= sound->GetEnd())
- {
- position_ -= (sound->GetEnd() - sound->GetRepeat());
- }
- }
- else
- position_ = nullptr;
- }
- }
- void SoundSource::MixNull(float timeStep)
- {
- if (!position_ || !sound_ || !IsEnabledEffective())
- return;
- // Advance only the time position
- timePosition_ += timeStep * frequency_ / sound_->GetFrequency();
- if (sound_->IsLooped())
- {
- // For simulated playback, simply reset the time position to zero when the sound loops
- if (timePosition_ >= sound_->GetLength())
- timePosition_ -= sound_->GetLength();
- }
- else
- {
- if (timePosition_ >= sound_->GetLength())
- {
- position_ = nullptr;
- timePosition_ = 0.0f;
- }
- }
- }
- }
|