2017-04-28 19:54:58 -04:00
|
|
|
#include "stdafx.h"
|
|
|
|
#include "RewindManager.h"
|
|
|
|
#include "MessageManager.h"
|
|
|
|
#include "Console.h"
|
|
|
|
#include "VideoRenderer.h"
|
|
|
|
#include "SoundMixer.h"
|
2017-11-19 23:08:23 -05:00
|
|
|
#include "BaseControlDevice.h"
|
2018-07-11 18:07:13 -04:00
|
|
|
#include "HistoryViewer.h"
|
2017-04-28 19:54:58 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
RewindManager::RewindManager(shared_ptr<Console> console)
|
2017-04-28 19:54:58 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
_console = console;
|
2018-07-13 22:19:26 -04:00
|
|
|
_settings = console->GetSettings();
|
2017-04-28 19:54:58 -04:00
|
|
|
_rewindState = RewindState::Stopped;
|
|
|
|
_framesToFastForward = 0;
|
2018-08-06 18:32:22 -04:00
|
|
|
_hasHistory = false;
|
2017-04-28 19:54:58 -04:00
|
|
|
AddHistoryBlock();
|
|
|
|
|
2019-12-23 16:39:06 -05:00
|
|
|
Initialize();
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
RewindManager::~RewindManager()
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
_console->GetControlManager()->UnregisterInputProvider(this);
|
|
|
|
_console->GetControlManager()->UnregisterInputRecorder(this);
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
|
2019-12-23 16:39:06 -05:00
|
|
|
void RewindManager::Initialize()
|
|
|
|
{
|
|
|
|
_console->GetControlManager()->RegisterInputProvider(this);
|
|
|
|
_console->GetControlManager()->RegisterInputRecorder(this);
|
|
|
|
}
|
|
|
|
|
2017-05-13 21:18:55 -04:00
|
|
|
void RewindManager::ClearBuffer()
|
|
|
|
{
|
2018-08-06 18:32:22 -04:00
|
|
|
_hasHistory = false;
|
2018-07-01 15:21:05 -04:00
|
|
|
_history.clear();
|
|
|
|
_historyBackup.clear();
|
|
|
|
_currentHistory = RewindData();
|
|
|
|
_framesToFastForward = 0;
|
|
|
|
_videoHistory.clear();
|
|
|
|
_videoHistoryBuilder.clear();
|
|
|
|
_audioHistory.clear();
|
|
|
|
_audioHistoryBuilder.clear();
|
|
|
|
_rewindState = RewindState::Stopped;
|
|
|
|
_currentHistory = RewindData();
|
2017-05-13 21:18:55 -04:00
|
|
|
}
|
|
|
|
|
2017-04-28 19:54:58 -04:00
|
|
|
void RewindManager::ProcessNotification(ConsoleNotificationType type, void * parameter)
|
|
|
|
{
|
2019-12-24 13:46:10 -05:00
|
|
|
if(_settings->IsRunAheadFrame()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-04-28 19:54:58 -04:00
|
|
|
if(type == ConsoleNotificationType::PpuFrameDone) {
|
2018-08-06 18:32:22 -04:00
|
|
|
_hasHistory = _history.size() >= 2;
|
2018-07-13 22:19:26 -04:00
|
|
|
if(_settings->GetRewindBufferSize() > 0) {
|
2017-08-01 22:49:50 -04:00
|
|
|
switch(_rewindState) {
|
|
|
|
case RewindState::Starting:
|
|
|
|
case RewindState::Started:
|
|
|
|
case RewindState::Debugging:
|
|
|
|
_currentHistory.FrameCount--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RewindState::Stopping:
|
|
|
|
_framesToFastForward--;
|
|
|
|
_currentHistory.FrameCount++;
|
|
|
|
if(_framesToFastForward == 0) {
|
|
|
|
for(int i = 0; i < 4; i++) {
|
|
|
|
size_t numberToRemove = _currentHistory.InputLogs[i].size();
|
|
|
|
_currentHistory.InputLogs[i] = _historyBackup.front().InputLogs[i];
|
|
|
|
for(size_t j = 0; j < numberToRemove; j++) {
|
|
|
|
_currentHistory.InputLogs[i].pop_back();
|
|
|
|
}
|
2017-05-13 21:18:55 -04:00
|
|
|
}
|
2017-08-01 22:49:50 -04:00
|
|
|
_historyBackup.clear();
|
|
|
|
_rewindState = RewindState::Stopped;
|
2018-07-13 22:19:26 -04:00
|
|
|
_settings->ClearFlags(EmulationFlags::Rewind);
|
|
|
|
_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
2017-08-01 22:49:50 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RewindState::Stopped:
|
|
|
|
_currentHistory.FrameCount++;
|
|
|
|
break;
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
2017-11-14 00:00:00 -05:00
|
|
|
} else {
|
|
|
|
ClearBuffer();
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
2018-07-15 18:26:08 -04:00
|
|
|
} else if(type == ConsoleNotificationType::StateLoaded) {
|
|
|
|
if(_rewindState == RewindState::Stopped) {
|
|
|
|
//A save state was loaded by the user, mark as the end of the current "segment" (for history viewer)
|
|
|
|
_currentHistory.EndOfSegment = true;
|
|
|
|
}
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RewindManager::AddHistoryBlock()
|
|
|
|
{
|
2018-07-13 22:19:26 -04:00
|
|
|
uint32_t maxHistorySize = _settings->GetRewindBufferSize() * 120;
|
2017-11-14 00:00:00 -05:00
|
|
|
if(maxHistorySize > 0) {
|
|
|
|
while(_history.size() > maxHistorySize) {
|
|
|
|
_history.pop_front();
|
|
|
|
}
|
2017-04-28 19:54:58 -04:00
|
|
|
|
2017-11-14 00:00:00 -05:00
|
|
|
if(_currentHistory.FrameCount > 0) {
|
|
|
|
_history.push_back(_currentHistory);
|
|
|
|
}
|
|
|
|
_currentHistory = RewindData();
|
2018-07-01 15:21:05 -04:00
|
|
|
_currentHistory.SaveState(_console);
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RewindManager::PopHistory()
|
|
|
|
{
|
|
|
|
if(_history.empty() && _currentHistory.FrameCount <= 0) {
|
|
|
|
StopRewinding();
|
|
|
|
} else {
|
|
|
|
if(_currentHistory.FrameCount <= 0) {
|
|
|
|
_currentHistory = _history.back();
|
|
|
|
_history.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
_historyBackup.push_front(_currentHistory);
|
2018-07-01 15:21:05 -04:00
|
|
|
_currentHistory.LoadState(_console);
|
2017-04-28 19:54:58 -04:00
|
|
|
if(!_audioHistoryBuilder.empty()) {
|
|
|
|
_audioHistory.insert(_audioHistory.begin(), _audioHistoryBuilder.begin(), _audioHistoryBuilder.end());
|
|
|
|
_audioHistoryBuilder.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-01 22:49:50 -04:00
|
|
|
void RewindManager::Start(bool forDebugger)
|
2017-04-28 19:54:58 -04:00
|
|
|
{
|
2018-07-13 22:19:26 -04:00
|
|
|
if(_rewindState == RewindState::Stopped && _settings->GetRewindBufferSize() > 0) {
|
2019-11-13 18:44:11 -05:00
|
|
|
if(_history.empty() && !forDebugger) {
|
|
|
|
//No history to rewind
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_console->Pause();
|
2017-04-28 19:54:58 -04:00
|
|
|
|
2017-08-01 22:49:50 -04:00
|
|
|
_rewindState = forDebugger ? RewindState::Debugging : RewindState::Starting;
|
2017-04-28 19:54:58 -04:00
|
|
|
_videoHistoryBuilder.clear();
|
|
|
|
_videoHistory.clear();
|
|
|
|
_audioHistoryBuilder.clear();
|
|
|
|
_audioHistory.clear();
|
|
|
|
_historyBackup.clear();
|
2017-11-26 18:19:37 -05:00
|
|
|
|
2019-11-03 11:46:05 -05:00
|
|
|
if(_history.empty()) {
|
|
|
|
_currentHistory.LoadState(_console);
|
|
|
|
} else {
|
|
|
|
PopHistory();
|
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_console->GetSoundMixer()->StopAudio(true);
|
2018-07-13 22:19:26 -04:00
|
|
|
_settings->SetFlags(EmulationFlags::ForceMaxSpeed);
|
|
|
|
_settings->SetFlags(EmulationFlags::Rewind);
|
2017-04-28 19:54:58 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_console->Resume();
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-01 22:49:50 -04:00
|
|
|
void RewindManager::ForceStop()
|
|
|
|
{
|
|
|
|
if(_rewindState != RewindState::Stopped) {
|
|
|
|
while(_historyBackup.size() > 1) {
|
|
|
|
_history.push_back(_historyBackup.front());
|
|
|
|
_historyBackup.pop_front();
|
|
|
|
}
|
2019-11-03 11:46:05 -05:00
|
|
|
if(!_historyBackup.empty()) {
|
|
|
|
_currentHistory = _historyBackup.front();
|
|
|
|
}
|
2017-08-01 22:49:50 -04:00
|
|
|
_historyBackup.clear();
|
|
|
|
_rewindState = RewindState::Stopped;
|
2018-07-13 22:19:26 -04:00
|
|
|
_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
|
|
|
|
_settings->ClearFlags(EmulationFlags::Rewind);
|
2017-08-01 22:49:50 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-28 19:54:58 -04:00
|
|
|
void RewindManager::Stop()
|
|
|
|
{
|
|
|
|
if(_rewindState >= RewindState::Starting) {
|
2018-07-01 15:21:05 -04:00
|
|
|
_console->Pause();
|
2017-04-28 19:54:58 -04:00
|
|
|
if(_rewindState == RewindState::Started) {
|
|
|
|
//Move back to the save state containing the frame currently shown on the screen
|
2017-05-13 21:18:55 -04:00
|
|
|
if(_historyBackup.size() > 1) {
|
|
|
|
_framesToFastForward = (uint32_t)_videoHistory.size() + _historyBackup.front().FrameCount;
|
|
|
|
do {
|
|
|
|
_history.push_back(_historyBackup.front());
|
|
|
|
_framesToFastForward -= _historyBackup.front().FrameCount;
|
|
|
|
_historyBackup.pop_front();
|
2017-04-28 19:54:58 -04:00
|
|
|
|
2017-05-13 21:18:55 -04:00
|
|
|
_currentHistory = _historyBackup.front();
|
|
|
|
}
|
|
|
|
while(_framesToFastForward > RewindManager::BufferSize && _historyBackup.size() > 1);
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//We started rewinding, but didn't actually visually rewind anything yet
|
|
|
|
//Move back to the save state containing the frame currently shown on the screen
|
|
|
|
while(_historyBackup.size() > 1) {
|
|
|
|
_history.push_back(_historyBackup.front());
|
|
|
|
_historyBackup.pop_front();
|
|
|
|
}
|
|
|
|
_currentHistory = _historyBackup.front();
|
|
|
|
_framesToFastForward = _historyBackup.front().FrameCount;
|
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_currentHistory.LoadState(_console);
|
2017-04-28 19:54:58 -04:00
|
|
|
if(_framesToFastForward > 0) {
|
|
|
|
_rewindState = RewindState::Stopping;
|
|
|
|
_currentHistory.FrameCount = 0;
|
2018-07-13 22:19:26 -04:00
|
|
|
_settings->SetFlags(EmulationFlags::ForceMaxSpeed);
|
2017-04-28 19:54:58 -04:00
|
|
|
} else {
|
|
|
|
_rewindState = RewindState::Stopped;
|
|
|
|
_historyBackup.clear();
|
2018-07-13 22:19:26 -04:00
|
|
|
_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
|
|
|
|
_settings->ClearFlags(EmulationFlags::Rewind);
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
_videoHistoryBuilder.clear();
|
|
|
|
_videoHistory.clear();
|
|
|
|
_audioHistoryBuilder.clear();
|
|
|
|
_audioHistory.clear();
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_console->Resume();
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RewindManager::ProcessEndOfFrame()
|
|
|
|
{
|
|
|
|
if(_rewindState >= RewindState::Starting) {
|
2018-03-10 11:17:43 -05:00
|
|
|
if(_currentHistory.FrameCount <= 0 && _rewindState != RewindState::Debugging) {
|
|
|
|
//If we're debugging, we want to keep running the emulation to the end of the next frame (even if it's incomplete)
|
|
|
|
//Otherwise the emulation might diverge due to missing inputs.
|
|
|
|
PopHistory();
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
} else if(_currentHistory.FrameCount >= RewindManager::BufferSize) {
|
|
|
|
AddHistoryBlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-26 18:19:37 -05:00
|
|
|
void RewindManager::ProcessFrame(void * frameBuffer, uint32_t width, uint32_t height, bool forRewind)
|
2017-04-28 19:54:58 -04:00
|
|
|
{
|
2017-08-01 22:49:50 -04:00
|
|
|
if(_rewindState == RewindState::Starting || _rewindState == RewindState::Started) {
|
2017-11-26 18:19:37 -05:00
|
|
|
if(!forRewind) {
|
|
|
|
//Ignore any frames that occur between start of rewind process & first rewinded frame completed
|
|
|
|
//These are caused by the fact that VideoDecoder is asynchronous - a previous (extra) frame can end up
|
|
|
|
//in the rewind queue, which causes display glitches
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-04-28 19:54:58 -04:00
|
|
|
_videoHistoryBuilder.push_back(vector<uint32_t>((uint32_t*)frameBuffer, (uint32_t*)frameBuffer + width*height));
|
|
|
|
|
2018-03-24 11:22:43 -04:00
|
|
|
if(_videoHistoryBuilder.size() == (size_t)_historyBackup.front().FrameCount) {
|
2017-04-28 19:54:58 -04:00
|
|
|
for(int i = (int)_videoHistoryBuilder.size() - 1; i >= 0; i--) {
|
|
|
|
_videoHistory.push_front(_videoHistoryBuilder[i]);
|
|
|
|
}
|
|
|
|
_videoHistoryBuilder.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(_rewindState == RewindState::Started || _videoHistory.size() >= RewindManager::BufferSize) {
|
|
|
|
_rewindState = RewindState::Started;
|
2018-07-13 22:19:26 -04:00
|
|
|
_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
|
2017-04-28 19:54:58 -04:00
|
|
|
if(!_videoHistory.empty()) {
|
2018-07-01 15:21:05 -04:00
|
|
|
_console->GetVideoRenderer()->UpdateFrame(_videoHistory.back().data(), width, height);
|
2017-04-28 19:54:58 -04:00
|
|
|
_videoHistory.pop_back();
|
|
|
|
}
|
|
|
|
}
|
2017-08-01 22:49:50 -04:00
|
|
|
} else if(_rewindState == RewindState::Stopping || _rewindState == RewindState::Debugging) {
|
2017-04-28 19:54:58 -04:00
|
|
|
//Display nothing while resyncing
|
|
|
|
} else {
|
2018-07-01 15:21:05 -04:00
|
|
|
_console->GetVideoRenderer()->UpdateFrame(frameBuffer, width, height);
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RewindManager::ProcessAudio(int16_t * soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
|
|
|
|
{
|
2017-08-01 22:49:50 -04:00
|
|
|
if(_rewindState == RewindState::Starting || _rewindState == RewindState::Started) {
|
2017-04-28 19:54:58 -04:00
|
|
|
_audioHistoryBuilder.insert(_audioHistoryBuilder.end(), soundBuffer, soundBuffer + sampleCount * 2);
|
|
|
|
|
|
|
|
if(_rewindState == RewindState::Started && _audioHistory.size() > sampleCount * 2) {
|
|
|
|
for(uint32_t i = 0; i < sampleCount * 2; i++) {
|
|
|
|
soundBuffer[i] = _audioHistory.back();
|
|
|
|
_audioHistory.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
//Mute while we prepare to rewind
|
|
|
|
return false;
|
|
|
|
}
|
2017-08-01 22:49:50 -04:00
|
|
|
} else if(_rewindState == RewindState::Stopping || _rewindState == RewindState::Debugging) {
|
2017-04-28 19:54:58 -04:00
|
|
|
//Mute while we resync
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-21 17:54:54 -05:00
|
|
|
void RewindManager::RecordInput(vector<shared_ptr<BaseControlDevice>> devices)
|
2017-04-28 19:54:58 -04:00
|
|
|
{
|
2018-07-13 22:19:26 -04:00
|
|
|
if(_settings->GetRewindBufferSize() > 0 && _rewindState == RewindState::Stopped) {
|
2017-11-21 17:54:54 -05:00
|
|
|
for(shared_ptr<BaseControlDevice> &device : devices) {
|
2018-07-01 15:21:05 -04:00
|
|
|
_currentHistory.InputLogs[device->GetPort()].push_back(device->GetRawState());
|
2017-11-21 17:54:54 -05:00
|
|
|
}
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-19 23:08:23 -05:00
|
|
|
bool RewindManager::SetInput(BaseControlDevice *device)
|
2017-04-28 19:54:58 -04:00
|
|
|
{
|
2017-11-19 23:08:23 -05:00
|
|
|
uint8_t port = device->GetPort();
|
2018-07-01 15:21:05 -04:00
|
|
|
if(!_currentHistory.InputLogs[port].empty() && IsRewinding()) {
|
|
|
|
ControlDeviceState state = _currentHistory.InputLogs[port].front();
|
|
|
|
_currentHistory.InputLogs[port].pop_front();
|
2017-11-19 23:08:23 -05:00
|
|
|
device->SetRawState(state);
|
|
|
|
return true;
|
2017-07-30 19:30:02 -04:00
|
|
|
} else {
|
2017-11-19 23:08:23 -05:00
|
|
|
return false;
|
2017-07-30 19:30:02 -04:00
|
|
|
}
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
|
2017-08-01 22:49:50 -04:00
|
|
|
void RewindManager::StartRewinding(bool forDebugger)
|
2017-04-28 19:54:58 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
Start(forDebugger);
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
|
2017-08-01 22:49:50 -04:00
|
|
|
void RewindManager::StopRewinding(bool forDebugger)
|
2017-04-28 19:54:58 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
if(forDebugger) {
|
|
|
|
ForceStop();
|
|
|
|
} else {
|
|
|
|
Stop();
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RewindManager::IsRewinding()
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return _rewindState != RewindState::Stopped;
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
|
2017-08-01 22:49:50 -04:00
|
|
|
bool RewindManager::IsStepBack()
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return _rewindState == RewindState::Debugging;
|
2017-08-01 22:49:50 -04:00
|
|
|
}
|
|
|
|
|
2017-04-28 19:54:58 -04:00
|
|
|
void RewindManager::RewindSeconds(uint32_t seconds)
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_rewindState == RewindState::Stopped) {
|
2017-04-28 19:54:58 -04:00
|
|
|
uint32_t removeCount = (seconds * 60 / RewindManager::BufferSize) + 1;
|
2018-07-01 15:21:05 -04:00
|
|
|
_console->Pause();
|
2017-04-28 19:54:58 -04:00
|
|
|
for(uint32_t i = 0; i < removeCount; i++) {
|
2018-07-01 15:21:05 -04:00
|
|
|
if(!_history.empty()) {
|
|
|
|
_currentHistory = _history.back();
|
|
|
|
_history.pop_back();
|
2017-04-28 19:54:58 -04:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-07-01 15:21:05 -04:00
|
|
|
_currentHistory.LoadState(_console);
|
|
|
|
_console->Resume();
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 18:32:22 -04:00
|
|
|
bool RewindManager::HasHistory()
|
|
|
|
{
|
|
|
|
return _hasHistory;
|
|
|
|
}
|
|
|
|
|
2018-07-11 18:07:13 -04:00
|
|
|
void RewindManager::CopyHistory(shared_ptr<HistoryViewer> destHistoryViewer)
|
|
|
|
{
|
|
|
|
destHistoryViewer->SetHistoryData(_history);
|
|
|
|
}
|
|
|
|
|
2017-11-26 18:19:37 -05:00
|
|
|
void RewindManager::SendFrame(void * frameBuffer, uint32_t width, uint32_t height, bool forRewind)
|
2017-04-28 19:54:58 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
ProcessFrame(frameBuffer, width, height, forRewind);
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool RewindManager::SendAudio(int16_t * soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return ProcessAudio(soundBuffer, sampleCount, sampleRate);
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|