Audio: Added channel panning options

This commit is contained in:
Souryo 2016-12-09 21:23:20 -05:00
parent ab56dd1ccb
commit e24514781f
21 changed files with 489 additions and 236 deletions

View file

@ -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;
}
}

View file

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

View file

@ -413,7 +413,6 @@
<ClInclude Include="BaseExpansionAudio.h" />
<ClInclude Include="BaseFdsChannel.h" />
<ClInclude Include="BaseMapper.h" />
<ClInclude Include="BaseSoundFilter.h" />
<ClInclude Include="Bb.h" />
<ClInclude Include="BF9096.h" />
<ClInclude Include="BF909x.h" />
@ -747,7 +746,6 @@
<ClCompile Include="AutoSaveManager.cpp" />
<ClCompile Include="BaseControlDevice.cpp" />
<ClCompile Include="BaseMapper.cpp" />
<ClCompile Include="BaseSoundFilter.cpp" />
<ClCompile Include="Breakpoint.cpp" />
<ClCompile Include="CheatManager.cpp" />
<ClCompile Include="CodeDataLogger.cpp" />

View file

@ -523,9 +523,6 @@
<ClInclude Include="ReverbFilter.h">
<Filter>Nes\APU\Filters</Filter>
</ClInclude>
<ClInclude Include="BaseSoundFilter.h">
<Filter>Nes\APU\Filters</Filter>
</ClInclude>
<ClInclude Include="TaitoX1005.h">
<Filter>Nes\Mappers\Taito</Filter>
</ClInclude>
@ -1236,9 +1233,6 @@
<ClCompile Include="StereoDelayFilter.cpp">
<Filter>Nes\APU\Filters</Filter>
</ClCompile>
<ClCompile Include="BaseSoundFilter.cpp">
<Filter>Nes\APU\Filters</Filter>
</ClCompile>
<ClCompile Include="ReverbFilter.cpp">
<Filter>Nes\APU\Filters</Filter>
</ClCompile>

View file

@ -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;

View file

@ -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;

View file

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

View file

@ -1,6 +1,5 @@
#pragma once
#include "stdafx.h"
#include "BaseSoundFilter.h"
#include <deque>
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<size_t>(_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);
};

View file

@ -2,7 +2,6 @@
#include "SoundMixer.h"
#include "APU.h"
#include "CPU.h"
#include "CrossFeedFilter.h"
IAudioDevice* SoundMixer::AudioDevice = nullptr;
unique_ptr<WaveRecorder> 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<int16_t> 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()

View file

@ -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<uint32_t> _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);

View file

@ -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<size_t>(_delayedSamples.size() - delaySampleCount, sampleCount);
if(_delayedSamplesLeft.size() > delaySampleCount) {
size_t samplesToInsert = std::max<size_t>(_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;
}

View file

@ -3,14 +3,14 @@
#include <deque>
#include <algorithm>
#include "EmulationSettings.h"
#include "BaseSoundFilter.h"
class StereoDelayFilter : public BaseSoundFilter
class StereoDelayFilter
{
private:
std::deque<int16_t> _delayedSamples;
std::deque<int16_t> _delayedSamplesLeft;
std::deque<int16_t> _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);
};

View file

@ -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;
}

View file

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

View file

@ -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);

View file

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

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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);

View file

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

View file

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