mirror of https://github.com/SourMesen/Mesen2
Audio player freq visualization
This commit is contained in:
parent
768a3f6e17
commit
8c1c469250
|
|
@ -43,9 +43,6 @@
|
|||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\Mesen\Core\NsfeLoader.h" />
|
||||
<ClInclude Include="..\..\Mesen\Core\NsfLoader.h" />
|
||||
<ClInclude Include="..\..\Mesen\Core\NsfMapper.h" />
|
||||
<ClInclude Include="Debugger\stdafx.h" />
|
||||
<ClInclude Include="NES\Input\ArkanoidController.h" />
|
||||
<ClInclude Include="NES\Input\AsciiTurboFile.h" />
|
||||
|
|
@ -101,6 +98,7 @@
|
|||
<ClInclude Include="NES\NesConstants.h" />
|
||||
<ClInclude Include="NES\NesNtscFilter.h" />
|
||||
<ClInclude Include="Netplay\stdafx.h" />
|
||||
<ClInclude Include="Shared\Audio\AudioTrackInfo.h" />
|
||||
<ClInclude Include="Shared\Audio\stdafx.h" />
|
||||
<ClInclude Include="Shared\BaseControlManager.h" />
|
||||
<ClInclude Include="Shared\BaseState.h" />
|
||||
|
|
@ -335,7 +333,7 @@
|
|||
<ClInclude Include="SNES\Coprocessors\SPC7110\Spc7110Decomp.h" />
|
||||
<ClInclude Include="SNES\Debugger\SpcDebugger.h" />
|
||||
<ClInclude Include="SNES\Debugger\SpcDisUtils.h" />
|
||||
<ClInclude Include="SNES\SpcHud.h" />
|
||||
<ClInclude Include="Shared\Audio\AudioPlayerHud.h" />
|
||||
<ClInclude Include="SNES\SpcFileData.h" />
|
||||
<ClInclude Include="SNES\SpcTimer.h" />
|
||||
<ClInclude Include="SNES\SpcTypes.h" />
|
||||
|
|
@ -352,8 +350,6 @@
|
|||
<ClInclude Include="Shared\Audio\WaveRecorder.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\Mesen\Core\NsfLoader.cpp" />
|
||||
<ClCompile Include="..\..\Mesen\Core\NsfMapper.cpp" />
|
||||
<ClCompile Include="Gameboy\GbControlManager.cpp" />
|
||||
<ClCompile Include="NES\Loaders\FdsLoader.cpp" />
|
||||
<ClCompile Include="NES\Loaders\iNesLoader.cpp" />
|
||||
|
|
@ -495,7 +491,7 @@
|
|||
<ClCompile Include="SNES\Coprocessors\SPC7110\Spc7110Decomp.cpp" />
|
||||
<ClCompile Include="SNES\Debugger\SpcDebugger.cpp" />
|
||||
<ClCompile Include="SNES\Debugger\SpcDisUtils.cpp" />
|
||||
<ClCompile Include="SNES\SpcHud.cpp" />
|
||||
<ClCompile Include="Shared\Audio\AudioPlayerHud.cpp" />
|
||||
<ClCompile Include="SNES\DSP\SPC_DSP.cpp" />
|
||||
<ClCompile Include="SNES\DSP\SPC_Filter.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@
|
|||
<ClInclude Include="Shared\InputHud.h">
|
||||
<Filter>SNES\Input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SNES\SpcHud.h">
|
||||
<ClInclude Include="Shared\Audio\AudioPlayerHud.h">
|
||||
<Filter>Misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SNES\SpcFileData.h">
|
||||
|
|
@ -765,9 +765,6 @@
|
|||
<ClInclude Include="NES\Mappers\VsSystem\VsSystem.h" />
|
||||
<ClInclude Include="NES\Mappers\VsSystem\VsInputButtons.h" />
|
||||
<ClInclude Include="NES\NesNtscFilter.h" />
|
||||
<ClInclude Include="..\..\Mesen\Core\NsfMapper.h" />
|
||||
<ClInclude Include="..\..\Mesen\Core\NsfeLoader.h" />
|
||||
<ClInclude Include="..\..\Mesen\Core\NsfLoader.h" />
|
||||
<ClInclude Include="NES\Loaders\NsfeLoader.h" />
|
||||
<ClInclude Include="NES\Loaders\NsfLoader.h" />
|
||||
<ClInclude Include="NES\Mappers\NsfMapper.h" />
|
||||
|
|
@ -782,6 +779,7 @@
|
|||
<ClInclude Include="NES\Mappers\Audio\Vrc6Pulse.h" />
|
||||
<ClInclude Include="NES\Mappers\Audio\Vrc6Saw.h" />
|
||||
<ClInclude Include="NES\NesConstants.h" />
|
||||
<ClInclude Include="Shared\Audio\AudioTrackInfo.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="SNES\Cpu.cpp">
|
||||
|
|
@ -1039,7 +1037,7 @@
|
|||
<ClCompile Include="SNES\Input\Multitap.cpp">
|
||||
<Filter>SNES\Input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SNES\SpcHud.cpp">
|
||||
<ClCompile Include="Shared\Audio\AudioPlayerHud.cpp">
|
||||
<Filter>Misc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Netplay\GameClient.cpp">
|
||||
|
|
@ -1204,8 +1202,6 @@
|
|||
<ClCompile Include="NES\Mappers\FDS\FdsInputButtons.cpp" />
|
||||
<ClCompile Include="NES\Mappers\VsSystem\VsControlManager.cpp" />
|
||||
<ClCompile Include="NES\NesNtscFilter.cpp" />
|
||||
<ClCompile Include="..\..\Mesen\Core\NsfLoader.cpp" />
|
||||
<ClCompile Include="..\..\Mesen\Core\NsfMapper.cpp" />
|
||||
<ClCompile Include="NES\Loaders\NsfLoader.cpp" />
|
||||
<ClCompile Include="NES\Mappers\NsfMapper.cpp" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -482,6 +482,16 @@ BaseVideoFilter* Gameboy::GetVideoFilter()
|
|||
}
|
||||
}
|
||||
|
||||
RomFormat Gameboy::GetRomFormat()
|
||||
{
|
||||
return RomFormat::Gb;
|
||||
}
|
||||
|
||||
AudioTrackInfo Gameboy::GetAudioTrackInfo()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
ConsoleRegion Gameboy::GetRegion()
|
||||
{
|
||||
return ConsoleRegion::Ntsc;
|
||||
|
|
|
|||
|
|
@ -120,4 +120,7 @@ public:
|
|||
uint32_t GetMasterClockRate() override;
|
||||
|
||||
BaseVideoFilter* GetVideoFilter() override;
|
||||
|
||||
RomFormat GetRomFormat() override;
|
||||
AudioTrackInfo GetAudioTrackInfo() override;
|
||||
};
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#include "NES/MapperFactory.h"
|
||||
#include "NES/GameDatabase.h"
|
||||
#include "Shared/MessageManager.h"
|
||||
#include "Shared/RomInfo.h"
|
||||
#include "Shared/EmuSettings.h"
|
||||
#include "Utilities/FolderUtilities.h"
|
||||
#include "Utilities/CRC32.h"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "NES/Loaders/NsfLoader.h"
|
||||
#include "NES/RomData.h"
|
||||
#include "NES/MapperFactory.h"
|
||||
#include "Shared/RomInfo.h"
|
||||
|
||||
void NsfLoader::Read(uint8_t *& data, uint8_t & dest)
|
||||
{
|
||||
|
|
@ -108,7 +109,6 @@ void NsfLoader::InitializeFromHeader(RomData &romData)
|
|||
|
||||
void NsfLoader::InitHeader(NsfHeader & header)
|
||||
{
|
||||
memset(&header, 0, sizeof(NsfHeader));
|
||||
for(int i = 0; i < 256; i++) {
|
||||
//Used by NSFE
|
||||
header.TrackLength[i] = -1;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <algorithm>
|
||||
#include "NES/RomData.h"
|
||||
#include "NES/Loaders/NsfLoader.h"
|
||||
#include "Shared/RomInfo.h"
|
||||
|
||||
class NsfeLoader : public NsfLoader
|
||||
{
|
||||
|
|
@ -59,11 +60,6 @@ private:
|
|||
data++;
|
||||
}
|
||||
|
||||
//truncate all strings to 255 characters + null
|
||||
for(size_t i = 0; i < strings.size(); i++) {
|
||||
strings[i] = strings[i].substr(0, std::min((int)strings[i].size(), 255));
|
||||
}
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
|
|
@ -142,13 +138,7 @@ private:
|
|||
i++;
|
||||
}
|
||||
} else if(fourCC.compare("tlbl") == 0) {
|
||||
vector<string> trackNames = ReadStrings(data, chunkEnd);
|
||||
stringstream ss;
|
||||
for(string &trackName : trackNames) {
|
||||
ss << trackName;
|
||||
ss << "[!|!]";
|
||||
}
|
||||
CopyString(header.TrackName, ss.str(), 20000);
|
||||
header.TrackNames = ReadStrings(data, chunkEnd);
|
||||
} else if(fourCC.compare("auth") == 0) {
|
||||
vector<string> infoStrings = ReadStrings(data, chunkEnd);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "NES/NesHeader.h"
|
||||
#include "NES/RomData.h"
|
||||
#include "NES/GameDatabase.h"
|
||||
#include "Shared/RomInfo.h"
|
||||
|
||||
void iNesLoader::LoadRom(RomData& romData, vector<uint8_t>& romFile, NesHeader *preloadedHeader)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -496,6 +496,18 @@ ConsoleFeatures NsfMapper::GetAvailableFeatures()
|
|||
return ConsoleFeatures::Nsf;
|
||||
}
|
||||
|
||||
AudioTrackInfo NsfMapper::GetAudioTrackInfo()
|
||||
{
|
||||
AudioTrackInfo track = {};
|
||||
track.Artist = _nsfHeader.ArtistName;
|
||||
track.Comment = _nsfHeader.RipperName;
|
||||
track.GameTitle = _nsfHeader.SongName;
|
||||
track.SongTitle = _nsfHeader.TrackNames.size() > _songNumber ? _nsfHeader.TrackNames[_songNumber] : "";
|
||||
track.Position = _console->GetPpuFrame().FrameCount / _console->GetFps();
|
||||
track.Length = _nsfHeader.TrackLength[_songNumber] / 1000;
|
||||
return track;
|
||||
}
|
||||
|
||||
void NsfMapper::Serialize(Serializer& s)
|
||||
{
|
||||
BaseMapper::Serialize(s);
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@ public:
|
|||
|
||||
ConsoleFeatures GetAvailableFeatures() override;
|
||||
|
||||
AudioTrackInfo GetAudioTrackInfo();
|
||||
|
||||
void SelectTrack(uint8_t trackNumber);
|
||||
uint8_t GetCurrentTrack();
|
||||
NsfHeader GetNsfHeader();
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@
|
|||
#include "NES/NesNtscFilter.h"
|
||||
#include "NES/NesConstants.h"
|
||||
#include "NES/Mappers/VsSystem/VsControlManager.h"
|
||||
#include "NES/Mappers/NsfMapper.h"
|
||||
#include "Shared/Emulator.h"
|
||||
#include "Shared/Audio/AudioTrackInfo.h"
|
||||
#include "Shared/Interfaces/IControlManager.h"
|
||||
#include "Shared/Interfaces/IBattery.h"
|
||||
#include "Shared/EmuSettings.h"
|
||||
|
|
@ -357,3 +359,17 @@ BaseVideoFilter* NesConsole::GetVideoFilter()
|
|||
return new NesDefaultVideoFilter(_emu);
|
||||
}
|
||||
}
|
||||
|
||||
RomFormat NesConsole::GetRomFormat()
|
||||
{
|
||||
return _mapper->GetRomInfo().Format;
|
||||
}
|
||||
|
||||
AudioTrackInfo NesConsole::GetAudioTrackInfo()
|
||||
{
|
||||
NsfMapper* nsfMapper = dynamic_cast<NsfMapper*>(_mapper.get());
|
||||
if(nsfMapper) {
|
||||
return nsfMapper->GetAudioTrackInfo();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "Shared/SettingTypes.h"
|
||||
#include "Shared/Interfaces/IConsole.h"
|
||||
#include "Shared/Audio/AudioTrackInfo.h"
|
||||
#include "Debugger/DebugTypes.h"
|
||||
|
||||
class Emulator;
|
||||
|
|
@ -91,4 +92,7 @@ public:
|
|||
void SaveBattery() override;
|
||||
|
||||
BaseVideoFilter* GetVideoFilter() override;
|
||||
|
||||
RomFormat GetRomFormat() override;
|
||||
AudioTrackInfo GetAudioTrackInfo() override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -309,16 +309,6 @@ struct HashInfo
|
|||
string PrgChrMd5;
|
||||
};
|
||||
|
||||
enum class RomFormat
|
||||
{
|
||||
Unknown = 0,
|
||||
iNes = 1,
|
||||
Unif = 2,
|
||||
Fds = 3,
|
||||
Nsf = 4,
|
||||
StudyBox = 5
|
||||
};
|
||||
|
||||
enum class VsSystemType
|
||||
{
|
||||
Default = 0,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "NesTypes.h"
|
||||
#include "NesHeader.h"
|
||||
#include "Shared/RomInfo.h"
|
||||
|
||||
enum class RomHeaderVersion
|
||||
{
|
||||
|
|
@ -13,28 +14,28 @@ enum class RomHeaderVersion
|
|||
|
||||
struct NsfHeader
|
||||
{
|
||||
char Header[5];
|
||||
uint8_t Version;
|
||||
uint8_t TotalSongs;
|
||||
uint8_t StartingSong;
|
||||
uint16_t LoadAddress;
|
||||
uint16_t InitAddress;
|
||||
uint16_t PlayAddress;
|
||||
char SongName[256];
|
||||
char ArtistName[256];
|
||||
char CopyrightHolder[256];
|
||||
uint16_t PlaySpeedNtsc;
|
||||
uint8_t BankSetup[8];
|
||||
uint16_t PlaySpeedPal;
|
||||
uint8_t Flags;
|
||||
uint8_t SoundChips;
|
||||
uint8_t Padding[4];
|
||||
char Header[5] = {};
|
||||
uint8_t Version = 0;
|
||||
uint8_t TotalSongs = 0;
|
||||
uint8_t StartingSong = 0;
|
||||
uint16_t LoadAddress = 0;
|
||||
uint16_t InitAddress = 0;
|
||||
uint16_t PlayAddress = 0;
|
||||
char SongName[256] = {};
|
||||
char ArtistName[256] = {};
|
||||
char CopyrightHolder[256] = {};
|
||||
uint16_t PlaySpeedNtsc = 0;
|
||||
uint8_t BankSetup[8] = {};
|
||||
uint16_t PlaySpeedPal = 0;
|
||||
uint8_t Flags = 0;
|
||||
uint8_t SoundChips = 0;
|
||||
uint8_t Padding[4] = {};
|
||||
|
||||
//NSFe extensions
|
||||
char RipperName[256];
|
||||
char TrackName[20000];
|
||||
int32_t TrackLength[256];
|
||||
int32_t TrackFade[256];
|
||||
char RipperName[256] = {};
|
||||
vector<string> TrackNames;
|
||||
int32_t TrackLength[256] = {};
|
||||
int32_t TrackFade[256] = {};
|
||||
};
|
||||
|
||||
struct GameInfo
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#include "SNES/BaseCartridge.h"
|
||||
#include "SNES/RamHandler.h"
|
||||
#include "SNES/CartTypes.h"
|
||||
#include "SNES/SpcHud.h"
|
||||
#include "SNES/SpcFileData.h"
|
||||
#include "SNES/SnesDefaultVideoFilter.h"
|
||||
#include "SNES/SnesNtscFilter.h"
|
||||
#include "Gameboy/Gameboy.h"
|
||||
|
|
@ -74,10 +74,6 @@ void Console::ProcessEndOfFrame()
|
|||
_cart->GetCoprocessor()->ProcessEndOfFrame();
|
||||
}
|
||||
|
||||
if(_spcHud) {
|
||||
_spcHud->Draw(_ppu->GetFrameCount());
|
||||
}
|
||||
|
||||
_emu->ProcessEndOfFrame();
|
||||
|
||||
_controlManager->UpdateControlDevices();
|
||||
|
|
@ -131,13 +127,6 @@ void Console::Reset()
|
|||
|
||||
//Reset cart before CPU to ensure correct memory mappings when fetching reset vector
|
||||
_cpu->Reset();
|
||||
|
||||
if(_cart->GetSpcData()) {
|
||||
_spc->LoadSpcFile(_cart->GetSpcData());
|
||||
_spcHud.reset(new SpcHud(_emu, _cart->GetSpcData()));
|
||||
} else {
|
||||
_spcHud.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool Console::LoadRom(VirtualFile& romFile)
|
||||
|
|
@ -160,10 +149,8 @@ bool Console::LoadRom(VirtualFile& romFile)
|
|||
_msu1.reset(Msu1::Init(romFile, _spc.get()));
|
||||
|
||||
if(_cart->GetSpcData()) {
|
||||
//TODO
|
||||
_spc->LoadSpcFile(_cart->GetSpcData());
|
||||
_spcHud.reset(new SpcHud(_emu, _cart->GetSpcData()));
|
||||
} else {
|
||||
_spcHud.reset();
|
||||
}
|
||||
|
||||
_cpu.reset(new Cpu(this));
|
||||
|
|
@ -263,6 +250,26 @@ BaseVideoFilter* Console::GetVideoFilter()
|
|||
}
|
||||
}
|
||||
|
||||
RomFormat Console::GetRomFormat()
|
||||
{
|
||||
return _cart->GetSpcData() ? RomFormat::Spc : RomFormat::Sfc;
|
||||
}
|
||||
|
||||
AudioTrackInfo Console::GetAudioTrackInfo()
|
||||
{
|
||||
AudioTrackInfo track = {};
|
||||
SpcFileData* spc = _cart->GetSpcData();
|
||||
if(spc) {
|
||||
track.Artist = spc->Artist;
|
||||
track.Comment = spc->Comment;
|
||||
track.GameTitle = spc->GameTitle;
|
||||
track.SongTitle = spc->SongTitle;
|
||||
track.Position = _ppu->GetFrameCount() / GetFps();
|
||||
track.Length = -1;
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
double Console::GetFrameDelay()
|
||||
{
|
||||
UpdateRegion();
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ class RewindManager;
|
|||
class BatteryManager;
|
||||
class CheatManager;
|
||||
class MovieManager;
|
||||
class SpcHud;
|
||||
class FrameLimiter;
|
||||
class DebugStats;
|
||||
class Msu1;
|
||||
|
|
@ -57,7 +56,6 @@ private:
|
|||
|
||||
shared_ptr<Msu1> _msu1;
|
||||
EmuSettings* _settings;
|
||||
shared_ptr<SpcHud> _spcHud;
|
||||
Emulator* _emu;
|
||||
|
||||
uint32_t _masterClockRate;
|
||||
|
|
@ -117,4 +115,7 @@ public:
|
|||
void SaveBattery() override;
|
||||
|
||||
BaseVideoFilter* GetVideoFilter() override;
|
||||
|
||||
RomFormat GetRomFormat() override;
|
||||
AudioTrackInfo GetAudioTrackInfo() override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "SNES/SpcHud.h"
|
||||
#include "SNES/SpcFileData.h"
|
||||
#include "Shared/Emulator.h"
|
||||
#include "Shared/Audio/SoundMixer.h"
|
||||
#include "Shared/Video/DebugHud.h"
|
||||
|
||||
SpcHud::SpcHud(Emulator* emu, SpcFileData* spcData)
|
||||
{
|
||||
_mixer = emu->GetSoundMixer().get();
|
||||
_hud = emu->GetDebugHud().get();
|
||||
_spcData = spcData;
|
||||
}
|
||||
|
||||
void SpcHud::Draw(uint32_t frame)
|
||||
{
|
||||
_hud->DrawString(20, 20, "Game:", 0xBBBBBB, 0, 1, frame);
|
||||
_hud->DrawString(20, 30, "Track:", 0xBBBBBB, 0, 1, frame);
|
||||
_hud->DrawString(20, 40, "Artist:", 0xBBBBBB, 0, 1, frame);
|
||||
_hud->DrawString(20, 50, "Comment:", 0xBBBBBB, 0, 1, frame);
|
||||
|
||||
_hud->DrawString(70, 20, _spcData->GameTitle, 0xFFFFFF, 0, 1, frame);
|
||||
_hud->DrawString(70, 30, _spcData->SongTitle, 0xFFFFFF, 0, 1, frame);
|
||||
_hud->DrawString(70, 40, _spcData->Artist, 0xFFFFFF, 0, 1, frame);
|
||||
_hud->DrawString(70, 50, _spcData->Comment, 0xFFFFFF, 0, 1, frame);
|
||||
|
||||
int16_t left, right;
|
||||
_mixer->GetLastSamples(left, right);
|
||||
_volumesL[_volPosition] = left / 128;
|
||||
_volumesR[_volPosition] = right / 128;
|
||||
_volPosition = (_volPosition + 1) & 0x7F;
|
||||
for(int i = 1; i < 128; i++) {
|
||||
_hud->DrawLine((i - 1)*2, 160 + _volumesL[(_volPosition + i - 1) & 0x7F], i*2, 160 + _volumesL[(_volPosition + i) & 0x7F], 0x30FFAAAA, 1, frame);
|
||||
_hud->DrawLine((i - 1)*2, 160 + _volumesR[(_volPosition + i - 1) & 0x7F], i*2, 160 + _volumesR[(_volPosition + i) & 0x7F], 0x30AAAAFF, 1, frame);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
class Emulator;
|
||||
class SoundMixer;
|
||||
class DebugHud;
|
||||
class SpcFileData;
|
||||
|
||||
class SpcHud
|
||||
{
|
||||
private:
|
||||
DebugHud* _hud;
|
||||
SoundMixer* _mixer;
|
||||
|
||||
int8_t _volumesL[128] = {};
|
||||
int8_t _volumesR[128] = {};
|
||||
uint8_t _volPosition = 0;
|
||||
|
||||
SpcFileData* _spcData;
|
||||
|
||||
public:
|
||||
SpcHud(Emulator* emu, SpcFileData* spcData);
|
||||
|
||||
void Draw(uint32_t frame);
|
||||
};
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
#include "stdafx.h"
|
||||
#include "Shared/Audio/AudioPlayerHud.h"
|
||||
#include "Shared/Audio/SoundMixer.h"
|
||||
#include "Shared/Video/DebugHud.h"
|
||||
#include "Shared/Emulator.h"
|
||||
|
||||
static constexpr double PI = 3.14159265358979323846;
|
||||
|
||||
AudioPlayerHud::AudioPlayerHud(Emulator* emu)
|
||||
{
|
||||
_emu = emu;
|
||||
_mixer = emu->GetSoundMixer().get();
|
||||
_hud = emu->GetDebugHud().get();
|
||||
|
||||
for(int i = 0; i < N; i++) {
|
||||
_hannWindow[i] = 0.5f * (1.0f - cos(2.0f * PI * (float)(i) / (float)(N - 1.0f)));
|
||||
}
|
||||
}
|
||||
|
||||
string AudioPlayerHud::FormatSeconds(uint32_t s)
|
||||
{
|
||||
string seconds = std::to_string(s % 60);
|
||||
if(seconds.size() == 1) {
|
||||
seconds = "0" + seconds;
|
||||
}
|
||||
return std::to_string(s / 60) + ":" + seconds;
|
||||
}
|
||||
|
||||
void AudioPlayerHud::Draw()
|
||||
{
|
||||
AudioTrackInfo trackInfo = _emu->GetAudioTrackInfo();
|
||||
|
||||
_hud->DrawRectangle(0, 0, 256, 240, 0, true, 1);
|
||||
|
||||
int y = 20;
|
||||
auto drawLabel = [=, &y](string label, string content) {
|
||||
if(content.size() > 0 && content[0] != 0) {
|
||||
_hud->DrawString(20, y, label, 0xBBBBBB, 0, 1);
|
||||
_hud->DrawString(70, y, content, 0xFFFFFF, 0, 1);
|
||||
y += 10;
|
||||
}
|
||||
};
|
||||
|
||||
drawLabel("Game:", trackInfo.GameTitle);
|
||||
drawLabel("Track:", trackInfo.SongTitle);
|
||||
drawLabel("Artist:", trackInfo.Artist);
|
||||
drawLabel("Comment:", trackInfo.Comment);
|
||||
|
||||
string position = FormatSeconds((uint32_t)trackInfo.Position);
|
||||
if(trackInfo.Length <= 0) {
|
||||
_hud->DrawString(220, 218, position, 0xFFFFFF, 0, 1);
|
||||
} else {
|
||||
position += " / " + FormatSeconds(trackInfo.Length);
|
||||
_hud->DrawString(182, 218, position, 0xFFFFFF, 0, 1);
|
||||
|
||||
_hud->DrawRectangle(15, 209, 226, 6, 0xFFFFFF, false, 1);
|
||||
_hud->DrawRectangle(16, 210, 224, 4, 0, true, 1);
|
||||
_hud->DrawRectangle(17, 211, (int)(trackInfo.Position / trackInfo.Length * 222), 2, 0x44FF88, false, 1);
|
||||
}
|
||||
|
||||
//Arbitrary ranges to split the graph into (8 equally sized sections on the screen that contain a specific freq range)
|
||||
static constexpr double ranges[8][3] {
|
||||
{ 20, 150, 0.5 },
|
||||
{ 150, 400, 0.5 },
|
||||
{ 400, 700, 0.75 },
|
||||
{ 700, 1000, 0.75 },
|
||||
{ 1000, 2000, 1 },
|
||||
{ 2000, 4000, 1 },
|
||||
{ 4000, 6000, 1.25 },
|
||||
{ 6000, 20000, 1.25 }
|
||||
};
|
||||
|
||||
static constexpr int maxVal = 140;
|
||||
|
||||
if(_amplitudes.size() >= N / 2) {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for(int j = 0; j < 32; j++) {
|
||||
double freqRange = ranges[i][1] - ranges[i][0];
|
||||
double startFreq = ranges[i][0] + freqRange * j / 32;
|
||||
double endFreq = ranges[i][0] + freqRange * (j + 1) / 32;
|
||||
|
||||
int startIndex = (int)(startFreq / (_sampleRate / N));
|
||||
int endIndex = (int)(endFreq / (_sampleRate / N));
|
||||
|
||||
double avgAmp = 0;
|
||||
for(int j = startIndex; j <= endIndex && j < _amplitudes.size(); j++) {
|
||||
avgAmp += _amplitudes[j];
|
||||
}
|
||||
avgAmp /= (endIndex - startIndex + 1);
|
||||
avgAmp *= ranges[i][2];
|
||||
avgAmp = std::min<double>(maxVal, avgAmp);
|
||||
|
||||
int red = std::min(255, (int)(256 * (avgAmp / maxVal) * 2));
|
||||
int green = std::max(0, std::min(255, (int)(256 * ((maxVal - avgAmp) / maxVal) * 2)));
|
||||
_hud->DrawRectangle(i*32+j, 200, 1, (int)-avgAmp, red << 16 | green << 8, true, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayerHud::ProcessSamples(int16_t* samples, size_t sampleCount, uint32_t sampleRate)
|
||||
{
|
||||
_sampleRate = sampleRate;
|
||||
for(int i = 0; i < sampleCount; i++) {
|
||||
_samples.push_back((samples[i * 2] + samples[i * 2 + 1]) / 2);
|
||||
if(_samples.size() > N) {
|
||||
_samples.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
if(_samples.size() >= N) {
|
||||
for(int i = 0; i < N; i++) {
|
||||
_input[i] = _samples[i] * _hannWindow[i];
|
||||
}
|
||||
|
||||
_fft.transform_real(_input, _out);
|
||||
|
||||
_amplitudes.clear();
|
||||
for(int i = 0; i < N / 2; i++) {
|
||||
std::complex<double> c = _out[i];
|
||||
double amp = sqrt(c.real() * c.real() + c.imag() * c.imag());
|
||||
_amplitudes.push_back(amp / N);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <complex>
|
||||
#include "Utilities/kissfft.h"
|
||||
|
||||
class Emulator;
|
||||
class SoundMixer;
|
||||
class DebugHud;
|
||||
|
||||
class AudioPlayerHud
|
||||
{
|
||||
private:
|
||||
static constexpr int N = 2048*4;
|
||||
|
||||
Emulator* _emu = nullptr;
|
||||
DebugHud* _hud = nullptr;
|
||||
SoundMixer* _mixer = nullptr;
|
||||
|
||||
kissfft<double> _fft = kissfft<double>(N / 2, false);
|
||||
std::vector<double> _amplitudes;
|
||||
std::deque<int16_t> _samples;
|
||||
|
||||
uint32_t _sampleRate;
|
||||
double _hannWindow[N] = {};
|
||||
double _input[N] = {};
|
||||
std::complex<double> _out[N] = {};
|
||||
|
||||
string FormatSeconds(uint32_t s);
|
||||
|
||||
public:
|
||||
AudioPlayerHud(Emulator* emu);
|
||||
|
||||
void Draw();
|
||||
void ProcessSamples(int16_t* samples, size_t sampleCount, uint32_t sampleRate);
|
||||
};
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
struct AudioTrackInfo
|
||||
{
|
||||
string GameTitle;
|
||||
string SongTitle;
|
||||
string Artist;
|
||||
string Comment;
|
||||
|
||||
double Position;
|
||||
int32_t Length;
|
||||
};
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#include "stdafx.h"
|
||||
#include "Shared/Audio/SoundMixer.h"
|
||||
#include "Shared/Audio/AudioPlayerHud.h"
|
||||
#include "Shared/Emulator.h"
|
||||
#include "Shared/EmuSettings.h"
|
||||
#include "Shared/Audio/SoundResampler.h"
|
||||
|
|
@ -92,6 +93,10 @@ void SoundMixer::PlayAudioBuffer(int16_t* samples, uint32_t sampleCount, uint32_
|
|||
ProcessEqualizer(out, count, targetRate);
|
||||
}
|
||||
|
||||
if(_emu->GetAudioPlayerHud()) {
|
||||
_emu->GetAudioPlayerHud()->ProcessSamples(out, count, targetRate);
|
||||
}
|
||||
|
||||
if(cfg.ReverbEnabled) {
|
||||
if(cfg.ReverbStrength > 0) {
|
||||
_reverbFilter->ApplyFilter(out, count, cfg.SampleRate, cfg.ReverbStrength / 10.0, cfg.ReverbDelay / 10.0);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "Shared/Emulator.h"
|
||||
#include "Shared/NotificationManager.h"
|
||||
#include "Shared/Audio/SoundMixer.h"
|
||||
#include "Shared/Audio/AudioPlayerHud.h"
|
||||
#include "Shared/Video/VideoDecoder.h"
|
||||
#include "Shared/Video/VideoRenderer.h"
|
||||
#include "Shared/Video/DebugHud.h"
|
||||
|
|
@ -194,6 +195,10 @@ void Emulator::ProcessEndOfFrame()
|
|||
{
|
||||
#ifndef LIBRETRO
|
||||
if(!_isRunAheadFrame) {
|
||||
if(_audioPlayerHud) {
|
||||
_audioPlayerHud->Draw();
|
||||
}
|
||||
|
||||
_frameLimiter->ProcessFrame();
|
||||
while(_frameLimiter->WaitForNextFrame()) {
|
||||
if(_stopFlag || _frameDelay != GetFrameDelay() || _paused || _pauseOnNextFrame || _lockCounter > 0) {
|
||||
|
|
@ -332,7 +337,6 @@ bool Emulator::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom,
|
|||
}
|
||||
|
||||
if(patchFile.IsValid()) {
|
||||
_patchFile = patchFile;
|
||||
if(romFile.ApplyPatch(patchFile)) {
|
||||
MessageManager::DisplayMessage("Patch", "ApplyingPatch", patchFile.GetFileName());
|
||||
}
|
||||
|
|
@ -367,8 +371,17 @@ bool Emulator::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom,
|
|||
}
|
||||
}
|
||||
|
||||
_romFile = romFile;
|
||||
_patchFile = patchFile;
|
||||
_rom.RomFile = romFile;
|
||||
if(patchFile.IsValid()) {
|
||||
_rom.PatchFile = patchFile;
|
||||
}
|
||||
_rom.Format = console->GetRomFormat();
|
||||
|
||||
if(_rom.Format == RomFormat::Spc || _rom.Format == RomFormat::Nsf || _rom.Format == RomFormat::Gbs) {
|
||||
_audioPlayerHud.reset(new AudioPlayerHud(this));
|
||||
} else {
|
||||
_audioPlayerHud.reset();
|
||||
}
|
||||
|
||||
bool debuggerActive = _debugger != nullptr;
|
||||
if(stopRom) {
|
||||
|
|
@ -425,10 +438,7 @@ bool Emulator::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom,
|
|||
|
||||
RomInfo Emulator::GetRomInfo()
|
||||
{
|
||||
RomInfo romInfo = {};
|
||||
romInfo.RomFile = _romFile;
|
||||
romInfo.PatchFile = _patchFile;
|
||||
return romInfo;
|
||||
return _rom;
|
||||
}
|
||||
|
||||
string Emulator::GetHash(HashType type)
|
||||
|
|
@ -793,6 +803,16 @@ ConsoleMemoryInfo Emulator::GetMemory(SnesMemoryType type)
|
|||
return _consoleMemory[(int)type];
|
||||
}
|
||||
|
||||
AudioTrackInfo Emulator::GetAudioTrackInfo()
|
||||
{
|
||||
return _console->GetAudioTrackInfo();
|
||||
}
|
||||
|
||||
AudioPlayerHud* Emulator::GetAudioPlayerHud()
|
||||
{
|
||||
return _audioPlayerHud.get();
|
||||
}
|
||||
|
||||
bool Emulator::IsRunning()
|
||||
{
|
||||
return _console != nullptr;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "Core/Debugger/Debugger.h"
|
||||
#include "Core/Shared/EmulatorLock.h"
|
||||
#include "Core/Shared/Interfaces/IConsole.h"
|
||||
#include "Core/Shared/Audio/AudioTrackInfo.h"
|
||||
#include "Utilities/Timer.h"
|
||||
#include "Utilities/SimpleLock.h"
|
||||
#include "Utilities/VirtualFile.h"
|
||||
|
|
@ -26,6 +27,7 @@ class IControlManager;
|
|||
class VirtualFile;
|
||||
class BaseVideoFilter;
|
||||
class SystemActionManager;
|
||||
class AudioPlayerHud;
|
||||
|
||||
struct RomInfo;
|
||||
|
||||
|
|
@ -64,6 +66,8 @@ private:
|
|||
|
||||
shared_ptr<IConsole> _console;
|
||||
|
||||
unique_ptr<AudioPlayerHud> _audioPlayerHud;
|
||||
|
||||
thread::id _emulationThreadId;
|
||||
|
||||
atomic<uint32_t> _lockCounter;
|
||||
|
|
@ -82,8 +86,7 @@ private:
|
|||
atomic<bool> _isRunAheadFrame;
|
||||
bool _frameRunning = false;
|
||||
|
||||
string _romFile;
|
||||
string _patchFile;
|
||||
RomInfo _rom;
|
||||
|
||||
ConsoleMemoryInfo _consoleMemory[(int)SnesMemoryType::Register] = {};
|
||||
|
||||
|
|
@ -167,6 +170,9 @@ public:
|
|||
void RegisterMemory(SnesMemoryType type, void* memory, uint32_t size);
|
||||
ConsoleMemoryInfo GetMemory(SnesMemoryType type);
|
||||
|
||||
AudioTrackInfo GetAudioTrackInfo();
|
||||
AudioPlayerHud* GetAudioPlayerHud();
|
||||
|
||||
bool IsRunning();
|
||||
bool IsRunAheadFrame();
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "Utilities/ISerializable.h"
|
||||
#include "Core/Debugger/DebugTypes.h"
|
||||
#include "Shared/Audio/AudioTrackInfo.h"
|
||||
#include "Shared/RomInfo.h"
|
||||
|
||||
class IControlManager;
|
||||
|
|
@ -53,6 +54,9 @@ public:
|
|||
|
||||
virtual PpuFrameInfo GetPpuFrame() = 0;
|
||||
|
||||
virtual RomFormat GetRomFormat() = 0;
|
||||
virtual AudioTrackInfo GetAudioTrackInfo() = 0;
|
||||
|
||||
virtual AddressInfo GetAbsoluteAddress(AddressInfo relAddress) = 0;
|
||||
virtual AddressInfo GetRelativeAddress(AddressInfo absAddress, CpuType cpuType) = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,28 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "Utilities/VirtualFile.h"
|
||||
#include "Utilities/VirtualFile.h"
|
||||
|
||||
enum class RomFormat
|
||||
{
|
||||
Unknown,
|
||||
|
||||
Sfc,
|
||||
SfcWithCopierHeader,
|
||||
Spc,
|
||||
|
||||
Gb,
|
||||
Gbs,
|
||||
|
||||
iNes,
|
||||
Unif,
|
||||
Fds,
|
||||
Nsf,
|
||||
StudyBox
|
||||
};
|
||||
|
||||
struct RomInfo
|
||||
{
|
||||
VirtualFile RomFile;
|
||||
VirtualFile PatchFile;
|
||||
RomFormat Format;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ public:
|
|||
void Draw(uint32_t* argbBuffer, OverscanDimensions overscan, uint32_t width, uint32_t frameNumber);
|
||||
void ClearScreen();
|
||||
|
||||
void DrawPixel(int x, int y, int color, int frameCount, int startFrame);
|
||||
void DrawLine(int x, int y, int x2, int y2, int color, int frameCount, int startFrame);
|
||||
void DrawRectangle(int x, int y, int width, int height, int color, bool fill, int frameCount, int startFrame);
|
||||
void DrawScreenBuffer(uint32_t* screenBuffer, int startFrame);
|
||||
void DrawString(int x, int y, string text, int color, int backColor, int frameCount, int startFrame);
|
||||
void DrawPixel(int x, int y, int color, int frameCount, int startFrame = -1);
|
||||
void DrawLine(int x, int y, int x2, int y2, int color, int frameCount, int startFrame = -1);
|
||||
void DrawRectangle(int x, int y, int width, int height, int color, bool fill, int frameCount, int startFrame = -1);
|
||||
void DrawScreenBuffer(uint32_t* screenBuffer, int startFrame = -1);
|
||||
void DrawString(int x, int y, string text, int color, int backColor, int frameCount, int startFrame = -1);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ private:
|
|||
uint32_t* _argbBuffer;
|
||||
OverscanDimensions _overscan;
|
||||
uint32_t _lineWidth;
|
||||
uint32_t _startFrame;
|
||||
int32_t _startFrame;
|
||||
|
||||
protected:
|
||||
bool _useIntegerScaling;
|
||||
|
|
@ -76,6 +76,11 @@ public:
|
|||
|
||||
void Draw(uint32_t* argbBuffer, OverscanDimensions &overscan, uint32_t lineWidth, uint32_t frameNumber)
|
||||
{
|
||||
if(_startFrame < 0) {
|
||||
//When no start frame was specified, start on the next drawn frame
|
||||
_startFrame = frameNumber;
|
||||
}
|
||||
|
||||
if(_startFrame <= frameNumber) {
|
||||
_argbBuffer = argbBuffer;
|
||||
_overscan = overscan;
|
||||
|
|
|
|||
|
|
@ -444,6 +444,7 @@
|
|||
<ClInclude Include="Base64.h" />
|
||||
<ClInclude Include="CRC32.h" />
|
||||
<ClInclude Include="FastString.h" />
|
||||
<ClInclude Include="kissfft.h" />
|
||||
<ClInclude Include="FolderUtilities.h" />
|
||||
<ClInclude Include="HexUtilities.h" />
|
||||
<ClInclude Include="HQX\common.h" />
|
||||
|
|
|
|||
|
|
@ -223,6 +223,9 @@
|
|||
<ClInclude Include="NTSC\snes_ntsc_impl.h">
|
||||
<Filter>Misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="kissfft.h">
|
||||
<Filter>Misc</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="xBRZ\xbrz.cpp">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2010, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#ifndef KISSFFT_CLASS_HH
|
||||
#define KISSFFT_CLASS_HH
|
||||
#include <complex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
||||
template <typename scalar_t>
|
||||
class kissfft
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::complex<scalar_t> cpx_t;
|
||||
|
||||
kissfft( const std::size_t nfft,
|
||||
const bool inverse )
|
||||
:_nfft(nfft)
|
||||
,_inverse(inverse)
|
||||
{
|
||||
// fill twiddle factors
|
||||
_twiddles.resize(_nfft);
|
||||
const scalar_t phinc = (_inverse?2:-2)* std::acos( (scalar_t) -1) / _nfft;
|
||||
for (std::size_t i=0;i<_nfft;++i)
|
||||
_twiddles[i] = std::exp( cpx_t(0,i*phinc) );
|
||||
|
||||
//factorize
|
||||
//start factoring out 4's, then 2's, then 3,5,7,9,...
|
||||
std::size_t n= _nfft;
|
||||
std::size_t p=4;
|
||||
do {
|
||||
while (n % p) {
|
||||
switch (p) {
|
||||
case 4: p = 2; break;
|
||||
case 2: p = 3; break;
|
||||
default: p += 2; break;
|
||||
}
|
||||
if (p*p>n)
|
||||
p = n;// no more factors
|
||||
}
|
||||
n /= p;
|
||||
_stageRadix.push_back(p);
|
||||
_stageRemainder.push_back(n);
|
||||
}while(n>1);
|
||||
}
|
||||
|
||||
|
||||
/// Changes the FFT-length and/or the transform direction.
|
||||
///
|
||||
/// @post The @c kissfft object will be in the same state as if it
|
||||
/// had been newly constructed with the passed arguments.
|
||||
/// However, the implementation may be faster than constructing a
|
||||
/// new fft object.
|
||||
void assign( const std::size_t nfft,
|
||||
const bool inverse )
|
||||
{
|
||||
if ( nfft != _nfft )
|
||||
{
|
||||
kissfft tmp( nfft, inverse ); // O(n) time.
|
||||
std::swap( tmp, *this ); // this is O(1) in C++11, O(n) otherwise.
|
||||
}
|
||||
else if ( inverse != _inverse )
|
||||
{
|
||||
// conjugate the twiddle factors.
|
||||
for ( typename std::vector<cpx_t>::iterator it = _twiddles.begin();
|
||||
it != _twiddles.end(); ++it )
|
||||
it->imag( -it->imag() );
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the complex Discrete Fourier Transform.
|
||||
///
|
||||
/// The size of the passed arrays must be passed in the constructor.
|
||||
/// The sum of the squares of the absolute values in the @c dst
|
||||
/// array will be @c N times the sum of the squares of the absolute
|
||||
/// values in the @c src array, where @c N is the size of the array.
|
||||
/// In other words, the l_2 norm of the resulting array will be
|
||||
/// @c sqrt(N) times as big as the l_2 norm of the input array.
|
||||
/// This is also the case when the inverse flag is set in the
|
||||
/// constructor. Hence when applying the same transform twice, but with
|
||||
/// the inverse flag changed the second time, then the result will
|
||||
/// be equal to the original input times @c N.
|
||||
void transform(const cpx_t * fft_in, cpx_t * fft_out, const std::size_t stage = 0, const std::size_t fstride = 1, const std::size_t in_stride = 1) const
|
||||
{
|
||||
const std::size_t p = _stageRadix[stage];
|
||||
const std::size_t m = _stageRemainder[stage];
|
||||
cpx_t * const Fout_beg = fft_out;
|
||||
cpx_t * const Fout_end = fft_out + p*m;
|
||||
|
||||
if (m==1) {
|
||||
do{
|
||||
*fft_out = *fft_in;
|
||||
fft_in += fstride*in_stride;
|
||||
}while(++fft_out != Fout_end );
|
||||
}else{
|
||||
do{
|
||||
// recursive call:
|
||||
// DFT of size m*p performed by doing
|
||||
// p instances of smaller DFTs of size m,
|
||||
// each one takes a decimated version of the input
|
||||
transform(fft_in, fft_out, stage+1, fstride*p,in_stride);
|
||||
fft_in += fstride*in_stride;
|
||||
}while( (fft_out += m) != Fout_end );
|
||||
}
|
||||
|
||||
fft_out=Fout_beg;
|
||||
|
||||
// recombine the p smaller DFTs
|
||||
switch (p) {
|
||||
case 2: kf_bfly2(fft_out,fstride,m); break;
|
||||
case 3: kf_bfly3(fft_out,fstride,m); break;
|
||||
case 4: kf_bfly4(fft_out,fstride,m); break;
|
||||
case 5: kf_bfly5(fft_out,fstride,m); break;
|
||||
default: kf_bfly_generic(fft_out,fstride,m,p); break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the Discrete Fourier Transform (DFT) of a real input
|
||||
/// of size @c 2*N.
|
||||
///
|
||||
/// The 0-th and N-th value of the DFT are real numbers. These are
|
||||
/// stored in @c dst[0].real() and @c dst[0].imag() respectively.
|
||||
/// The remaining DFT values up to the index N-1 are stored in
|
||||
/// @c dst[1] to @c dst[N-1].
|
||||
/// The other half of the DFT values can be calculated from the
|
||||
/// symmetry relation
|
||||
/// @code
|
||||
/// DFT(src)[2*N-k] == conj( DFT(src)[k] );
|
||||
/// @endcode
|
||||
/// The same scaling factors as in @c transform() apply.
|
||||
///
|
||||
/// @note For this to work, the types @c scalar_t and @c cpx_t
|
||||
/// must fulfill the following requirements:
|
||||
///
|
||||
/// For any object @c z of type @c cpx_t,
|
||||
/// @c reinterpret_cast<scalar_t(&)[2]>(z)[0] is the real part of @c z and
|
||||
/// @c reinterpret_cast<scalar_t(&)[2]>(z)[1] is the imaginary part of @c z.
|
||||
/// For any pointer to an element of an array of @c cpx_t named @c p
|
||||
/// and any valid array index @c i, @c reinterpret_cast<T*>(p)[2*i]
|
||||
/// is the real part of the complex number @c p[i], and
|
||||
/// @c reinterpret_cast<T*>(p)[2*i+1] is the imaginary part of the
|
||||
/// complex number @c p[i].
|
||||
///
|
||||
/// Since C++11, these requirements are guaranteed to be satisfied for
|
||||
/// @c scalar_ts being @c float, @c double or @c long @c double
|
||||
/// together with @c cpx_t being @c std::complex<scalar_t>.
|
||||
void transform_real( const scalar_t * const src,
|
||||
cpx_t * const dst ) const
|
||||
{
|
||||
const std::size_t N = _nfft;
|
||||
if ( N == 0 )
|
||||
return;
|
||||
|
||||
// perform complex FFT
|
||||
transform( reinterpret_cast<const cpx_t*>(src), dst );
|
||||
|
||||
// post processing for k = 0 and k = N
|
||||
dst[0] = cpx_t( dst[0].real() + dst[0].imag(),
|
||||
dst[0].real() - dst[0].imag() );
|
||||
|
||||
// post processing for all the other k = 1, 2, ..., N-1
|
||||
const scalar_t pi = std::acos( (scalar_t) -1);
|
||||
const scalar_t half_phi_inc = ( _inverse ? pi : -pi ) / N;
|
||||
const cpx_t twiddle_mul = std::exp( cpx_t(0, half_phi_inc) );
|
||||
for ( std::size_t k = 1; 2*k < N; ++k )
|
||||
{
|
||||
const cpx_t w = (scalar_t)0.5 * cpx_t(
|
||||
dst[k].real() + dst[N-k].real(),
|
||||
dst[k].imag() - dst[N-k].imag() );
|
||||
const cpx_t z = (scalar_t)0.5 * cpx_t(
|
||||
dst[k].imag() + dst[N-k].imag(),
|
||||
-dst[k].real() + dst[N-k].real() );
|
||||
const cpx_t twiddle =
|
||||
k % 2 == 0 ?
|
||||
_twiddles[k/2] :
|
||||
_twiddles[k/2] * twiddle_mul;
|
||||
dst[ k] = w + twiddle * z;
|
||||
dst[N-k] = std::conj( w - twiddle * z );
|
||||
}
|
||||
if ( N % 2 == 0 )
|
||||
dst[N/2] = std::conj( dst[N/2] );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void kf_bfly2( cpx_t * Fout, const size_t fstride, const std::size_t m) const
|
||||
{
|
||||
for (std::size_t k=0;k<m;++k) {
|
||||
const cpx_t t = Fout[m+k] * _twiddles[k*fstride];
|
||||
Fout[m+k] = Fout[k] - t;
|
||||
Fout[k] += t;
|
||||
}
|
||||
}
|
||||
|
||||
void kf_bfly3( cpx_t * Fout, const std::size_t fstride, const std::size_t m) const
|
||||
{
|
||||
std::size_t k=m;
|
||||
const std::size_t m2 = 2*m;
|
||||
const cpx_t *tw1,*tw2;
|
||||
cpx_t scratch[5];
|
||||
const cpx_t epi3 = _twiddles[fstride*m];
|
||||
|
||||
tw1=tw2=&_twiddles[0];
|
||||
|
||||
do{
|
||||
scratch[1] = Fout[m] * *tw1;
|
||||
scratch[2] = Fout[m2] * *tw2;
|
||||
|
||||
scratch[3] = scratch[1] + scratch[2];
|
||||
scratch[0] = scratch[1] - scratch[2];
|
||||
tw1 += fstride;
|
||||
tw2 += fstride*2;
|
||||
|
||||
Fout[m] = Fout[0] - scratch[3]*scalar_t(0.5);
|
||||
scratch[0] *= epi3.imag();
|
||||
|
||||
Fout[0] += scratch[3];
|
||||
|
||||
Fout[m2] = cpx_t( Fout[m].real() + scratch[0].imag() , Fout[m].imag() - scratch[0].real() );
|
||||
|
||||
Fout[m] += cpx_t( -scratch[0].imag(),scratch[0].real() );
|
||||
++Fout;
|
||||
}while(--k);
|
||||
}
|
||||
|
||||
void kf_bfly4( cpx_t * const Fout, const std::size_t fstride, const std::size_t m) const
|
||||
{
|
||||
cpx_t scratch[7];
|
||||
const scalar_t negative_if_inverse = _inverse ? -1 : +1;
|
||||
for (std::size_t k=0;k<m;++k) {
|
||||
scratch[0] = Fout[k+ m] * _twiddles[k*fstride ];
|
||||
scratch[1] = Fout[k+2*m] * _twiddles[k*fstride*2];
|
||||
scratch[2] = Fout[k+3*m] * _twiddles[k*fstride*3];
|
||||
scratch[5] = Fout[k] - scratch[1];
|
||||
|
||||
Fout[k] += scratch[1];
|
||||
scratch[3] = scratch[0] + scratch[2];
|
||||
scratch[4] = scratch[0] - scratch[2];
|
||||
scratch[4] = cpx_t( scratch[4].imag()*negative_if_inverse ,
|
||||
-scratch[4].real()*negative_if_inverse );
|
||||
|
||||
Fout[k+2*m] = Fout[k] - scratch[3];
|
||||
Fout[k ]+= scratch[3];
|
||||
Fout[k+ m] = scratch[5] + scratch[4];
|
||||
Fout[k+3*m] = scratch[5] - scratch[4];
|
||||
}
|
||||
}
|
||||
|
||||
void kf_bfly5( cpx_t * const Fout, const std::size_t fstride, const std::size_t m) const
|
||||
{
|
||||
cpx_t *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
|
||||
cpx_t scratch[13];
|
||||
const cpx_t ya = _twiddles[fstride*m];
|
||||
const cpx_t yb = _twiddles[fstride*2*m];
|
||||
|
||||
Fout0=Fout;
|
||||
Fout1=Fout0+m;
|
||||
Fout2=Fout0+2*m;
|
||||
Fout3=Fout0+3*m;
|
||||
Fout4=Fout0+4*m;
|
||||
|
||||
for ( std::size_t u=0; u<m; ++u ) {
|
||||
scratch[0] = *Fout0;
|
||||
|
||||
scratch[1] = *Fout1 * _twiddles[ u*fstride];
|
||||
scratch[2] = *Fout2 * _twiddles[2*u*fstride];
|
||||
scratch[3] = *Fout3 * _twiddles[3*u*fstride];
|
||||
scratch[4] = *Fout4 * _twiddles[4*u*fstride];
|
||||
|
||||
scratch[7] = scratch[1] + scratch[4];
|
||||
scratch[10]= scratch[1] - scratch[4];
|
||||
scratch[8] = scratch[2] + scratch[3];
|
||||
scratch[9] = scratch[2] - scratch[3];
|
||||
|
||||
*Fout0 += scratch[7];
|
||||
*Fout0 += scratch[8];
|
||||
|
||||
scratch[5] = scratch[0] + cpx_t(
|
||||
scratch[7].real()*ya.real() + scratch[8].real()*yb.real(),
|
||||
scratch[7].imag()*ya.real() + scratch[8].imag()*yb.real()
|
||||
);
|
||||
|
||||
scratch[6] = cpx_t(
|
||||
scratch[10].imag()*ya.imag() + scratch[9].imag()*yb.imag(),
|
||||
-scratch[10].real()*ya.imag() - scratch[9].real()*yb.imag()
|
||||
);
|
||||
|
||||
*Fout1 = scratch[5] - scratch[6];
|
||||
*Fout4 = scratch[5] + scratch[6];
|
||||
|
||||
scratch[11] = scratch[0] +
|
||||
cpx_t(
|
||||
scratch[7].real()*yb.real() + scratch[8].real()*ya.real(),
|
||||
scratch[7].imag()*yb.real() + scratch[8].imag()*ya.real()
|
||||
);
|
||||
|
||||
scratch[12] = cpx_t(
|
||||
-scratch[10].imag()*yb.imag() + scratch[9].imag()*ya.imag(),
|
||||
scratch[10].real()*yb.imag() - scratch[9].real()*ya.imag()
|
||||
);
|
||||
|
||||
*Fout2 = scratch[11] + scratch[12];
|
||||
*Fout3 = scratch[11] - scratch[12];
|
||||
|
||||
++Fout0;
|
||||
++Fout1;
|
||||
++Fout2;
|
||||
++Fout3;
|
||||
++Fout4;
|
||||
}
|
||||
}
|
||||
|
||||
/* perform the butterfly for one stage of a mixed radix FFT */
|
||||
void kf_bfly_generic(
|
||||
cpx_t * const Fout,
|
||||
const size_t fstride,
|
||||
const std::size_t m,
|
||||
const std::size_t p
|
||||
) const
|
||||
{
|
||||
const cpx_t * twiddles = &_twiddles[0];
|
||||
|
||||
if(p > _scratchbuf.size()) _scratchbuf.resize(p);
|
||||
|
||||
for ( std::size_t u=0; u<m; ++u ) {
|
||||
std::size_t k = u;
|
||||
for ( std::size_t q1=0 ; q1<p ; ++q1 ) {
|
||||
_scratchbuf[q1] = Fout[ k ];
|
||||
k += m;
|
||||
}
|
||||
|
||||
k=u;
|
||||
for ( std::size_t q1=0 ; q1<p ; ++q1 ) {
|
||||
std::size_t twidx=0;
|
||||
Fout[ k ] = _scratchbuf[0];
|
||||
for ( std::size_t q=1;q<p;++q ) {
|
||||
twidx += fstride * k;
|
||||
if (twidx>=_nfft)
|
||||
twidx-=_nfft;
|
||||
Fout[ k ] += _scratchbuf[q] * twiddles[twidx];
|
||||
}
|
||||
k += m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t _nfft;
|
||||
bool _inverse;
|
||||
std::vector<cpx_t> _twiddles;
|
||||
std::vector<std::size_t> _stageRadix;
|
||||
std::vector<std::size_t> _stageRemainder;
|
||||
mutable std::vector<cpx_t> _scratchbuf;
|
||||
};
|
||||
#endif
|
||||
Loading…
Reference in New Issue