diff --git a/Core/BaseSoundFilter.cpp b/Core/BaseSoundFilter.cpp deleted file mode 100644 index 92e92554..00000000 --- a/Core/BaseSoundFilter.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "stdafx.h" -#include "BaseSoundFilter.h" - -void BaseSoundFilter::UpdateBufferSize(size_t sampleCount, bool isStereo) -{ - if(_maxSampleCount < sampleCount) { - if(_filterBuffer) { - delete[] _filterBuffer; - } - _maxSampleCount = sampleCount; - _filterBuffer = new int16_t[_maxSampleCount * (isStereo ? 2 : 1)]; - memset(_filterBuffer, 0, _maxSampleCount * sizeof(int16_t) * (isStereo ? 2 : 1)); - } -} - -BaseSoundFilter::~BaseSoundFilter() -{ - if(_filterBuffer) { - delete[] _filterBuffer; - } -} \ No newline at end of file diff --git a/Core/BaseSoundFilter.h b/Core/BaseSoundFilter.h deleted file mode 100644 index cf3a7a71..00000000 --- a/Core/BaseSoundFilter.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include "stdafx.h" - -class BaseSoundFilter -{ -protected: - int16_t* _filterBuffer = nullptr; - size_t _maxSampleCount = 0; - - void UpdateBufferSize(size_t sampleCount, bool isStereo); - -public: - virtual ~BaseSoundFilter(); -}; \ No newline at end of file diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index b5f18f82..ceac124b 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -413,7 +413,6 @@ - @@ -747,7 +746,6 @@ - diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index fb21afb0..44989428 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -523,9 +523,6 @@ Nes\APU\Filters - - Nes\APU\Filters - Nes\Mappers\Taito @@ -1236,9 +1233,6 @@ Nes\APU\Filters - - Nes\APU\Filters - Nes\APU\Filters diff --git a/Core/EmulationSettings.cpp b/Core/EmulationSettings.cpp index 980ec3f5..5a816b17 100644 --- a/Core/EmulationSettings.cpp +++ b/Core/EmulationSettings.cpp @@ -13,6 +13,7 @@ uint32_t EmulationSettings::_flags = 0; uint32_t EmulationSettings::_audioLatency = 20000; double EmulationSettings::_channelVolume[11] = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; +double EmulationSettings::_channelPanning[11] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; double EmulationSettings::_masterVolume = 1.0; uint32_t EmulationSettings::_sampleRate = 44100; StereoFilter EmulationSettings::_stereoFilter = StereoFilter::None; diff --git a/Core/EmulationSettings.h b/Core/EmulationSettings.h index de6ee1cc..174d9311 100644 --- a/Core/EmulationSettings.h +++ b/Core/EmulationSettings.h @@ -301,6 +301,7 @@ private: static uint32_t _audioLatency; static double _channelVolume[11]; + static double _channelPanning[11]; static double _masterVolume; static uint32_t _sampleRate; static StereoFilter _stereoFilter; @@ -429,6 +430,11 @@ public: _masterVolume = volume; } + static void SetChannelPanning(AudioChannel channel, double panning) + { + _channelPanning[(int)channel] = panning; + } + static void SetSampleRate(uint32_t sampleRate) { _sampleRate = sampleRate; @@ -647,6 +653,11 @@ public: return _masterVolume; } + static double GetChannelPanning(AudioChannel channel) + { + return _channelPanning[(int)channel]; + } + static uint32_t GetAudioLatency() { return _audioLatency; diff --git a/Core/ReverbFilter.cpp b/Core/ReverbFilter.cpp index eac69eee..da75fb44 100644 --- a/Core/ReverbFilter.cpp +++ b/Core/ReverbFilter.cpp @@ -8,24 +8,22 @@ void ReverbFilter::ResetFilter() } } -int16_t* ReverbFilter::ApplyFilter(int16_t* monoBuffer, size_t sampleCount, uint32_t sampleRate, double reverbStrength, double reverbDelay) +void ReverbFilter::ApplyFilter(int16_t* stereoBuffer, size_t sampleCount, uint32_t sampleRate, double reverbStrength, double reverbDelay) { - _delay[0].SetParameters(550 * reverbDelay, 0.25 * reverbStrength, sampleRate); - _delay[1].SetParameters(330 * reverbDelay, 0.15 * reverbStrength, sampleRate); - _delay[2].SetParameters(485 * reverbDelay, 0.12 * reverbStrength, sampleRate); - _delay[3].SetParameters(150 * reverbDelay, 0.20 * reverbStrength, sampleRate); - _delay[4].SetParameters(285 * reverbDelay, 0.05 * reverbStrength, sampleRate); - - UpdateBufferSize(sampleCount, false); - - memcpy(_filterBuffer, monoBuffer, sampleCount * sizeof(int16_t)); - - for(int i = 0; i < 5; i++) { - _delay[i].ApplyReverb(_filterBuffer, sampleCount); - } - for(int i = 0; i < 5; i++) { - _delay[i].AddSamples(_filterBuffer, sampleCount); + for(int i = 0; i < 2; i++) { + _delay[i*5].SetParameters(550 * reverbDelay, 0.25 * reverbStrength, sampleRate); + _delay[i*5+1].SetParameters(330 * reverbDelay, 0.15 * reverbStrength, sampleRate); + _delay[i*5+2].SetParameters(485 * reverbDelay, 0.12 * reverbStrength, sampleRate); + _delay[i*5+3].SetParameters(150 * reverbDelay, 0.20 * reverbStrength, sampleRate); + _delay[i*5+4].SetParameters(285 * reverbDelay, 0.05 * reverbStrength, sampleRate); } - return _filterBuffer; + for(int i = 0; i < 5; i++) { + _delay[i].ApplyReverb(stereoBuffer, sampleCount); + _delay[i+5].ApplyReverb(stereoBuffer+1, sampleCount); + } + for(int i = 0; i < 5; i++) { + _delay[i].AddSamples(stereoBuffer, sampleCount); + _delay[i+5].AddSamples(stereoBuffer+1, sampleCount); + } } \ No newline at end of file diff --git a/Core/ReverbFilter.h b/Core/ReverbFilter.h index e4733b5c..c96f2187 100644 --- a/Core/ReverbFilter.h +++ b/Core/ReverbFilter.h @@ -1,6 +1,5 @@ #pragma once #include "stdafx.h" -#include "BaseSoundFilter.h" #include class ReverbDelay @@ -29,7 +28,7 @@ public: void AddSamples(int16_t* buffer, size_t sampleCount) { for(size_t i = 0; i < sampleCount; i++) { - _samples.push_back(buffer[i]); + _samples.push_back(buffer[i*2]); } } @@ -39,19 +38,19 @@ public: size_t samplesToInsert = std::min(_samples.size() - _delay, sampleCount); for(size_t j = sampleCount - samplesToInsert; j < sampleCount; j++) { - buffer[j] += (int16_t)((double)_samples.front() * _decay); + buffer[j*2] += (int16_t)((double)_samples.front() * _decay); _samples.pop_front(); } } } }; -class ReverbFilter : public BaseSoundFilter +class ReverbFilter { private: - ReverbDelay _delay[5]; + ReverbDelay _delay[10]; public: void ResetFilter(); - int16_t* ApplyFilter(int16_t* monoBuffer, size_t sampleCount, uint32_t sampleRate, double reverbStrength, double reverbDelay); + void ApplyFilter(int16_t* stereoBuffer, size_t sampleCount, uint32_t sampleRate, double reverbStrength, double reverbDelay); }; \ No newline at end of file diff --git a/Core/SoundMixer.cpp b/Core/SoundMixer.cpp index 7f3eb875..8eec4e61 100644 --- a/Core/SoundMixer.cpp +++ b/Core/SoundMixer.cpp @@ -2,7 +2,6 @@ #include "SoundMixer.h" #include "APU.h" #include "CPU.h" -#include "CrossFeedFilter.h" IAudioDevice* SoundMixer::AudioDevice = nullptr; unique_ptr SoundMixer::_waveRecorder; @@ -13,7 +12,8 @@ uint32_t SoundMixer::_muteFrameCount; SoundMixer::SoundMixer() { _outputBuffer = new int16_t[SoundMixer::MaxSamplesPerFrame]; - _blipBuf = blip_new(SoundMixer::MaxSamplesPerFrame); + _blipBufLeft = blip_new(SoundMixer::MaxSamplesPerFrame); + _blipBufRight = blip_new(SoundMixer::MaxSamplesPerFrame); _sampleRate = EmulationSettings::GetSampleRate(); _model = NesModel::NTSC; @@ -25,7 +25,8 @@ SoundMixer::~SoundMixer() delete[] _outputBuffer; _outputBuffer = nullptr; - blip_delete(_blipBuf); + blip_delete(_blipBufLeft); + blip_delete(_blipBufRight); } void SoundMixer::StreamState(bool saving) @@ -38,7 +39,7 @@ void SoundMixer::StreamState(bool saving) } ArrayInfo currentOutput = { _currentOutput, MaxChannelCount }; - Stream(_previousOutput, currentOutput); + Stream(_previousOutputLeft, currentOutput, _previousOutputRight); } void SoundMixer::RegisterAudioDevice(IAudioDevice *audioDevice) @@ -62,13 +63,16 @@ void SoundMixer::Reset() _fadeRatio = 1.0; _muteFrameCount = 0; - _previousOutput = 0; - blip_clear(_blipBuf); + _previousOutputLeft = 0; + _previousOutputRight = 0; + blip_clear(_blipBufLeft); + blip_clear(_blipBufRight); _timestamps.clear(); for(int i = 0; i < MaxChannelCount; i++) { - _volumes[0] = 0; + _volumes[i] = 0; + _panning[i] = 0; } memset(_channelOutput, 0, sizeof(_channelOutput)); memset(_currentOutput, 0, sizeof(_currentOutput)); @@ -77,7 +81,10 @@ void SoundMixer::Reset() void SoundMixer::PlayAudioBuffer(uint32_t time) { EndFrame(time); - size_t sampleCount = blip_read_samples(_blipBuf, _outputBuffer, SoundMixer::MaxSamplesPerFrame, 0); + + size_t sampleCount = blip_read_samples(_blipBufLeft, _outputBuffer, SoundMixer::MaxSamplesPerFrame, 1); + blip_read_samples(_blipBufRight, _outputBuffer + 1, SoundMixer::MaxSamplesPerFrame, 1); + if(SoundMixer::AudioDevice && !EmulationSettings::IsPaused()) { //Apply low pass filter/volume reduction when in background (based on options) if(!_waveRecorder && !EmulationSettings::CheckFlag(EmulationFlags::NsfPlayerEnabled) && EmulationSettings::CheckFlag(EmulationFlags::InBackground)) { @@ -88,36 +95,26 @@ void SoundMixer::PlayAudioBuffer(uint32_t time) } } - int16_t* soundBuffer = _outputBuffer; if(EmulationSettings::GetReverbStrength() > 0) { - soundBuffer = _reverbFilter.ApplyFilter(soundBuffer, sampleCount, _sampleRate, EmulationSettings::GetReverbStrength(), EmulationSettings::GetReverbDelay()); + _reverbFilter.ApplyFilter(_outputBuffer, sampleCount, _sampleRate, EmulationSettings::GetReverbStrength(), EmulationSettings::GetReverbDelay()); } else { _reverbFilter.ResetFilter(); } - bool isStereo = false; switch(EmulationSettings::GetStereoFilter()) { - case StereoFilter::Delay: - soundBuffer = _stereoDelay.ApplyFilter(soundBuffer, sampleCount, _sampleRate); - isStereo = true; - break; - - case StereoFilter::Panning: - soundBuffer = _stereoPanning.ApplyFilter(soundBuffer, sampleCount); - isStereo = true; - break; + case StereoFilter::Delay: _stereoDelay.ApplyFilter(_outputBuffer, sampleCount, _sampleRate); break; + case StereoFilter::Panning: _stereoPanning.ApplyFilter(_outputBuffer, sampleCount); break; } - if(isStereo && EmulationSettings::GetCrossFeedRatio() > 0) { - CrossFeedFilter filter; - filter.ApplyFilter(soundBuffer, sampleCount, EmulationSettings::GetCrossFeedRatio()); + if(EmulationSettings::GetCrossFeedRatio() > 0) { + _crossFeedFilter.ApplyFilter(_outputBuffer, sampleCount, EmulationSettings::GetCrossFeedRatio()); } - SoundMixer::AudioDevice->PlayBuffer(soundBuffer, (uint32_t)sampleCount, _sampleRate, isStereo); + SoundMixer::AudioDevice->PlayBuffer(_outputBuffer, (uint32_t)sampleCount, _sampleRate, true); if(_waveRecorder) { auto lock = _waveRecorderLock.AcquireSafe(); if(_waveRecorder) { - if(!_waveRecorder->WriteSamples(soundBuffer, (uint32_t)sampleCount, _sampleRate, isStereo)) { + if(!_waveRecorder->WriteSamples(_outputBuffer, (uint32_t)sampleCount, _sampleRate, true)) { _waveRecorder.reset(); } } @@ -147,30 +144,35 @@ void SoundMixer::UpdateRates(bool forceUpdate) if(_clockRate != newRate || forceUpdate) { _clockRate = newRate; - blip_set_rates(_blipBuf, _clockRate, _sampleRate); + blip_set_rates(_blipBufLeft, _clockRate, _sampleRate); + blip_set_rates(_blipBufRight, _clockRate, _sampleRate); } } -double SoundMixer::GetChannelOutput(AudioChannel channel) +double SoundMixer::GetChannelOutput(AudioChannel channel, bool forRightChannel) { - return _currentOutput[(int)channel] * _volumes[(int)channel]; + if(forRightChannel) { + return _currentOutput[(int)channel] * _volumes[(int)channel] * _panning[(int)channel]; + } else { + return _currentOutput[(int)channel] * _volumes[(int)channel] * (1.0 - _panning[(int)channel]); + } } -int16_t SoundMixer::GetOutputVolume() +int16_t SoundMixer::GetOutputVolume(bool forRightChannel) { - double squareOutput = GetChannelOutput(AudioChannel::Square1) + GetChannelOutput(AudioChannel::Square2); - double tndOutput = 3 * GetChannelOutput(AudioChannel::Triangle) + 2 * GetChannelOutput(AudioChannel::Noise) + GetChannelOutput(AudioChannel::DMC); + double squareOutput = GetChannelOutput(AudioChannel::Square1, forRightChannel) + GetChannelOutput(AudioChannel::Square2, forRightChannel); + double tndOutput = 3 * GetChannelOutput(AudioChannel::Triangle, forRightChannel) + 2 * GetChannelOutput(AudioChannel::Noise, forRightChannel) + GetChannelOutput(AudioChannel::DMC, forRightChannel); uint16_t squareVolume = (uint16_t)(95.52 / (8128.0 / squareOutput + 100.0) * 5000); uint16_t tndVolume = (uint16_t)(163.67 / (24329.0 / tndOutput + 100.0) * 5000); return (int16_t)(squareVolume + tndVolume + - GetChannelOutput(AudioChannel::FDS) * 20 + - GetChannelOutput(AudioChannel::MMC5) * 40 + - GetChannelOutput(AudioChannel::Namco163) * 20 + - GetChannelOutput(AudioChannel::Sunsoft5B) * 15 + - GetChannelOutput(AudioChannel::VRC6) * 75 + - GetChannelOutput(AudioChannel::VRC7)); + GetChannelOutput(AudioChannel::FDS, forRightChannel) * 20 + + GetChannelOutput(AudioChannel::MMC5, forRightChannel) * 40 + + GetChannelOutput(AudioChannel::Namco163, forRightChannel) * 20 + + GetChannelOutput(AudioChannel::Sunsoft5B, forRightChannel) * 15 + + GetChannelOutput(AudioChannel::VRC6, forRightChannel) * 75 + + GetChannelOutput(AudioChannel::VRC7, forRightChannel)); } void SoundMixer::AddDelta(AudioChannel channel, uint32_t time, int16_t delta) @@ -188,7 +190,6 @@ void SoundMixer::EndFrame(uint32_t time) _timestamps.erase(std::unique(_timestamps.begin(), _timestamps.end()), _timestamps.end()); bool muteFrame = true; - int16_t originalOutput = _previousOutput; for(size_t i = 0, len = _timestamps.size(); i < len; i++) { uint32_t stamp = _timestamps[i]; for(int j = 0; j < MaxChannelCount; j++) { @@ -200,13 +201,17 @@ void SoundMixer::EndFrame(uint32_t time) _currentOutput[j] += _channelOutput[j][stamp]; } - int16_t currentOutput = GetOutputVolume(); - blip_add_delta(_blipBuf, stamp, (int)((currentOutput - _previousOutput) * masterVolume * _fadeRatio)); + int16_t currentOutput = GetOutputVolume(false); + blip_add_delta(_blipBufLeft, stamp, (int)((currentOutput - _previousOutputLeft) * masterVolume * _fadeRatio)); + _previousOutputLeft = currentOutput; - _previousOutput = currentOutput; + currentOutput = GetOutputVolume(true); + blip_add_delta(_blipBufRight, stamp, (int)((currentOutput - _previousOutputRight) * masterVolume * _fadeRatio)); + _previousOutputRight = currentOutput; } - blip_end_frame(_blipBuf, time); + blip_end_frame(_blipBufLeft, time); + blip_end_frame(_blipBufRight, time); if(muteFrame) { _muteFrameCount++; @@ -217,6 +222,7 @@ void SoundMixer::EndFrame(uint32_t time) //Reset everything for(int i = 0; i < MaxChannelCount; i++) { _volumes[i] = EmulationSettings::GetChannelVolume((AudioChannel)i); + _panning[i] = EmulationSettings::GetChannelPanning((AudioChannel)i); } _timestamps.clear(); @@ -226,7 +232,7 @@ void SoundMixer::EndFrame(uint32_t time) void SoundMixer::StartRecording(string filepath) { auto lock = _waveRecorderLock.AcquireSafe(); - _waveRecorder.reset(new WaveRecorder(filepath, EmulationSettings::GetSampleRate(), EmulationSettings::GetStereoFilter() != StereoFilter::None)); + _waveRecorder.reset(new WaveRecorder(filepath, EmulationSettings::GetSampleRate(), true)); } void SoundMixer::StopRecording() diff --git a/Core/SoundMixer.h b/Core/SoundMixer.h index 13885ffd..26d61eb6 100644 --- a/Core/SoundMixer.h +++ b/Core/SoundMixer.h @@ -9,6 +9,7 @@ #include "StereoPanningFilter.h" #include "StereoDelayFilter.h" #include "ReverbFilter.h" +#include "CrossFeedFilter.h" #include "WaveRecorder.h" class SoundMixer : public Snapshotable @@ -25,30 +26,34 @@ private: static IAudioDevice* AudioDevice; static const uint32_t MaxSampleRate = 48000; - static const uint32_t MaxSamplesPerFrame = MaxSampleRate / 60 * 4; //x4 to allow CPU overclocking up to 10x + static const uint32_t MaxSamplesPerFrame = MaxSampleRate / 60 * 4 * 2; //x4 to allow CPU overclocking up to 10x, x2 for panning stereo static const uint32_t MaxChannelCount = 11; + CrossFeedFilter _crossFeedFilter; LowPassFilter _lowPassFilter; StereoPanningFilter _stereoPanning; StereoDelayFilter _stereoDelay; ReverbFilter _reverbFilter; - int16_t _previousOutput = 0; + int16_t _previousOutputLeft = 0; + int16_t _previousOutputRight = 0; vector _timestamps; int16_t _channelOutput[MaxChannelCount][CycleLength]; int16_t _currentOutput[MaxChannelCount]; - blip_t* _blipBuf; + blip_t* _blipBufLeft; + blip_t* _blipBufRight; int16_t *_outputBuffer; double _volumes[MaxChannelCount]; + double _panning[MaxChannelCount]; NesModel _model; uint32_t _sampleRate; uint32_t _clockRate; - double GetChannelOutput(AudioChannel channel); - int16_t GetOutputVolume(); + double GetChannelOutput(AudioChannel channel, bool forRightChannel); + int16_t GetOutputVolume(bool forRightChannel); void EndFrame(uint32_t time); void UpdateRates(bool forceUpdate); diff --git a/Core/StereoDelayFilter.cpp b/Core/StereoDelayFilter.cpp index 9bb0f1ac..21b6d896 100644 --- a/Core/StereoDelayFilter.cpp +++ b/Core/StereoDelayFilter.cpp @@ -1,33 +1,28 @@ #include "stdafx.h" #include "StereoDelayFilter.h" -int16_t* StereoDelayFilter::ApplyFilter(int16_t* monoBuffer, size_t sampleCount, uint32_t sampleRate) +void StereoDelayFilter::ApplyFilter(int16_t* stereoBuffer, size_t sampleCount, uint32_t sampleRate) { - UpdateBufferSize(sampleCount, true); - size_t delaySampleCount = (int32_t)((double)EmulationSettings::GetStereoDelay() / 1000 * sampleRate); if(delaySampleCount != _lastDelay) { - _delayedSamples.clear(); + _delayedSamplesLeft.clear(); + _delayedSamplesRight.clear(); } _lastDelay = delaySampleCount; - for(size_t i = 0; i < sampleCount; i++) { - _delayedSamples.push_back(monoBuffer[i]); + for(size_t i = 0; i < sampleCount * 2; i+=2) { + _delayedSamplesLeft.push_back(stereoBuffer[i]); + _delayedSamplesRight.push_back(stereoBuffer[i+1]); } - for(size_t i = 0; i < sampleCount; i++) { - _filterBuffer[i * 2] = monoBuffer[i]; - _filterBuffer[i * 2 + 1] = 0; - } - - if(_delayedSamples.size() > delaySampleCount) { - size_t samplesToInsert = std::max(_delayedSamples.size() - delaySampleCount, sampleCount); + if(_delayedSamplesLeft.size() > delaySampleCount) { + size_t samplesToInsert = std::max(_delayedSamplesLeft.size() - delaySampleCount, sampleCount); for(size_t i = sampleCount - samplesToInsert; i < sampleCount; i++) { - _filterBuffer[i * 2 + 1] = _delayedSamples.front(); - _delayedSamples.pop_front(); + stereoBuffer[i*2] = (stereoBuffer[i*2] + stereoBuffer[i*2+1]) / 2; + stereoBuffer[i*2+1] = (_delayedSamplesRight.front() + _delayedSamplesLeft.front()) / 2; + _delayedSamplesLeft.pop_front(); + _delayedSamplesRight.pop_front(); } } - - return _filterBuffer; } diff --git a/Core/StereoDelayFilter.h b/Core/StereoDelayFilter.h index cf47a51e..51275693 100644 --- a/Core/StereoDelayFilter.h +++ b/Core/StereoDelayFilter.h @@ -3,14 +3,14 @@ #include #include #include "EmulationSettings.h" -#include "BaseSoundFilter.h" -class StereoDelayFilter : public BaseSoundFilter +class StereoDelayFilter { private: - std::deque _delayedSamples; + std::deque _delayedSamplesLeft; + std::deque _delayedSamplesRight; size_t _lastDelay = 0; public: - int16_t* ApplyFilter(int16_t* monoBuffer, size_t sampleCount, uint32_t sampleRate); + void ApplyFilter(int16_t* stereoBuffer, size_t sampleCount, uint32_t sampleRate); }; \ No newline at end of file diff --git a/Core/StereoPanningFilter.cpp b/Core/StereoPanningFilter.cpp index 4f696a52..fa130715 100644 --- a/Core/StereoPanningFilter.cpp +++ b/Core/StereoPanningFilter.cpp @@ -10,15 +10,14 @@ void StereoPanningFilter::UpdateFactors() _rightChannelFactor = _baseFactor * (std::cos(angle) + std::sin(angle)); } -int16_t* StereoPanningFilter::ApplyFilter(int16_t* monoBuffer, size_t sampleCount) +void StereoPanningFilter::ApplyFilter(int16_t* stereoBuffer, size_t sampleCount) { UpdateFactors(); - UpdateBufferSize(sampleCount, true); - for(size_t i = 0; i < sampleCount; i++) { - _filterBuffer[i * 2] = (int16_t)(_leftChannelFactor * (double)monoBuffer[i]); - _filterBuffer[i * 2 + 1] = (int16_t)(_rightChannelFactor * (double)monoBuffer[i]); + for(size_t i = 0; i < sampleCount * 2; i+=2) { + int16_t leftSample = stereoBuffer[i]; + int16_t rightSample = stereoBuffer[i+1]; + stereoBuffer[i] = (int16_t)((_leftChannelFactor * leftSample + _leftChannelFactor * rightSample) / 2); + stereoBuffer[i+1] = (int16_t)((_rightChannelFactor * rightSample + _rightChannelFactor * leftSample) / 2); } - - return _filterBuffer; } \ No newline at end of file diff --git a/Core/StereoPanningFilter.h b/Core/StereoPanningFilter.h index fe018726..f6923cd7 100644 --- a/Core/StereoPanningFilter.h +++ b/Core/StereoPanningFilter.h @@ -1,8 +1,7 @@ #pragma once #include "stdafx.h" -#include "BaseSoundFilter.h" -class StereoPanningFilter : public BaseSoundFilter +class StereoPanningFilter { private: const double _baseFactor = 0.70710678118654752440084436210485; // == sqrt(2)/2 @@ -12,5 +11,5 @@ private: void UpdateFactors(); public: - int16_t* ApplyFilter(int16_t* monoBuffer, size_t sampleCount); + void ApplyFilter(int16_t* stereoBuffer, size_t sampleCount); }; \ No newline at end of file diff --git a/GUI.NET/Config/AudioInfo.cs b/GUI.NET/Config/AudioInfo.cs index 726aead2..afb83639 100644 --- a/GUI.NET/Config/AudioInfo.cs +++ b/GUI.NET/Config/AudioInfo.cs @@ -26,6 +26,19 @@ namespace Mesen.GUI.Config public UInt32 Vrc7Volume = 100; public UInt32 Namco163Volume = 100; public UInt32 Sunsoft5bVolume = 100; + + public Int32 Square1Panning = 0; + public Int32 Square2Panning = 0; + public Int32 TrianglePanning = 0; + public Int32 NoisePanning = 0; + public Int32 DmcPanning = 0; + public Int32 FdsPanning = 0; + public Int32 Mmc5Panning = 0; + public Int32 Vrc6Panning = 0; + public Int32 Vrc7Panning = 0; + public Int32 Namco163Panning = 0; + public Int32 Sunsoft5bPanning = 0; + public UInt32 SampleRate = 44100; public bool ReduceSoundInBackground = true; public bool MuteSoundInBackground = false; @@ -54,6 +67,11 @@ namespace Mesen.GUI.Config } } + static private double ConvertPanning(Int32 panning) + { + return (double)((panning + 100) / 200d); + } + static public void ApplyConfig() { AudioInfo audioInfo = ConfigManager.Config.AudioInfo; @@ -71,6 +89,19 @@ namespace Mesen.GUI.Config InteropEmu.SetChannelVolume(AudioChannel.VRC7, ConvertVolume(audioInfo.Vrc7Volume)); InteropEmu.SetChannelVolume(AudioChannel.Namco163, ConvertVolume(audioInfo.Namco163Volume)); InteropEmu.SetChannelVolume(AudioChannel.Sunsoft5B, ConvertVolume(audioInfo.Sunsoft5bVolume)); + + InteropEmu.SetChannelPanning(AudioChannel.Square1, ConvertPanning(audioInfo.Square1Panning)); + InteropEmu.SetChannelPanning(AudioChannel.Square2, ConvertPanning(audioInfo.Square2Panning)); + InteropEmu.SetChannelPanning(AudioChannel.Triangle, ConvertPanning(audioInfo.TrianglePanning)); + InteropEmu.SetChannelPanning(AudioChannel.Noise, ConvertPanning(audioInfo.NoisePanning)); + InteropEmu.SetChannelPanning(AudioChannel.DMC, ConvertPanning(audioInfo.DmcPanning)); + InteropEmu.SetChannelPanning(AudioChannel.FDS, ConvertPanning(audioInfo.FdsPanning)); + InteropEmu.SetChannelPanning(AudioChannel.MMC5, ConvertPanning(audioInfo.Mmc5Panning)); + InteropEmu.SetChannelPanning(AudioChannel.VRC6, ConvertPanning(audioInfo.Vrc6Panning)); + InteropEmu.SetChannelPanning(AudioChannel.VRC7, ConvertPanning(audioInfo.Vrc7Panning)); + InteropEmu.SetChannelPanning(AudioChannel.Namco163, ConvertPanning(audioInfo.Namco163Panning)); + InteropEmu.SetChannelPanning(AudioChannel.Sunsoft5B, ConvertPanning(audioInfo.Sunsoft5bPanning)); + InteropEmu.SetSampleRate(audioInfo.SampleRate); InteropEmu.SetFlag(EmulationFlags.MuteSoundInBackground, audioInfo.MuteSoundInBackground); diff --git a/GUI.NET/Controls/ctrlHorizontalTrackbar.cs b/GUI.NET/Controls/ctrlHorizontalTrackbar.cs index 62389074..723e188c 100644 --- a/GUI.NET/Controls/ctrlHorizontalTrackbar.cs +++ b/GUI.NET/Controls/ctrlHorizontalTrackbar.cs @@ -50,7 +50,7 @@ namespace Mesen.GUI.Controls get { return trackBar.Value; } set { - trackBar.Value = value; + trackBar.Value = Math.Max(trackBar.Minimum, Math.Min(value, trackBar.Maximum)); txtValue.Text = trackBar.Value.ToString(); } } diff --git a/GUI.NET/Forms/Config/frmAudioConfig.Designer.cs b/GUI.NET/Forms/Config/frmAudioConfig.Designer.cs index 6a2e2dc7..4e91502e 100644 --- a/GUI.NET/Forms/Config/frmAudioConfig.Designer.cs +++ b/GUI.NET/Forms/Config/frmAudioConfig.Designer.cs @@ -59,6 +59,18 @@ this.tabMain = new System.Windows.Forms.TabControl(); this.tpgGeneral = new System.Windows.Forms.TabPage(); this.tpgVolume = new System.Windows.Forms.TabPage(); + this.tpgPanning = new System.Windows.Forms.TabPage(); + this.tableLayoutPanel6 = new System.Windows.Forms.TableLayoutPanel(); + this.trkDmcPan = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkNoisePan = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkVrc6Pan = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkVrc7Pan = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkNamcoPan = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkSunsoftPan = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkSquare1Pan = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkFdsPan = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkSquare2Pan = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkMmc5Pan = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); this.tpgEffects = new System.Windows.Forms.TabPage(); this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel(); this.grpStereo = new System.Windows.Forms.GroupBox(); @@ -72,10 +84,6 @@ this.flowLayoutPanel4 = new System.Windows.Forms.FlowLayoutPanel(); this.nudStereoPanning = new System.Windows.Forms.NumericUpDown(); this.lblStereoPanningAngle = new System.Windows.Forms.Label(); - this.flowLayoutPanel5 = new System.Windows.Forms.FlowLayoutPanel(); - this.chkCrossFeedEnabled = new System.Windows.Forms.CheckBox(); - this.nudCrossFeedRatio = new System.Windows.Forms.NumericUpDown(); - this.lblCrossFeedRatio = new System.Windows.Forms.Label(); this.grpReverb = new System.Windows.Forms.GroupBox(); this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel(); this.chkReverbEnabled = new System.Windows.Forms.CheckBox(); @@ -83,11 +91,16 @@ this.lblReverbDelay = new System.Windows.Forms.Label(); this.trkReverbDelay = new System.Windows.Forms.TrackBar(); this.trkReverbStrength = new System.Windows.Forms.TrackBar(); + this.flowLayoutPanel5 = new System.Windows.Forms.FlowLayoutPanel(); + this.chkCrossFeedEnabled = new System.Windows.Forms.CheckBox(); + this.nudCrossFeedRatio = new System.Windows.Forms.NumericUpDown(); + this.lblCrossFeedRatio = new System.Windows.Forms.Label(); this.tpgAdvanced = new System.Windows.Forms.TabPage(); this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); this.chkSilenceTriangleHighFreq = new System.Windows.Forms.CheckBox(); this.chkSwapDutyCycles = new System.Windows.Forms.CheckBox(); this.chkReduceDmcPopping = new System.Windows.Forms.CheckBox(); + this.trkTrianglePan = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); this.baseConfigPanel.SuspendLayout(); this.grpVolume.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout(); @@ -98,6 +111,8 @@ this.tabMain.SuspendLayout(); this.tpgGeneral.SuspendLayout(); this.tpgVolume.SuspendLayout(); + this.tpgPanning.SuspendLayout(); + this.tableLayoutPanel6.SuspendLayout(); this.tpgEffects.SuspendLayout(); this.tableLayoutPanel4.SuspendLayout(); this.grpStereo.SuspendLayout(); @@ -106,12 +121,12 @@ ((System.ComponentModel.ISupportInitialize)(this.nudStereoDelay)).BeginInit(); this.flowLayoutPanel4.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.nudStereoPanning)).BeginInit(); - this.flowLayoutPanel5.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.nudCrossFeedRatio)).BeginInit(); this.grpReverb.SuspendLayout(); this.tableLayoutPanel5.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.trkReverbDelay)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trkReverbStrength)).BeginInit(); + this.flowLayoutPanel5.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nudCrossFeedRatio)).BeginInit(); this.tpgAdvanced.SuspendLayout(); this.tableLayoutPanel3.SuspendLayout(); this.SuspendLayout(); @@ -119,8 +134,8 @@ // baseConfigPanel // this.baseConfigPanel.Controls.Add(this.btnReset); - this.baseConfigPanel.Location = new System.Drawing.Point(0, 375); - this.baseConfigPanel.Size = new System.Drawing.Size(477, 29); + this.baseConfigPanel.Location = new System.Drawing.Point(0, 376); + this.baseConfigPanel.Size = new System.Drawing.Size(474, 29); this.baseConfigPanel.Controls.SetChildIndex(this.btnReset, 0); // // grpVolume @@ -358,7 +373,7 @@ this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel2.Size = new System.Drawing.Size(463, 343); + this.tableLayoutPanel2.Size = new System.Drawing.Size(460, 344); this.tableLayoutPanel2.TabIndex = 3; // // chkMuteSoundInBackground @@ -532,13 +547,14 @@ // this.tabMain.Controls.Add(this.tpgGeneral); this.tabMain.Controls.Add(this.tpgVolume); + this.tabMain.Controls.Add(this.tpgPanning); this.tabMain.Controls.Add(this.tpgEffects); this.tabMain.Controls.Add(this.tpgAdvanced); this.tabMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tabMain.Location = new System.Drawing.Point(0, 0); this.tabMain.Name = "tabMain"; this.tabMain.SelectedIndex = 0; - this.tabMain.Size = new System.Drawing.Size(477, 375); + this.tabMain.Size = new System.Drawing.Size(474, 376); this.tabMain.TabIndex = 4; // // tpgGeneral @@ -547,7 +563,7 @@ this.tpgGeneral.Location = new System.Drawing.Point(4, 22); this.tpgGeneral.Name = "tpgGeneral"; this.tpgGeneral.Padding = new System.Windows.Forms.Padding(3); - this.tpgGeneral.Size = new System.Drawing.Size(469, 349); + this.tpgGeneral.Size = new System.Drawing.Size(466, 350); this.tpgGeneral.TabIndex = 0; this.tpgGeneral.Text = "General"; this.tpgGeneral.UseVisualStyleBackColor = true; @@ -558,18 +574,210 @@ this.tpgVolume.Location = new System.Drawing.Point(4, 22); this.tpgVolume.Name = "tpgVolume"; this.tpgVolume.Padding = new System.Windows.Forms.Padding(3); - this.tpgVolume.Size = new System.Drawing.Size(469, 349); + this.tpgVolume.Size = new System.Drawing.Size(466, 350); this.tpgVolume.TabIndex = 1; this.tpgVolume.Text = "Volume"; this.tpgVolume.UseVisualStyleBackColor = true; // + // tpgPanning + // + this.tpgPanning.Controls.Add(this.tableLayoutPanel6); + this.tpgPanning.Location = new System.Drawing.Point(4, 22); + this.tpgPanning.Name = "tpgPanning"; + this.tpgPanning.Padding = new System.Windows.Forms.Padding(3); + this.tpgPanning.Size = new System.Drawing.Size(466, 350); + this.tpgPanning.TabIndex = 4; + this.tpgPanning.Text = "Panning"; + this.tpgPanning.UseVisualStyleBackColor = true; + // + // tableLayoutPanel6 + // + this.tableLayoutPanel6.ColumnCount = 2; + this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel6.Controls.Add(this.trkSquare1Pan, 0, 0); + this.tableLayoutPanel6.Controls.Add(this.trkFdsPan, 1, 0); + this.tableLayoutPanel6.Controls.Add(this.trkSquare2Pan, 0, 1); + this.tableLayoutPanel6.Controls.Add(this.trkMmc5Pan, 1, 1); + this.tableLayoutPanel6.Controls.Add(this.trkTrianglePan, 0, 2); + this.tableLayoutPanel6.Controls.Add(this.trkNoisePan, 0, 3); + this.tableLayoutPanel6.Controls.Add(this.trkDmcPan, 0, 4); + this.tableLayoutPanel6.Controls.Add(this.trkVrc6Pan, 1, 2); + this.tableLayoutPanel6.Controls.Add(this.trkVrc7Pan, 1, 3); + this.tableLayoutPanel6.Controls.Add(this.trkNamcoPan, 1, 4); + this.tableLayoutPanel6.Controls.Add(this.trkSunsoftPan, 1, 5); + this.tableLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel6.Location = new System.Drawing.Point(3, 3); + this.tableLayoutPanel6.Name = "tableLayoutPanel6"; + this.tableLayoutPanel6.RowCount = 7; + this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel6.Size = new System.Drawing.Size(460, 344); + this.tableLayoutPanel6.TabIndex = 3; + // + // trkDmcPan + // + this.trkDmcPan.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.trkDmcPan.Location = new System.Drawing.Point(12, 220); + this.trkDmcPan.Margin = new System.Windows.Forms.Padding(0); + this.trkDmcPan.Maximum = 100; + this.trkDmcPan.MaximumSize = new System.Drawing.Size(63, 160); + this.trkDmcPan.Minimum = -100; + this.trkDmcPan.MinimumSize = new System.Drawing.Size(206, 55); + this.trkDmcPan.Name = "trkDmcPan"; + this.trkDmcPan.Size = new System.Drawing.Size(206, 55); + this.trkDmcPan.TabIndex = 16; + this.trkDmcPan.Text = "DMC"; + this.trkDmcPan.Value = 0; + // + // trkNoisePan + // + this.trkNoisePan.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.trkNoisePan.Location = new System.Drawing.Point(12, 165); + this.trkNoisePan.Margin = new System.Windows.Forms.Padding(0); + this.trkNoisePan.Maximum = 100; + this.trkNoisePan.MaximumSize = new System.Drawing.Size(63, 160); + this.trkNoisePan.Minimum = -100; + this.trkNoisePan.MinimumSize = new System.Drawing.Size(206, 55); + this.trkNoisePan.Name = "trkNoisePan"; + this.trkNoisePan.Size = new System.Drawing.Size(206, 55); + this.trkNoisePan.TabIndex = 15; + this.trkNoisePan.Text = "Noise"; + this.trkNoisePan.Value = 0; + // + // trkVrc6Pan + // + this.trkVrc6Pan.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.trkVrc6Pan.Location = new System.Drawing.Point(242, 110); + this.trkVrc6Pan.Margin = new System.Windows.Forms.Padding(0); + this.trkVrc6Pan.Maximum = 100; + this.trkVrc6Pan.MaximumSize = new System.Drawing.Size(63, 160); + this.trkVrc6Pan.Minimum = -100; + this.trkVrc6Pan.MinimumSize = new System.Drawing.Size(206, 55); + this.trkVrc6Pan.Name = "trkVrc6Pan"; + this.trkVrc6Pan.Size = new System.Drawing.Size(206, 55); + this.trkVrc6Pan.TabIndex = 19; + this.trkVrc6Pan.Text = "VRC6"; + this.trkVrc6Pan.Value = 0; + // + // trkVrc7Pan + // + this.trkVrc7Pan.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.trkVrc7Pan.Location = new System.Drawing.Point(242, 165); + this.trkVrc7Pan.Margin = new System.Windows.Forms.Padding(0); + this.trkVrc7Pan.Maximum = 100; + this.trkVrc7Pan.MaximumSize = new System.Drawing.Size(63, 160); + this.trkVrc7Pan.Minimum = -100; + this.trkVrc7Pan.MinimumSize = new System.Drawing.Size(206, 55); + this.trkVrc7Pan.Name = "trkVrc7Pan"; + this.trkVrc7Pan.Size = new System.Drawing.Size(206, 55); + this.trkVrc7Pan.TabIndex = 20; + this.trkVrc7Pan.Text = "VRC7"; + this.trkVrc7Pan.Value = 0; + // + // trkNamcoPan + // + this.trkNamcoPan.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.trkNamcoPan.Location = new System.Drawing.Point(242, 220); + this.trkNamcoPan.Margin = new System.Windows.Forms.Padding(0); + this.trkNamcoPan.Maximum = 100; + this.trkNamcoPan.MaximumSize = new System.Drawing.Size(63, 160); + this.trkNamcoPan.Minimum = -100; + this.trkNamcoPan.MinimumSize = new System.Drawing.Size(206, 55); + this.trkNamcoPan.Name = "trkNamcoPan"; + this.trkNamcoPan.Size = new System.Drawing.Size(206, 55); + this.trkNamcoPan.TabIndex = 21; + this.trkNamcoPan.Text = "Namco"; + this.trkNamcoPan.Value = 0; + // + // trkSunsoftPan + // + this.trkSunsoftPan.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.trkSunsoftPan.Location = new System.Drawing.Point(242, 275); + this.trkSunsoftPan.Margin = new System.Windows.Forms.Padding(0); + this.trkSunsoftPan.Maximum = 100; + this.trkSunsoftPan.MaximumSize = new System.Drawing.Size(63, 160); + this.trkSunsoftPan.Minimum = -100; + this.trkSunsoftPan.MinimumSize = new System.Drawing.Size(206, 55); + this.trkSunsoftPan.Name = "trkSunsoftPan"; + this.trkSunsoftPan.Size = new System.Drawing.Size(206, 55); + this.trkSunsoftPan.TabIndex = 22; + this.trkSunsoftPan.Text = "Sunsoft"; + this.trkSunsoftPan.Value = 0; + // + // trkSquare1Pan + // + this.trkSquare1Pan.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.trkSquare1Pan.Location = new System.Drawing.Point(12, 0); + this.trkSquare1Pan.Margin = new System.Windows.Forms.Padding(0); + this.trkSquare1Pan.Maximum = 100; + this.trkSquare1Pan.MaximumSize = new System.Drawing.Size(63, 160); + this.trkSquare1Pan.Minimum = -100; + this.trkSquare1Pan.MinimumSize = new System.Drawing.Size(206, 55); + this.trkSquare1Pan.Name = "trkSquare1Pan"; + this.trkSquare1Pan.Size = new System.Drawing.Size(206, 55); + this.trkSquare1Pan.TabIndex = 12; + this.trkSquare1Pan.Text = "Square 1"; + this.trkSquare1Pan.Value = 0; + // + // trkFdsPan + // + this.trkFdsPan.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.trkFdsPan.Location = new System.Drawing.Point(242, 0); + this.trkFdsPan.Margin = new System.Windows.Forms.Padding(0); + this.trkFdsPan.Maximum = 100; + this.trkFdsPan.MaximumSize = new System.Drawing.Size(63, 160); + this.trkFdsPan.Minimum = -100; + this.trkFdsPan.MinimumSize = new System.Drawing.Size(206, 55); + this.trkFdsPan.Name = "trkFdsPan"; + this.trkFdsPan.Size = new System.Drawing.Size(206, 55); + this.trkFdsPan.TabIndex = 17; + this.trkFdsPan.Text = "FDS"; + this.trkFdsPan.Value = 0; + // + // trkSquare2Pan + // + this.trkSquare2Pan.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.trkSquare2Pan.Location = new System.Drawing.Point(12, 55); + this.trkSquare2Pan.Margin = new System.Windows.Forms.Padding(0); + this.trkSquare2Pan.Maximum = 100; + this.trkSquare2Pan.MaximumSize = new System.Drawing.Size(63, 160); + this.trkSquare2Pan.Minimum = -100; + this.trkSquare2Pan.MinimumSize = new System.Drawing.Size(206, 55); + this.trkSquare2Pan.Name = "trkSquare2Pan"; + this.trkSquare2Pan.Size = new System.Drawing.Size(206, 55); + this.trkSquare2Pan.TabIndex = 13; + this.trkSquare2Pan.Text = "Square 2"; + this.trkSquare2Pan.Value = 0; + // + // trkMmc5Pan + // + this.trkMmc5Pan.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.trkMmc5Pan.Location = new System.Drawing.Point(242, 55); + this.trkMmc5Pan.Margin = new System.Windows.Forms.Padding(0); + this.trkMmc5Pan.Maximum = 100; + this.trkMmc5Pan.MaximumSize = new System.Drawing.Size(63, 160); + this.trkMmc5Pan.Minimum = -100; + this.trkMmc5Pan.MinimumSize = new System.Drawing.Size(206, 55); + this.trkMmc5Pan.Name = "trkMmc5Pan"; + this.trkMmc5Pan.Size = new System.Drawing.Size(206, 55); + this.trkMmc5Pan.TabIndex = 18; + this.trkMmc5Pan.Text = "MMC5"; + this.trkMmc5Pan.Value = 0; + // // tpgEffects // this.tpgEffects.Controls.Add(this.tableLayoutPanel4); this.tpgEffects.Location = new System.Drawing.Point(4, 22); this.tpgEffects.Name = "tpgEffects"; this.tpgEffects.Padding = new System.Windows.Forms.Padding(3); - this.tpgEffects.Size = new System.Drawing.Size(469, 349); + this.tpgEffects.Size = new System.Drawing.Size(466, 350); this.tpgEffects.TabIndex = 3; this.tpgEffects.Text = "Effects"; this.tpgEffects.UseVisualStyleBackColor = true; @@ -580,14 +788,16 @@ this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel4.Controls.Add(this.grpStereo, 0, 0); this.tableLayoutPanel4.Controls.Add(this.grpReverb, 0, 1); + this.tableLayoutPanel4.Controls.Add(this.flowLayoutPanel5, 0, 2); this.tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel4.Location = new System.Drawing.Point(3, 3); this.tableLayoutPanel4.Name = "tableLayoutPanel4"; - this.tableLayoutPanel4.RowCount = 3; + this.tableLayoutPanel4.RowCount = 4; + this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel4.Size = new System.Drawing.Size(463, 343); + this.tableLayoutPanel4.Size = new System.Drawing.Size(460, 344); this.tableLayoutPanel4.TabIndex = 0; // // grpStereo @@ -596,7 +806,7 @@ this.grpStereo.Dock = System.Windows.Forms.DockStyle.Fill; this.grpStereo.Location = new System.Drawing.Point(3, 3); this.grpStereo.Name = "grpStereo"; - this.grpStereo.Size = new System.Drawing.Size(457, 130); + this.grpStereo.Size = new System.Drawing.Size(454, 95); this.grpStereo.TabIndex = 0; this.grpStereo.TabStop = false; this.grpStereo.Text = "Stereo"; @@ -611,18 +821,17 @@ this.tlpStereoFilter.Controls.Add(this.radStereoDelay, 0, 1); this.tlpStereoFilter.Controls.Add(this.radStereoPanning, 0, 2); this.tlpStereoFilter.Controls.Add(this.flowLayoutPanel4, 1, 2); - this.tlpStereoFilter.Controls.Add(this.flowLayoutPanel5, 0, 4); this.tlpStereoFilter.Dock = System.Windows.Forms.DockStyle.Fill; this.tlpStereoFilter.Location = new System.Drawing.Point(3, 16); this.tlpStereoFilter.Name = "tlpStereoFilter"; - this.tlpStereoFilter.RowCount = 6; + this.tlpStereoFilter.RowCount = 4; this.tlpStereoFilter.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tlpStereoFilter.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tlpStereoFilter.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tlpStereoFilter.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 10F)); - this.tlpStereoFilter.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tlpStereoFilter.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tlpStereoFilter.Size = new System.Drawing.Size(451, 111); + this.tlpStereoFilter.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tlpStereoFilter.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tlpStereoFilter.Size = new System.Drawing.Size(448, 76); this.tlpStereoFilter.TabIndex = 0; // // flowLayoutPanel3 @@ -633,7 +842,7 @@ this.flowLayoutPanel3.Location = new System.Drawing.Point(72, 23); this.flowLayoutPanel3.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel3.Name = "flowLayoutPanel3"; - this.flowLayoutPanel3.Size = new System.Drawing.Size(379, 26); + this.flowLayoutPanel3.Size = new System.Drawing.Size(376, 26); this.flowLayoutPanel3.TabIndex = 1; // // nudStereoDelay @@ -665,7 +874,6 @@ this.radStereoDisabled.Tag = "None"; this.radStereoDisabled.Text = "Disabled"; this.radStereoDisabled.UseVisualStyleBackColor = true; - this.radStereoDisabled.CheckedChanged += new System.EventHandler(this.radStereoDisabled_CheckedChanged); // // radStereoDelay // @@ -699,7 +907,7 @@ this.flowLayoutPanel4.Location = new System.Drawing.Point(72, 49); this.flowLayoutPanel4.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel4.Name = "flowLayoutPanel4"; - this.flowLayoutPanel4.Size = new System.Drawing.Size(379, 26); + this.flowLayoutPanel4.Size = new System.Drawing.Size(376, 26); this.flowLayoutPanel4.TabIndex = 4; // // nudStereoPanning @@ -729,58 +937,13 @@ this.lblStereoPanningAngle.TabIndex = 1; this.lblStereoPanningAngle.Text = "(Angle in degrees)"; // - // flowLayoutPanel5 - // - this.tlpStereoFilter.SetColumnSpan(this.flowLayoutPanel5, 2); - this.flowLayoutPanel5.Controls.Add(this.chkCrossFeedEnabled); - this.flowLayoutPanel5.Controls.Add(this.nudCrossFeedRatio); - this.flowLayoutPanel5.Controls.Add(this.lblCrossFeedRatio); - this.flowLayoutPanel5.Dock = System.Windows.Forms.DockStyle.Fill; - this.flowLayoutPanel5.Location = new System.Drawing.Point(20, 85); - this.flowLayoutPanel5.Margin = new System.Windows.Forms.Padding(20, 0, 0, 0); - this.flowLayoutPanel5.Name = "flowLayoutPanel5"; - this.flowLayoutPanel5.Size = new System.Drawing.Size(431, 25); - this.flowLayoutPanel5.TabIndex = 5; - // - // chkCrossFeedEnabled - // - this.chkCrossFeedEnabled.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.chkCrossFeedEnabled.AutoSize = true; - this.chkCrossFeedEnabled.Enabled = false; - this.chkCrossFeedEnabled.Location = new System.Drawing.Point(3, 5); - this.chkCrossFeedEnabled.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3); - this.chkCrossFeedEnabled.Name = "chkCrossFeedEnabled"; - this.chkCrossFeedEnabled.Size = new System.Drawing.Size(112, 17); - this.chkCrossFeedEnabled.TabIndex = 1; - this.chkCrossFeedEnabled.Text = "Enable Crossfeed:"; - this.chkCrossFeedEnabled.UseVisualStyleBackColor = true; - // - // nudCrossFeedRatio - // - this.nudCrossFeedRatio.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.nudCrossFeedRatio.Enabled = false; - this.nudCrossFeedRatio.Location = new System.Drawing.Point(121, 3); - this.nudCrossFeedRatio.Name = "nudCrossFeedRatio"; - this.nudCrossFeedRatio.Size = new System.Drawing.Size(42, 20); - this.nudCrossFeedRatio.TabIndex = 2; - // - // lblCrossFeedRatio - // - this.lblCrossFeedRatio.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.lblCrossFeedRatio.AutoSize = true; - this.lblCrossFeedRatio.Location = new System.Drawing.Point(169, 6); - this.lblCrossFeedRatio.Name = "lblCrossFeedRatio"; - this.lblCrossFeedRatio.Size = new System.Drawing.Size(15, 13); - this.lblCrossFeedRatio.TabIndex = 3; - this.lblCrossFeedRatio.Text = "%"; - // // grpReverb // this.grpReverb.Controls.Add(this.tableLayoutPanel5); this.grpReverb.Dock = System.Windows.Forms.DockStyle.Fill; - this.grpReverb.Location = new System.Drawing.Point(3, 139); + this.grpReverb.Location = new System.Drawing.Point(3, 104); this.grpReverb.Name = "grpReverb"; - this.grpReverb.Size = new System.Drawing.Size(457, 109); + this.grpReverb.Size = new System.Drawing.Size(454, 109); this.grpReverb.TabIndex = 1; this.grpReverb.TabStop = false; this.grpReverb.Text = "Reverb"; @@ -803,7 +966,7 @@ this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel5.Size = new System.Drawing.Size(451, 90); + this.tableLayoutPanel5.Size = new System.Drawing.Size(448, 90); this.tableLayoutPanel5.TabIndex = 0; // // chkReverbEnabled @@ -859,13 +1022,55 @@ this.trkReverbStrength.TabIndex = 1; this.trkReverbStrength.Value = 1; // + // flowLayoutPanel5 + // + this.flowLayoutPanel5.Controls.Add(this.chkCrossFeedEnabled); + this.flowLayoutPanel5.Controls.Add(this.nudCrossFeedRatio); + this.flowLayoutPanel5.Controls.Add(this.lblCrossFeedRatio); + this.flowLayoutPanel5.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel5.Location = new System.Drawing.Point(6, 216); + this.flowLayoutPanel5.Margin = new System.Windows.Forms.Padding(6, 0, 0, 0); + this.flowLayoutPanel5.Name = "flowLayoutPanel5"; + this.flowLayoutPanel5.Size = new System.Drawing.Size(454, 25); + this.flowLayoutPanel5.TabIndex = 6; + // + // chkCrossFeedEnabled + // + this.chkCrossFeedEnabled.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkCrossFeedEnabled.AutoSize = true; + this.chkCrossFeedEnabled.Location = new System.Drawing.Point(3, 5); + this.chkCrossFeedEnabled.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3); + this.chkCrossFeedEnabled.Name = "chkCrossFeedEnabled"; + this.chkCrossFeedEnabled.Size = new System.Drawing.Size(112, 17); + this.chkCrossFeedEnabled.TabIndex = 1; + this.chkCrossFeedEnabled.Text = "Enable Crossfeed:"; + this.chkCrossFeedEnabled.UseVisualStyleBackColor = true; + // + // nudCrossFeedRatio + // + this.nudCrossFeedRatio.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.nudCrossFeedRatio.Location = new System.Drawing.Point(121, 3); + this.nudCrossFeedRatio.Name = "nudCrossFeedRatio"; + this.nudCrossFeedRatio.Size = new System.Drawing.Size(42, 20); + this.nudCrossFeedRatio.TabIndex = 2; + // + // lblCrossFeedRatio + // + this.lblCrossFeedRatio.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblCrossFeedRatio.AutoSize = true; + this.lblCrossFeedRatio.Location = new System.Drawing.Point(169, 6); + this.lblCrossFeedRatio.Name = "lblCrossFeedRatio"; + this.lblCrossFeedRatio.Size = new System.Drawing.Size(15, 13); + this.lblCrossFeedRatio.TabIndex = 3; + this.lblCrossFeedRatio.Text = "%"; + // // tpgAdvanced // this.tpgAdvanced.Controls.Add(this.tableLayoutPanel3); this.tpgAdvanced.Location = new System.Drawing.Point(4, 22); this.tpgAdvanced.Name = "tpgAdvanced"; this.tpgAdvanced.Padding = new System.Windows.Forms.Padding(3); - this.tpgAdvanced.Size = new System.Drawing.Size(469, 349); + this.tpgAdvanced.Size = new System.Drawing.Size(466, 350); this.tpgAdvanced.TabIndex = 2; this.tpgAdvanced.Text = "Advanced"; this.tpgAdvanced.UseVisualStyleBackColor = true; @@ -885,7 +1090,7 @@ this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel3.Size = new System.Drawing.Size(463, 343); + this.tableLayoutPanel3.Size = new System.Drawing.Size(460, 344); this.tableLayoutPanel3.TabIndex = 1; // // chkSilenceTriangleHighFreq @@ -922,6 +1127,21 @@ this.chkReduceDmcPopping.TextAlign = System.Drawing.ContentAlignment.TopLeft; this.chkReduceDmcPopping.UseVisualStyleBackColor = true; // + // trkTrianglePan + // + this.trkTrianglePan.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.trkTrianglePan.Location = new System.Drawing.Point(12, 110); + this.trkTrianglePan.Margin = new System.Windows.Forms.Padding(0); + this.trkTrianglePan.Maximum = 100; + this.trkTrianglePan.MaximumSize = new System.Drawing.Size(63, 160); + this.trkTrianglePan.Minimum = -100; + this.trkTrianglePan.MinimumSize = new System.Drawing.Size(206, 55); + this.trkTrianglePan.Name = "trkTrianglePan"; + this.trkTrianglePan.Size = new System.Drawing.Size(206, 55); + this.trkTrianglePan.TabIndex = 14; + this.trkTrianglePan.Text = "Triangle"; + this.trkTrianglePan.Value = 0; + // // frmAudioConfig // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -949,6 +1169,8 @@ this.tabMain.ResumeLayout(false); this.tpgGeneral.ResumeLayout(false); this.tpgVolume.ResumeLayout(false); + this.tpgPanning.ResumeLayout(false); + this.tableLayoutPanel6.ResumeLayout(false); this.tpgEffects.ResumeLayout(false); this.tableLayoutPanel4.ResumeLayout(false); this.grpStereo.ResumeLayout(false); @@ -960,14 +1182,14 @@ this.flowLayoutPanel4.ResumeLayout(false); this.flowLayoutPanel4.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.nudStereoPanning)).EndInit(); - this.flowLayoutPanel5.ResumeLayout(false); - this.flowLayoutPanel5.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.nudCrossFeedRatio)).EndInit(); this.grpReverb.ResumeLayout(false); this.tableLayoutPanel5.ResumeLayout(false); this.tableLayoutPanel5.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.trkReverbDelay)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trkReverbStrength)).EndInit(); + this.flowLayoutPanel5.ResumeLayout(false); + this.flowLayoutPanel5.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nudCrossFeedRatio)).EndInit(); this.tpgAdvanced.ResumeLayout(false); this.tableLayoutPanel3.ResumeLayout(false); this.tableLayoutPanel3.PerformLayout(); @@ -1034,9 +1256,22 @@ private System.Windows.Forms.CheckBox chkReduceDmcPopping; private System.Windows.Forms.PictureBox picLatencyWarning; private System.Windows.Forms.Label lblLatencyWarning; + private System.Windows.Forms.TabPage tpgPanning; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6; + private Controls.ctrlHorizontalTrackbar trkDmcPan; + private Controls.ctrlHorizontalTrackbar trkNoisePan; + private Controls.ctrlHorizontalTrackbar trkSquare2Pan; + private Controls.ctrlHorizontalTrackbar trkFdsPan; + private Controls.ctrlHorizontalTrackbar trkMmc5Pan; + private Controls.ctrlHorizontalTrackbar trkVrc6Pan; + private Controls.ctrlHorizontalTrackbar trkVrc7Pan; + private Controls.ctrlHorizontalTrackbar trkNamcoPan; + private Controls.ctrlHorizontalTrackbar trkSunsoftPan; + private Controls.ctrlHorizontalTrackbar trkSquare1Pan; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel5; private System.Windows.Forms.CheckBox chkCrossFeedEnabled; private System.Windows.Forms.NumericUpDown nudCrossFeedRatio; private System.Windows.Forms.Label lblCrossFeedRatio; + private Controls.ctrlHorizontalTrackbar trkTrianglePan; } } \ No newline at end of file diff --git a/GUI.NET/Forms/Config/frmAudioConfig.cs b/GUI.NET/Forms/Config/frmAudioConfig.cs index 9f3dd676..bccd74cf 100644 --- a/GUI.NET/Forms/Config/frmAudioConfig.cs +++ b/GUI.NET/Forms/Config/frmAudioConfig.cs @@ -36,6 +36,18 @@ namespace Mesen.GUI.Forms.Config AddBinding("Namco163Volume", trkNamco163Vol); AddBinding("Sunsoft5bVolume", trkSunsoft5b); + AddBinding("Square1Panning", trkSquare1Pan); + AddBinding("Square2Panning", trkSquare2Pan); + AddBinding("TrianglePanning", trkTrianglePan); + AddBinding("NoisePanning", trkNoisePan); + AddBinding("DmcPanning", trkDmcPan); + AddBinding("FdsPanning", trkFdsPan); + AddBinding("Mmc5Panning", trkMmc5Pan); + AddBinding("Vrc6Panning", trkVrc6Pan); + AddBinding("Vrc7Panning", trkVrc7Pan); + AddBinding("Namco163Panning", trkNamcoPan); + AddBinding("Sunsoft5bPanning", trkSunsoftPan); + AddBinding("AudioLatency", nudLatency); AddBinding("SampleRate", cboSampleRate); AddBinding("AudioDevice", cboAudioDevice); @@ -95,11 +107,5 @@ namespace Mesen.GUI.Forms.Config picLatencyWarning.Visible = nudLatency.Value <= 30; lblLatencyWarning.Visible = nudLatency.Value <= 30; } - - private void radStereoDisabled_CheckedChanged(object sender, EventArgs e) - { - chkCrossFeedEnabled.Enabled = !radStereoDisabled.Checked; - nudCrossFeedRatio.Enabled = !radStereoDisabled.Checked; - } } } diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 3e669170..6457bf33 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -128,6 +128,7 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void SetRamPowerOnState(RamPowerOnState state); [DllImport(DLLPath)] public static extern void SetMasterVolume(double volume); [DllImport(DLLPath)] public static extern void SetChannelVolume(AudioChannel channel, double volume); + [DllImport(DLLPath)] public static extern void SetChannelPanning(AudioChannel channel, double panning); [DllImport(DLLPath)] public static extern void SetSampleRate(UInt32 sampleRate); [DllImport(DLLPath)] public static extern void SetAudioLatency(UInt32 msLatency); [DllImport(DLLPath)] public static extern void SetStereoFilter(StereoFilter stereoFilter); diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index 77bd8e79..efd85014 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -323,6 +323,7 @@ namespace InteropEmu { DllExport void __stdcall SetRamPowerOnState(RamPowerOnState state) { EmulationSettings::SetRamPowerOnState(state); } DllExport void __stdcall SetDisplayLanguage(Language lang) { EmulationSettings::SetDisplayLanguage(lang); } DllExport void __stdcall SetChannelVolume(uint32_t channel, double volume) { EmulationSettings::SetChannelVolume((AudioChannel)channel, volume); } + DllExport void __stdcall SetChannelPanning(uint32_t channel, double panning) { EmulationSettings::SetChannelPanning((AudioChannel)channel, panning); } DllExport void __stdcall SetMasterVolume(double volume) { EmulationSettings::SetMasterVolume(volume); } DllExport void __stdcall SetSampleRate(uint32_t sampleRate) { EmulationSettings::SetSampleRate(sampleRate); } DllExport void __stdcall SetAudioLatency(uint32_t msLatency) { EmulationSettings::SetAudioLatency(msLatency); } diff --git a/Utilities/LowPassFilter.h b/Utilities/LowPassFilter.h index 52d1d37c..4550ecb9 100644 --- a/Utilities/LowPassFilter.h +++ b/Utilities/LowPassFilter.h @@ -7,21 +7,30 @@ class LowPassFilter { private: uint8_t _prevSampleCounter = 0; - int16_t _prevSamples[10] = { 0,0,0,0,0,0,0,0,0,0}; + int16_t _prevSamplesLeft[10] = { 0,0,0,0,0,0,0,0,0,0 }; + int16_t _prevSamplesRight[10] = { 0,0,0,0,0,0,0,0,0,0 }; + + void UpdateSample(int16_t *buffer, size_t index, int strength, double volume, int16_t *_prevSamples) + { + if(strength > 0) { + int32_t sum = std::accumulate(_prevSamples, _prevSamples + strength, 0); + buffer[index] = (int16_t)((sum + buffer[index]) / (strength + 1) * volume); + _prevSamples[_prevSampleCounter] = buffer[index]; + } else { + buffer[index] = (int16_t)(buffer[index] * volume); + } + } public: void ApplyFilter(int16_t *buffer, size_t sampleCount, int strength, double volume = 1.0f) { assert(strength <= 10); - for(size_t i = 0; i < sampleCount; i++) { + for(size_t i = 0; i < sampleCount*2; i+=2) { + UpdateSample(buffer, i, strength, volume, _prevSamplesLeft); + UpdateSample(buffer, i+1, strength, volume, _prevSamplesRight); if(strength > 0) { - int32_t sum = std::accumulate(&_prevSamples[0], &_prevSamples[strength], 0); - buffer[i] = (int16_t)((sum + buffer[i]) / (strength + 1) * volume); - _prevSamples[_prevSampleCounter] = buffer[i]; _prevSampleCounter = (_prevSampleCounter + 1) % strength; - } else { - buffer[i] = (int16_t)(buffer[i] * volume); } } }