2016-01-14 08:41:53 -05:00
|
|
|
#include "stdafx.h"
|
2017-04-28 19:54:58 -04:00
|
|
|
#include "../Utilities/orfanidis_eq.h"
|
2017-08-19 16:46:57 -04:00
|
|
|
#include "../Utilities/stb_vorbis.h"
|
2016-01-14 08:41:53 -05:00
|
|
|
#include "SoundMixer.h"
|
|
|
|
#include "CPU.h"
|
2017-04-28 19:54:58 -04:00
|
|
|
#include "VideoRenderer.h"
|
|
|
|
#include "RewindManager.h"
|
2017-08-19 16:46:57 -04:00
|
|
|
#include "WaveRecorder.h"
|
|
|
|
#include "OggMixer.h"
|
2018-07-01 15:21:05 -04:00
|
|
|
#include "Console.h"
|
2016-01-14 08:41:53 -05:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
SoundMixer::SoundMixer(shared_ptr<Console> console)
|
2016-01-14 08:41:53 -05:00
|
|
|
{
|
2018-07-02 21:32:59 -04:00
|
|
|
_audioDevice = nullptr;
|
2019-06-23 20:10:55 -04:00
|
|
|
_clockRate = 0;
|
2018-07-01 15:21:05 -04:00
|
|
|
_console = console;
|
2018-07-13 22:19:26 -04:00
|
|
|
_settings = _console->GetSettings();
|
2017-04-15 13:54:47 -04:00
|
|
|
_eqFrequencyGrid.reset(new orfanidis_eq::freq_grid());
|
2017-08-19 16:46:57 -04:00
|
|
|
_oggMixer.reset();
|
2016-01-14 19:33:16 -05:00
|
|
|
_outputBuffer = new int16_t[SoundMixer::MaxSamplesPerFrame];
|
2016-12-09 21:23:20 -05:00
|
|
|
_blipBufLeft = blip_new(SoundMixer::MaxSamplesPerFrame);
|
|
|
|
_blipBufRight = blip_new(SoundMixer::MaxSamplesPerFrame);
|
2018-07-13 22:19:26 -04:00
|
|
|
_sampleRate = _settings->GetSampleRate();
|
2016-06-12 18:11:31 -04:00
|
|
|
_model = NesModel::NTSC;
|
2016-01-14 08:41:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
SoundMixer::~SoundMixer()
|
|
|
|
{
|
2018-07-02 21:32:59 -04:00
|
|
|
StopRecording();
|
|
|
|
|
2016-01-14 08:41:53 -05:00
|
|
|
delete[] _outputBuffer;
|
|
|
|
_outputBuffer = nullptr;
|
|
|
|
|
2016-12-09 21:23:20 -05:00
|
|
|
blip_delete(_blipBufLeft);
|
|
|
|
blip_delete(_blipBufRight);
|
2016-01-14 08:41:53 -05:00
|
|
|
}
|
|
|
|
|
2016-01-21 00:53:02 -05:00
|
|
|
void SoundMixer::StreamState(bool saving)
|
|
|
|
{
|
2018-06-12 16:37:36 -04:00
|
|
|
Stream(_clockRate, _sampleRate, _model);
|
2016-01-23 00:52:06 -05:00
|
|
|
|
2016-01-21 00:53:02 -05:00
|
|
|
if(!saving) {
|
2018-06-12 16:37:36 -04:00
|
|
|
if(_model == NesModel::Auto) {
|
|
|
|
//Older savestates - assume NTSC
|
|
|
|
_model = NesModel::NTSC;
|
|
|
|
}
|
|
|
|
|
2016-01-21 00:53:02 -05:00
|
|
|
Reset();
|
2016-06-12 18:11:31 -04:00
|
|
|
UpdateRates(true);
|
2016-01-21 00:53:02 -05:00
|
|
|
}
|
|
|
|
|
2016-06-12 11:28:45 -04:00
|
|
|
ArrayInfo<int16_t> currentOutput = { _currentOutput, MaxChannelCount };
|
2016-12-09 21:23:20 -05:00
|
|
|
Stream(_previousOutputLeft, currentOutput, _previousOutputRight);
|
2016-01-21 00:53:02 -05:00
|
|
|
}
|
|
|
|
|
2016-01-14 19:33:16 -05:00
|
|
|
void SoundMixer::RegisterAudioDevice(IAudioDevice *audioDevice)
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
_audioDevice = audioDevice;
|
2016-01-14 19:33:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void SoundMixer::StopAudio(bool clearBuffer)
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_audioDevice) {
|
2016-01-14 19:33:16 -05:00
|
|
|
if(clearBuffer) {
|
2018-07-01 15:21:05 -04:00
|
|
|
_audioDevice->Stop();
|
2016-01-14 19:33:16 -05:00
|
|
|
} else {
|
2018-07-01 15:21:05 -04:00
|
|
|
_audioDevice->Pause();
|
2016-01-14 19:33:16 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-14 08:41:53 -05:00
|
|
|
void SoundMixer::Reset()
|
|
|
|
{
|
2017-08-19 16:46:57 -04:00
|
|
|
if(_oggMixer) {
|
2018-07-13 22:19:26 -04:00
|
|
|
_oggMixer->Reset(_settings->GetSampleRate());
|
2017-08-19 16:46:57 -04:00
|
|
|
}
|
2016-06-25 20:46:54 -04:00
|
|
|
_fadeRatio = 1.0;
|
|
|
|
_muteFrameCount = 0;
|
|
|
|
|
2016-12-09 21:23:20 -05:00
|
|
|
_previousOutputLeft = 0;
|
|
|
|
_previousOutputRight = 0;
|
|
|
|
blip_clear(_blipBufLeft);
|
|
|
|
blip_clear(_blipBufRight);
|
2016-01-14 08:41:53 -05:00
|
|
|
|
2016-01-15 23:51:27 -05:00
|
|
|
_timestamps.clear();
|
|
|
|
|
2016-12-11 10:56:23 -05:00
|
|
|
for(uint32_t i = 0; i < MaxChannelCount; i++) {
|
2016-12-09 21:23:20 -05:00
|
|
|
_volumes[i] = 0;
|
|
|
|
_panning[i] = 0;
|
2016-01-14 08:41:53 -05:00
|
|
|
}
|
|
|
|
memset(_channelOutput, 0, sizeof(_channelOutput));
|
|
|
|
memset(_currentOutput, 0, sizeof(_currentOutput));
|
2017-04-15 13:54:47 -04:00
|
|
|
|
|
|
|
UpdateRates(true);
|
|
|
|
UpdateEqualizers(true);
|
2018-06-09 14:03:53 -04:00
|
|
|
|
|
|
|
_previousTargetRate = _sampleRate;
|
2016-01-14 08:41:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void SoundMixer::PlayAudioBuffer(uint32_t time)
|
|
|
|
{
|
2018-06-09 14:03:53 -04:00
|
|
|
UpdateTargetSampleRate();
|
2016-01-14 08:41:53 -05:00
|
|
|
EndFrame(time);
|
2016-12-09 21:23:20 -05:00
|
|
|
|
|
|
|
size_t sampleCount = blip_read_samples(_blipBufLeft, _outputBuffer, SoundMixer::MaxSamplesPerFrame, 1);
|
2017-04-15 13:54:47 -04:00
|
|
|
ApplyEqualizer(_equalizerLeft.get(), sampleCount);
|
|
|
|
|
2017-04-02 17:41:24 -04:00
|
|
|
if(_hasPanning) {
|
|
|
|
blip_read_samples(_blipBufRight, _outputBuffer + 1, SoundMixer::MaxSamplesPerFrame, 1);
|
2017-04-15 13:54:47 -04:00
|
|
|
ApplyEqualizer(_equalizerRight.get(), sampleCount);
|
2017-04-02 17:41:24 -04:00
|
|
|
} else {
|
2017-04-15 13:54:47 -04:00
|
|
|
//Copy left channel to right channel (optimization - when no panning is used)
|
|
|
|
for(size_t i = 0; i < sampleCount * 2; i += 2) {
|
2017-04-02 17:41:24 -04:00
|
|
|
_outputBuffer[i + 1] = _outputBuffer[i];
|
|
|
|
}
|
|
|
|
}
|
2016-12-09 21:23:20 -05:00
|
|
|
|
2017-08-19 16:46:57 -04:00
|
|
|
if(_oggMixer) {
|
2018-07-13 22:19:26 -04:00
|
|
|
_oggMixer->ApplySamples(_outputBuffer, sampleCount, _settings->GetMasterVolume());
|
2017-08-19 16:46:57 -04:00
|
|
|
}
|
|
|
|
|
2018-07-02 22:44:23 -04:00
|
|
|
if(_console->IsDualSystem()) {
|
2018-07-13 22:19:26 -04:00
|
|
|
if(_console->IsMaster() && _settings->CheckFlag(EmulationFlags::VsDualMuteMaster)) {
|
2018-07-02 22:44:23 -04:00
|
|
|
_lowPassFilter.ApplyFilter(_outputBuffer, sampleCount, 0, 0);
|
2018-07-13 22:19:26 -04:00
|
|
|
} else if(!_console->IsMaster() && _settings->CheckFlag(EmulationFlags::VsDualMuteSlave)) {
|
2018-07-02 22:44:23 -04:00
|
|
|
_lowPassFilter.ApplyFilter(_outputBuffer, sampleCount, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 18:32:22 -04:00
|
|
|
shared_ptr<RewindManager> rewindManager = _console->GetRewindManager();
|
2018-07-01 15:21:05 -04:00
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
if(!_console->GetVideoRenderer()->IsRecording() && !_waveRecorder && !_settings->CheckFlag(EmulationFlags::NsfPlayerEnabled)) {
|
|
|
|
if((_settings->CheckFlag(EmulationFlags::Turbo) || (rewindManager && rewindManager->IsRewinding())) && _settings->CheckFlag(EmulationFlags::ReduceSoundInFastForward)) {
|
2018-06-27 19:45:22 -04:00
|
|
|
//Reduce volume when fast forwarding or rewinding
|
2018-07-13 22:19:26 -04:00
|
|
|
_lowPassFilter.ApplyFilter(_outputBuffer, sampleCount, 0, 1.0 - _settings->GetVolumeReduction());
|
|
|
|
} else if(_settings->CheckFlag(EmulationFlags::InBackground)) {
|
|
|
|
if(_settings->CheckFlag(EmulationFlags::MuteSoundInBackground)) {
|
2018-06-27 19:45:22 -04:00
|
|
|
//Mute sound when in background
|
|
|
|
_lowPassFilter.ApplyFilter(_outputBuffer, sampleCount, 0, 0);
|
2018-07-13 22:19:26 -04:00
|
|
|
} else if(_settings->CheckFlag(EmulationFlags::ReduceSoundInBackground)) {
|
2018-06-27 19:45:22 -04:00
|
|
|
//Apply low pass filter/volume reduction when in background (based on options)
|
2018-07-13 22:19:26 -04:00
|
|
|
_lowPassFilter.ApplyFilter(_outputBuffer, sampleCount, 0, 1.0 - _settings->GetVolumeReduction());
|
2018-06-27 19:45:22 -04:00
|
|
|
}
|
2016-01-31 13:53:17 -05:00
|
|
|
}
|
2016-12-29 21:19:13 -05:00
|
|
|
}
|
2016-01-31 13:53:17 -05:00
|
|
|
|
2018-08-03 10:49:23 -04:00
|
|
|
AudioFilterSettings filterSettings = _settings->GetAudioFilterSettings();
|
|
|
|
|
|
|
|
if(filterSettings.ReverbStrength > 0) {
|
|
|
|
_reverbFilter.ApplyFilter(_outputBuffer, sampleCount, _sampleRate, filterSettings.ReverbStrength, filterSettings.ReverbDelay);
|
2016-12-29 21:19:13 -05:00
|
|
|
} else {
|
|
|
|
_reverbFilter.ResetFilter();
|
|
|
|
}
|
2016-02-21 15:31:39 -05:00
|
|
|
|
2018-08-03 22:49:10 -04:00
|
|
|
switch(filterSettings.Filter) {
|
2018-06-25 15:56:05 -04:00
|
|
|
case StereoFilter::None: break;
|
2018-08-03 10:49:23 -04:00
|
|
|
case StereoFilter::Delay: _stereoDelay.ApplyFilter(_outputBuffer, sampleCount, _sampleRate, filterSettings.Delay); break;
|
|
|
|
case StereoFilter::Panning: _stereoPanning.ApplyFilter(_outputBuffer, sampleCount, filterSettings.Angle); break;
|
|
|
|
case StereoFilter::CombFilter: _stereoCombFilter.ApplyFilter(_outputBuffer, sampleCount, _sampleRate, filterSettings.Delay, filterSettings.Strength); break;
|
2016-12-29 21:19:13 -05:00
|
|
|
}
|
2016-02-21 15:31:39 -05:00
|
|
|
|
2018-08-03 10:49:23 -04:00
|
|
|
if(filterSettings.CrossFadeRatio > 0) {
|
|
|
|
_crossFeedFilter.ApplyFilter(_outputBuffer, sampleCount, filterSettings.CrossFadeRatio);
|
2016-12-29 21:19:13 -05:00
|
|
|
}
|
2016-12-09 14:47:34 -05:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
if(rewindManager && rewindManager->SendAudio(_outputBuffer, (uint32_t)sampleCount, _sampleRate)) {
|
2018-07-31 18:58:50 -04:00
|
|
|
bool isRecording = _waveRecorder || _console->GetVideoRenderer()->IsRecording();
|
|
|
|
if(isRecording) {
|
|
|
|
shared_ptr<WaveRecorder> recorder = _waveRecorder;
|
|
|
|
if(recorder) {
|
|
|
|
if(!recorder->WriteSamples(_outputBuffer, (uint32_t)sampleCount, _sampleRate, true)) {
|
2017-04-28 19:54:58 -04:00
|
|
|
_waveRecorder.reset();
|
|
|
|
}
|
2016-06-05 14:36:20 -04:00
|
|
|
}
|
2018-07-31 18:58:50 -04:00
|
|
|
_console->GetVideoRenderer()->AddRecordingSound(_outputBuffer, (uint32_t)sampleCount, _sampleRate);
|
2016-06-05 14:36:20 -04:00
|
|
|
}
|
2017-04-15 13:54:47 -04:00
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
if(_audioDevice && !_console->IsPaused()) {
|
2018-07-01 15:21:05 -04:00
|
|
|
_audioDevice->PlayBuffer(_outputBuffer, (uint32_t)sampleCount, _sampleRate, true);
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
}
|
2016-02-21 15:31:39 -05:00
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
if(_settings->NeedAudioSettingsUpdate()) {
|
|
|
|
if(_settings->GetSampleRate() != _sampleRate) {
|
2017-04-15 13:54:47 -04:00
|
|
|
//Update sample rate for next frame if setting changed
|
2018-07-13 22:19:26 -04:00
|
|
|
_sampleRate = _settings->GetSampleRate();
|
2017-04-15 13:54:47 -04:00
|
|
|
UpdateRates(true);
|
|
|
|
UpdateEqualizers(true);
|
|
|
|
} else {
|
|
|
|
UpdateEqualizers(false);
|
|
|
|
UpdateRates(false);
|
|
|
|
}
|
2016-01-14 08:41:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundMixer::SetNesModel(NesModel model)
|
|
|
|
{
|
2018-06-12 16:37:36 -04:00
|
|
|
if(_model != model) {
|
|
|
|
_model = model;
|
|
|
|
UpdateRates(true);
|
|
|
|
}
|
2016-01-14 19:33:16 -05:00
|
|
|
}
|
|
|
|
|
2016-06-12 18:11:31 -04:00
|
|
|
void SoundMixer::UpdateRates(bool forceUpdate)
|
2016-01-14 19:33:16 -05:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
uint32_t newRate = _console->GetCpu()->GetClockRate(_model);
|
2018-07-13 22:19:26 -04:00
|
|
|
if(!_settings->GetOverclockAdjustApu()) {
|
|
|
|
newRate = (uint32_t)(newRate * (double)_settings->GetOverclockRate() / 100);
|
2016-06-21 18:58:22 -04:00
|
|
|
}
|
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
if(_settings->CheckFlag(EmulationFlags::IntegerFpsMode)) {
|
2017-09-29 22:09:00 -04:00
|
|
|
//Adjust sample rate when running at 60.0 fps instead of 60.1
|
|
|
|
if(_model == NesModel::NTSC) {
|
|
|
|
newRate = (uint32_t)(newRate * 60.0 / 60.0988118623484);
|
|
|
|
} else {
|
|
|
|
newRate = (uint32_t)(newRate * 50.0 / 50.00697796826829);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-31 18:58:50 -04:00
|
|
|
double targetRate = _sampleRate * GetTargetRateAdjustment();
|
2016-06-12 18:11:31 -04:00
|
|
|
if(_clockRate != newRate || forceUpdate) {
|
|
|
|
_clockRate = newRate;
|
2018-06-09 14:03:53 -04:00
|
|
|
blip_set_rates(_blipBufLeft, _clockRate, targetRate);
|
|
|
|
blip_set_rates(_blipBufRight, _clockRate, targetRate);
|
2017-08-19 16:46:57 -04:00
|
|
|
if(_oggMixer) {
|
|
|
|
_oggMixer->SetSampleRate(_sampleRate);
|
|
|
|
}
|
2016-06-12 18:11:31 -04:00
|
|
|
}
|
2017-04-02 17:41:24 -04:00
|
|
|
|
|
|
|
bool hasPanning = false;
|
|
|
|
for(uint32_t i = 0; i < MaxChannelCount; i++) {
|
2018-07-13 22:19:26 -04:00
|
|
|
_volumes[i] = _settings->GetChannelVolume((AudioChannel)i);
|
|
|
|
_panning[i] = _settings->GetChannelPanning((AudioChannel)i);
|
2017-04-02 17:41:24 -04:00
|
|
|
if(_panning[i] != 1.0) {
|
2017-04-15 13:54:47 -04:00
|
|
|
if(!_hasPanning) {
|
2017-04-02 17:41:24 -04:00
|
|
|
blip_clear(_blipBufLeft);
|
|
|
|
blip_clear(_blipBufRight);
|
|
|
|
}
|
2017-04-15 13:54:47 -04:00
|
|
|
hasPanning = true;
|
2017-04-02 17:41:24 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_hasPanning = hasPanning;
|
2016-01-14 08:41:53 -05:00
|
|
|
}
|
|
|
|
|
2016-12-09 21:23:20 -05:00
|
|
|
double SoundMixer::GetChannelOutput(AudioChannel channel, bool forRightChannel)
|
2016-01-14 08:41:53 -05:00
|
|
|
{
|
2016-12-09 21:23:20 -05:00
|
|
|
if(forRightChannel) {
|
|
|
|
return _currentOutput[(int)channel] * _volumes[(int)channel] * _panning[(int)channel];
|
|
|
|
} else {
|
2016-12-19 21:13:40 -05:00
|
|
|
return _currentOutput[(int)channel] * _volumes[(int)channel] * (2.0 - _panning[(int)channel]);
|
2016-12-09 21:23:20 -05:00
|
|
|
}
|
2016-01-14 08:41:53 -05:00
|
|
|
}
|
|
|
|
|
2016-12-09 21:23:20 -05:00
|
|
|
int16_t SoundMixer::GetOutputVolume(bool forRightChannel)
|
2016-01-14 08:41:53 -05:00
|
|
|
{
|
2016-12-09 21:23:20 -05:00
|
|
|
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);
|
2016-06-01 19:38:31 -04:00
|
|
|
|
2017-04-02 17:41:24 -04:00
|
|
|
uint16_t squareVolume = (uint16_t)(477600 / (8128.0 / squareOutput + 100.0));
|
|
|
|
uint16_t tndVolume = (uint16_t)(818350 / (24329.0 / tndOutput + 100.0));
|
2016-06-01 19:38:31 -04:00
|
|
|
|
2016-06-25 20:46:54 -04:00
|
|
|
return (int16_t)(squareVolume + tndVolume +
|
2016-12-09 21:23:20 -05:00
|
|
|
GetChannelOutput(AudioChannel::FDS, forRightChannel) * 20 +
|
2017-04-26 22:24:38 -04:00
|
|
|
GetChannelOutput(AudioChannel::MMC5, forRightChannel) * 43 +
|
2016-12-09 21:23:20 -05:00
|
|
|
GetChannelOutput(AudioChannel::Namco163, forRightChannel) * 20 +
|
|
|
|
GetChannelOutput(AudioChannel::Sunsoft5B, forRightChannel) * 15 +
|
|
|
|
GetChannelOutput(AudioChannel::VRC6, forRightChannel) * 75 +
|
|
|
|
GetChannelOutput(AudioChannel::VRC7, forRightChannel));
|
2016-01-14 08:41:53 -05:00
|
|
|
}
|
|
|
|
|
2016-06-12 11:28:45 -04:00
|
|
|
void SoundMixer::AddDelta(AudioChannel channel, uint32_t time, int16_t delta)
|
2016-01-14 08:41:53 -05:00
|
|
|
{
|
2016-01-30 14:57:50 -05:00
|
|
|
if(delta != 0) {
|
|
|
|
_timestamps.push_back(time);
|
|
|
|
_channelOutput[(int)channel][time] += delta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-14 08:41:53 -05:00
|
|
|
void SoundMixer::EndFrame(uint32_t time)
|
|
|
|
{
|
2018-07-13 22:19:26 -04:00
|
|
|
double masterVolume = _settings->GetMasterVolume() * _fadeRatio;
|
2016-01-14 08:41:53 -05:00
|
|
|
sort(_timestamps.begin(), _timestamps.end());
|
|
|
|
_timestamps.erase(std::unique(_timestamps.begin(), _timestamps.end()), _timestamps.end());
|
|
|
|
|
2016-06-25 20:46:54 -04:00
|
|
|
bool muteFrame = true;
|
2016-01-14 08:41:53 -05:00
|
|
|
for(size_t i = 0, len = _timestamps.size(); i < len; i++) {
|
2016-06-02 23:56:11 -04:00
|
|
|
uint32_t stamp = _timestamps[i];
|
2016-12-11 10:56:23 -05:00
|
|
|
for(uint32_t j = 0; j < MaxChannelCount; j++) {
|
2016-07-19 17:33:08 -04:00
|
|
|
if(_channelOutput[j][stamp] != 0) {
|
|
|
|
//Assume any change in output means sound is playing, disregarding volume options
|
|
|
|
//NSF tracks that mute the triangle channel by setting it to a high-frequency value will not be considered silent
|
|
|
|
muteFrame = false;
|
|
|
|
}
|
2016-06-02 23:56:11 -04:00
|
|
|
_currentOutput[j] += _channelOutput[j][stamp];
|
2016-01-30 14:57:50 -05:00
|
|
|
}
|
2016-01-14 08:41:53 -05:00
|
|
|
|
2016-12-09 21:23:20 -05:00
|
|
|
int16_t currentOutput = GetOutputVolume(false);
|
2017-04-02 17:41:24 -04:00
|
|
|
blip_add_delta(_blipBufLeft, stamp, (int)((currentOutput - _previousOutputLeft) * masterVolume));
|
2016-12-09 21:23:20 -05:00
|
|
|
_previousOutputLeft = currentOutput;
|
2016-06-25 20:46:54 -04:00
|
|
|
|
2017-04-02 17:41:24 -04:00
|
|
|
if(_hasPanning) {
|
|
|
|
currentOutput = GetOutputVolume(true);
|
|
|
|
blip_add_delta(_blipBufRight, stamp, (int)((currentOutput - _previousOutputRight) * masterVolume));
|
|
|
|
_previousOutputRight = currentOutput;
|
|
|
|
}
|
2016-06-25 20:46:54 -04:00
|
|
|
}
|
|
|
|
|
2016-12-09 21:23:20 -05:00
|
|
|
blip_end_frame(_blipBufLeft, time);
|
2017-04-02 17:41:24 -04:00
|
|
|
if(_hasPanning) {
|
|
|
|
blip_end_frame(_blipBufRight, time);
|
|
|
|
}
|
2016-01-14 08:41:53 -05:00
|
|
|
|
2016-06-25 20:46:54 -04:00
|
|
|
if(muteFrame) {
|
|
|
|
_muteFrameCount++;
|
|
|
|
} else {
|
|
|
|
_muteFrameCount = 0;
|
|
|
|
}
|
|
|
|
|
2016-01-14 08:41:53 -05:00
|
|
|
//Reset everything
|
|
|
|
_timestamps.clear();
|
|
|
|
memset(_channelOutput, 0, sizeof(_channelOutput));
|
2016-06-05 14:36:20 -04:00
|
|
|
}
|
|
|
|
|
2017-04-15 13:54:47 -04:00
|
|
|
void SoundMixer::ApplyEqualizer(orfanidis_eq::eq1* equalizer, size_t sampleCount)
|
|
|
|
{
|
|
|
|
if(equalizer) {
|
|
|
|
int offset = equalizer == _equalizerRight.get() ? 1 : 0;
|
|
|
|
for(size_t i = 0; i < sampleCount; i++) {
|
|
|
|
double in = _outputBuffer[i * 2 + offset];
|
|
|
|
double out;
|
|
|
|
equalizer->sbs_process(&in, &out);
|
|
|
|
_outputBuffer[i * 2 + offset] = (int16_t)out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundMixer::UpdateEqualizers(bool forceUpdate)
|
|
|
|
{
|
2018-07-13 22:19:26 -04:00
|
|
|
EqualizerFilterType type = _settings->GetEqualizerFilterType();
|
2017-04-15 13:54:47 -04:00
|
|
|
if(type != EqualizerFilterType::None) {
|
2018-07-13 22:19:26 -04:00
|
|
|
vector<double> bands = _settings->GetEqualizerBands();
|
|
|
|
vector<double> bandGains = _settings->GetBandGains();
|
2017-04-15 13:54:47 -04:00
|
|
|
|
|
|
|
if(bands.size() != _eqFrequencyGrid->get_number_of_bands()) {
|
|
|
|
_equalizerLeft.reset();
|
|
|
|
_equalizerRight.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
if((_equalizerLeft && (int)_equalizerLeft->get_eq_type() != (int)type) || !_equalizerLeft || forceUpdate) {
|
|
|
|
bands.insert(bands.begin(), bands[0] - (bands[1] - bands[0]));
|
|
|
|
bands.insert(bands.end(), bands[bands.size() - 1] + (bands[bands.size() - 1] - bands[bands.size() - 2]));
|
|
|
|
_eqFrequencyGrid.reset(new orfanidis_eq::freq_grid());
|
|
|
|
for(size_t i = 1; i < bands.size() - 1; i++) {
|
|
|
|
_eqFrequencyGrid->add_band((bands[i] + bands[i - 1]) / 2, bands[i], (bands[i + 1] + bands[i]) / 2);
|
|
|
|
}
|
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
_equalizerLeft.reset(new orfanidis_eq::eq1(_eqFrequencyGrid.get(), (orfanidis_eq::filter_type)_settings->GetEqualizerFilterType()));
|
|
|
|
_equalizerRight.reset(new orfanidis_eq::eq1(_eqFrequencyGrid.get(), (orfanidis_eq::filter_type)_settings->GetEqualizerFilterType()));
|
2017-04-15 13:54:47 -04:00
|
|
|
_equalizerLeft->set_sample_rate(_sampleRate);
|
|
|
|
_equalizerRight->set_sample_rate(_sampleRate);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(unsigned int i = 0; i < _eqFrequencyGrid->get_number_of_bands(); i++) {
|
2017-11-14 00:00:00 -05:00
|
|
|
_equalizerLeft->change_band_gain_db(i, bandGains[i]);
|
|
|
|
_equalizerRight->change_band_gain_db(i, bandGains[i]);
|
2017-04-15 13:54:47 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_equalizerLeft.reset();
|
|
|
|
_equalizerRight.reset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-05 14:36:20 -04:00
|
|
|
void SoundMixer::StartRecording(string filepath)
|
|
|
|
{
|
2018-07-13 22:19:26 -04:00
|
|
|
_waveRecorder.reset(new WaveRecorder(filepath, _settings->GetSampleRate(), true));
|
2016-06-05 14:36:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void SoundMixer::StopRecording()
|
|
|
|
{
|
|
|
|
_waveRecorder.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SoundMixer::IsRecording()
|
|
|
|
{
|
|
|
|
return _waveRecorder.get() != nullptr;
|
2016-06-25 20:46:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void SoundMixer::SetFadeRatio(double fadeRatio)
|
|
|
|
{
|
|
|
|
_fadeRatio = fadeRatio;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t SoundMixer::GetMuteFrameCount()
|
|
|
|
{
|
|
|
|
return _muteFrameCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundMixer::ResetMuteFrameCount()
|
|
|
|
{
|
|
|
|
_muteFrameCount = 0;
|
2017-08-19 16:46:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
OggMixer* SoundMixer::GetOggMixer()
|
|
|
|
{
|
|
|
|
if(!_oggMixer) {
|
|
|
|
_oggMixer.reset(new OggMixer());
|
2018-07-13 22:19:26 -04:00
|
|
|
_oggMixer->Reset(_settings->GetSampleRate());
|
2017-08-19 16:46:57 -04:00
|
|
|
}
|
|
|
|
return _oggMixer.get();
|
2018-06-09 14:03:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
AudioStatistics SoundMixer::GetStatistics()
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_audioDevice) {
|
|
|
|
return _audioDevice->GetStatistics();
|
2018-06-09 14:03:53 -04:00
|
|
|
} else {
|
|
|
|
return AudioStatistics();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundMixer::ProcessEndOfFrame()
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_audioDevice) {
|
|
|
|
_audioDevice->ProcessEndOfFrame();
|
2018-06-09 14:03:53 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-02 22:09:55 -04:00
|
|
|
double SoundMixer::GetRateAdjustment()
|
|
|
|
{
|
|
|
|
return _rateAdjustment;
|
|
|
|
}
|
|
|
|
|
2018-07-31 18:58:50 -04:00
|
|
|
double SoundMixer::GetTargetRateAdjustment()
|
2018-06-09 14:03:53 -04:00
|
|
|
{
|
2018-07-31 18:58:50 -04:00
|
|
|
bool isRecording = _waveRecorder || _console->GetVideoRenderer()->IsRecording();
|
2018-08-02 22:09:55 -04:00
|
|
|
if(!isRecording && !_settings->CheckFlag(EmulationFlags::DisableDynamicSampleRate)) {
|
2018-07-31 18:58:50 -04:00
|
|
|
//Don't deviate from selected sample rate while recording
|
|
|
|
//TODO: Have 2 output streams (one for recording, one for the speakers)
|
|
|
|
AudioStatistics stats = GetStatistics();
|
|
|
|
|
|
|
|
if(stats.AverageLatency > 0 && _settings->GetEmulationSpeed() == 100) {
|
2018-08-02 22:09:55 -04:00
|
|
|
//Try to stay within +/- 3ms of requested latency
|
|
|
|
constexpr int32_t maxGap = 3;
|
|
|
|
constexpr int32_t maxSubAdjustment = 3600;
|
|
|
|
|
2018-07-31 18:58:50 -04:00
|
|
|
int32_t requestedLatency = (int32_t)_settings->GetAudioLatency();
|
2018-08-02 22:09:55 -04:00
|
|
|
double latencyGap = stats.AverageLatency - requestedLatency;
|
|
|
|
double adjustment = std::min(0.0025, (std::ceil((std::abs(latencyGap) - maxGap) * 8)) * 0.00003125);
|
|
|
|
|
|
|
|
if(latencyGap < 0 && _underTarget < maxSubAdjustment) {
|
|
|
|
_underTarget++;
|
|
|
|
} else if(latencyGap > 0 && _underTarget > -maxSubAdjustment) {
|
|
|
|
_underTarget--;
|
|
|
|
}
|
|
|
|
|
|
|
|
//For every ~1 second spent under/over target latency, further adjust rate (GetTargetRate is called approx. 3x per frame)
|
|
|
|
//This should slowly get us closer to the actual output rate of the sound card
|
|
|
|
double subAdjustment = 0.00003125 * _underTarget / 180;
|
2018-07-31 18:58:50 -04:00
|
|
|
|
2018-08-02 22:09:55 -04:00
|
|
|
if(adjustment > 0) {
|
|
|
|
if(latencyGap > maxGap) {
|
|
|
|
_rateAdjustment = 1 - adjustment + subAdjustment;
|
|
|
|
} else if(latencyGap < -maxGap) {
|
|
|
|
_rateAdjustment = 1 + adjustment + subAdjustment;
|
|
|
|
}
|
|
|
|
} else if(std::abs(latencyGap) < 1) {
|
|
|
|
//Restore normal rate once we get within +/- 1ms
|
|
|
|
_rateAdjustment = 1.0 + subAdjustment;
|
2018-07-31 18:58:50 -04:00
|
|
|
}
|
2018-08-02 22:09:55 -04:00
|
|
|
} else {
|
|
|
|
_underTarget = 0;
|
|
|
|
_rateAdjustment = 1.0;
|
2018-06-09 14:03:53 -04:00
|
|
|
}
|
2018-08-02 22:09:55 -04:00
|
|
|
} else {
|
|
|
|
_underTarget = 0;
|
|
|
|
_rateAdjustment = 1.0;
|
2018-07-31 18:58:50 -04:00
|
|
|
}
|
2018-08-02 22:09:55 -04:00
|
|
|
return _rateAdjustment;
|
2018-07-31 18:58:50 -04:00
|
|
|
}
|
2018-06-09 14:03:53 -04:00
|
|
|
|
2018-07-31 18:58:50 -04:00
|
|
|
void SoundMixer::UpdateTargetSampleRate()
|
|
|
|
{
|
|
|
|
double targetRate = _sampleRate * GetTargetRateAdjustment();
|
|
|
|
if(targetRate != _previousTargetRate) {
|
|
|
|
blip_set_rates(_blipBufLeft, _clockRate, targetRate);
|
|
|
|
blip_set_rates(_blipBufRight, _clockRate, targetRate);
|
|
|
|
_previousTargetRate = targetRate;
|
2018-06-09 14:03:53 -04:00
|
|
|
}
|
2016-01-14 19:33:16 -05:00
|
|
|
}
|