HD Packs: Allow replacing game audio
This commit is contained in:
parent
b5ab77d547
commit
a562c71094
21 changed files with 6046 additions and 16 deletions
|
@ -29,6 +29,7 @@
|
||||||
#include "RewindManager.h"
|
#include "RewindManager.h"
|
||||||
#include "SaveStateManager.h"
|
#include "SaveStateManager.h"
|
||||||
#include "HdPackBuilder.h"
|
#include "HdPackBuilder.h"
|
||||||
|
#include "HdAudioDevice.h"
|
||||||
|
|
||||||
shared_ptr<Console> Console::Instance(new Console());
|
shared_ptr<Console> Console::Instance(new Console());
|
||||||
|
|
||||||
|
@ -114,6 +115,10 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
|
||||||
_memoryManager->RegisterIODevice(_apu.get());
|
_memoryManager->RegisterIODevice(_apu.get());
|
||||||
_memoryManager->RegisterIODevice(_controlManager.get());
|
_memoryManager->RegisterIODevice(_controlManager.get());
|
||||||
_memoryManager->RegisterIODevice(_mapper.get());
|
_memoryManager->RegisterIODevice(_mapper.get());
|
||||||
|
if(_hdData && (!_hdData->BgmFilesById.empty() || !_hdData->SfxFilesById.empty())) {
|
||||||
|
_hdAudioDevice.reset(new HdAudioDevice(_hdData.get()));
|
||||||
|
_memoryManager->RegisterIODevice(_hdAudioDevice.get());
|
||||||
|
}
|
||||||
|
|
||||||
_model = NesModel::Auto;
|
_model = NesModel::Auto;
|
||||||
UpdateNesModel(false);
|
UpdateNesModel(false);
|
||||||
|
@ -630,6 +635,7 @@ void Console::LoadHdPack(VirtualFile &romFile, VirtualFile &patchFile)
|
||||||
_hdData.reset(new HdPackData());
|
_hdData.reset(new HdPackData());
|
||||||
if(!HdPackLoader::LoadHdNesPack(romFile, *_hdData.get())) {
|
if(!HdPackLoader::LoadHdNesPack(romFile, *_hdData.get())) {
|
||||||
_hdData.reset();
|
_hdData.reset();
|
||||||
|
_hdAudioDevice.reset();
|
||||||
} else {
|
} else {
|
||||||
auto result = _hdData->PatchesByHash.find(romFile.GetSha1Hash());
|
auto result = _hdData->PatchesByHash.find(romFile.GetSha1Hash());
|
||||||
if(result != _hdData->PatchesByHash.end()) {
|
if(result != _hdData->PatchesByHash.end()) {
|
||||||
|
|
|
@ -16,6 +16,7 @@ class MemoryManager;
|
||||||
class ControlManager;
|
class ControlManager;
|
||||||
class AutoSaveManager;
|
class AutoSaveManager;
|
||||||
class HdPackBuilder;
|
class HdPackBuilder;
|
||||||
|
class HdAudioDevice;
|
||||||
struct HdPackData;
|
struct HdPackData;
|
||||||
enum class NesModel;
|
enum class NesModel;
|
||||||
enum class ScaleFilterType;
|
enum class ScaleFilterType;
|
||||||
|
@ -42,6 +43,7 @@ class Console
|
||||||
|
|
||||||
shared_ptr<HdPackBuilder> _hdPackBuilder;
|
shared_ptr<HdPackBuilder> _hdPackBuilder;
|
||||||
unique_ptr<HdPackData> _hdData;
|
unique_ptr<HdPackData> _hdData;
|
||||||
|
unique_ptr<HdAudioDevice> _hdAudioDevice;
|
||||||
|
|
||||||
NesModel _model;
|
NesModel _model;
|
||||||
|
|
||||||
|
|
|
@ -413,11 +413,14 @@
|
||||||
<ClInclude Include="AutomaticRomTest.h" />
|
<ClInclude Include="AutomaticRomTest.h" />
|
||||||
<ClInclude Include="BaseRenderer.h" />
|
<ClInclude Include="BaseRenderer.h" />
|
||||||
<ClInclude Include="FceuxMovie.h" />
|
<ClInclude Include="FceuxMovie.h" />
|
||||||
|
<ClInclude Include="HdAudioDevice.h" />
|
||||||
<ClInclude Include="HdBuilderPpu.h" />
|
<ClInclude Include="HdBuilderPpu.h" />
|
||||||
<ClInclude Include="HdData.h" />
|
<ClInclude Include="HdData.h" />
|
||||||
<ClInclude Include="HdPackBuilder.h" />
|
<ClInclude Include="HdPackBuilder.h" />
|
||||||
<ClInclude Include="HdPackLoader.h" />
|
<ClInclude Include="HdPackLoader.h" />
|
||||||
<ClInclude Include="Mapper174.h" />
|
<ClInclude Include="Mapper174.h" />
|
||||||
|
<ClInclude Include="OggMixer.h" />
|
||||||
|
<ClInclude Include="OggReader.h" />
|
||||||
<ClInclude Include="Rambo1_158.h" />
|
<ClInclude Include="Rambo1_158.h" />
|
||||||
<ClInclude Include="RecordedRomTest.h" />
|
<ClInclude Include="RecordedRomTest.h" />
|
||||||
<ClInclude Include="AutoSaveManager.h" />
|
<ClInclude Include="AutoSaveManager.h" />
|
||||||
|
@ -778,9 +781,12 @@
|
||||||
<ClCompile Include="AutomaticRomTest.cpp" />
|
<ClCompile Include="AutomaticRomTest.cpp" />
|
||||||
<ClCompile Include="BaseRenderer.cpp" />
|
<ClCompile Include="BaseRenderer.cpp" />
|
||||||
<ClCompile Include="FceuxMovie.cpp" />
|
<ClCompile Include="FceuxMovie.cpp" />
|
||||||
|
<ClCompile Include="HdAudioDevice.cpp" />
|
||||||
<ClCompile Include="HdNesPack.cpp" />
|
<ClCompile Include="HdNesPack.cpp" />
|
||||||
<ClCompile Include="HdPackBuilder.cpp" />
|
<ClCompile Include="HdPackBuilder.cpp" />
|
||||||
<ClCompile Include="HdPackLoader.cpp" />
|
<ClCompile Include="HdPackLoader.cpp" />
|
||||||
|
<ClCompile Include="OggMixer.cpp" />
|
||||||
|
<ClCompile Include="OggReader.cpp" />
|
||||||
<ClCompile Include="RecordedRomTest.cpp" />
|
<ClCompile Include="RecordedRomTest.cpp" />
|
||||||
<ClCompile Include="AutoSaveManager.cpp" />
|
<ClCompile Include="AutoSaveManager.cpp" />
|
||||||
<ClCompile Include="AviRecorder.cpp" />
|
<ClCompile Include="AviRecorder.cpp" />
|
||||||
|
|
|
@ -25,9 +25,6 @@
|
||||||
<Filter Include="Nes\Interfaces">
|
<Filter Include="Nes\Interfaces">
|
||||||
<UniqueIdentifier>{ca661408-b52a-4378-aef4-80fda1d64cd6}</UniqueIdentifier>
|
<UniqueIdentifier>{ca661408-b52a-4378-aef4-80fda1d64cd6}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter Include="VideoDecoder\HD">
|
|
||||||
<UniqueIdentifier>{a6994cb5-f9d2-416c-84ab-c1abe4975eb1}</UniqueIdentifier>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Nes\RomLoader">
|
<Filter Include="Nes\RomLoader">
|
||||||
<UniqueIdentifier>{cfdbaafc-8e74-4e09-80fd-30f8bc833c88}</UniqueIdentifier>
|
<UniqueIdentifier>{cfdbaafc-8e74-4e09-80fd-30f8bc833c88}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
@ -92,6 +89,9 @@
|
||||||
<Filter Include="Rewinder">
|
<Filter Include="Rewinder">
|
||||||
<UniqueIdentifier>{52b03b24-dd62-4daf-bac8-bb60a555d3d2}</UniqueIdentifier>
|
<UniqueIdentifier>{52b03b24-dd62-4daf-bac8-bb60a555d3d2}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
<Filter Include="HdPacks">
|
||||||
|
<UniqueIdentifier>{a6994cb5-f9d2-416c-84ab-c1abe4975eb1}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="IAudioDevice.h">
|
<ClInclude Include="IAudioDevice.h">
|
||||||
|
@ -194,10 +194,10 @@
|
||||||
<Filter>Nes\Mappers</Filter>
|
<Filter>Nes\Mappers</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="HdNesPack.h">
|
<ClInclude Include="HdNesPack.h">
|
||||||
<Filter>VideoDecoder\HD</Filter>
|
<Filter>HdPacks</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="HdPpu.h">
|
<ClInclude Include="HdPpu.h">
|
||||||
<Filter>VideoDecoder\HD</Filter>
|
<Filter>HdPacks</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="CodeDataLogger.h">
|
<ClInclude Include="CodeDataLogger.h">
|
||||||
<Filter>Debugger</Filter>
|
<Filter>Debugger</Filter>
|
||||||
|
@ -275,7 +275,7 @@
|
||||||
<Filter>Nes</Filter>
|
<Filter>Nes</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="HdVideoFilter.h">
|
<ClInclude Include="HdVideoFilter.h">
|
||||||
<Filter>VideoDecoder\HD</Filter>
|
<Filter>HdPacks</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="ExpressionEvaluator.h">
|
<ClInclude Include="ExpressionEvaluator.h">
|
||||||
<Filter>Debugger</Filter>
|
<Filter>Debugger</Filter>
|
||||||
|
@ -1181,16 +1181,25 @@
|
||||||
<Filter>VideoDecoder</Filter>
|
<Filter>VideoDecoder</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="HdPackBuilder.h">
|
<ClInclude Include="HdPackBuilder.h">
|
||||||
<Filter>VideoDecoder\HD</Filter>
|
<Filter>HdPacks</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="HdBuilderPpu.h">
|
<ClInclude Include="HdBuilderPpu.h">
|
||||||
<Filter>VideoDecoder\HD</Filter>
|
<Filter>HdPacks</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="HdData.h">
|
<ClInclude Include="HdData.h">
|
||||||
<Filter>VideoDecoder\HD</Filter>
|
<Filter>HdPacks</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="HdPackLoader.h">
|
<ClInclude Include="HdPackLoader.h">
|
||||||
<Filter>VideoDecoder\HD</Filter>
|
<Filter>HdPacks</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="OggReader.h">
|
||||||
|
<Filter>HdPacks</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="HdAudioDevice.h">
|
||||||
|
<Filter>HdPacks</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="OggMixer.h">
|
||||||
|
<Filter>HdPacks</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1276,7 +1285,7 @@
|
||||||
<Filter>Nes\APU</Filter>
|
<Filter>Nes\APU</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="HdVideoFilter.cpp">
|
<ClCompile Include="HdVideoFilter.cpp">
|
||||||
<Filter>VideoDecoder\HD</Filter>
|
<Filter>HdPacks</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="ExpressionEvaluator.cpp">
|
<ClCompile Include="ExpressionEvaluator.cpp">
|
||||||
<Filter>Debugger</Filter>
|
<Filter>Debugger</Filter>
|
||||||
|
@ -1417,13 +1426,22 @@
|
||||||
<Filter>VideoDecoder</Filter>
|
<Filter>VideoDecoder</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="HdPackBuilder.cpp">
|
<ClCompile Include="HdPackBuilder.cpp">
|
||||||
<Filter>VideoDecoder\HD</Filter>
|
<Filter>HdPacks</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="HdPackLoader.cpp">
|
<ClCompile Include="HdPackLoader.cpp">
|
||||||
<Filter>VideoDecoder\HD</Filter>
|
<Filter>HdPacks</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="HdNesPack.cpp">
|
<ClCompile Include="HdNesPack.cpp">
|
||||||
<Filter>VideoDecoder\HD</Filter>
|
<Filter>HdPacks</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="OggMixer.cpp">
|
||||||
|
<Filter>HdPacks</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="OggReader.cpp">
|
||||||
|
<Filter>Misc</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="HdAudioDevice.cpp">
|
||||||
|
<Filter>Misc</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
109
Core/HdAudioDevice.cpp
Normal file
109
Core/HdAudioDevice.cpp
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "HdAudioDevice.h"
|
||||||
|
#include "HdData.h"
|
||||||
|
|
||||||
|
HdAudioDevice::HdAudioDevice(HdPackData * hdData)
|
||||||
|
{
|
||||||
|
_hdData = hdData;
|
||||||
|
_album = 0;
|
||||||
|
_flags = 0;
|
||||||
|
_trackError = false;
|
||||||
|
_oggMixer = SoundMixer::GetOggMixer();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HdAudioDevice::PlayBgmTrack(uint8_t track)
|
||||||
|
{
|
||||||
|
auto result = _hdData->BgmFilesById.find(_album * 256 + track);
|
||||||
|
if(result != _hdData->BgmFilesById.end()) {
|
||||||
|
return !_oggMixer->Play(result->second, false);
|
||||||
|
} else {
|
||||||
|
MessageManager::Log("[HDPack] Invalid album+track combination: " + std::to_string(_album) + ":" + std::to_string(track));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HdAudioDevice::PlaySfx(uint8_t sfxNumber)
|
||||||
|
{
|
||||||
|
auto result = _hdData->SfxFilesById.find(_album * 256 + sfxNumber);
|
||||||
|
if(result != _hdData->SfxFilesById.end()) {
|
||||||
|
return !_oggMixer->Play(result->second, true);
|
||||||
|
} else {
|
||||||
|
MessageManager::Log("[HDPack] Invalid album+sfx number combination: " + std::to_string(_album) + ":" + std::to_string(sfxNumber));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HdAudioDevice::ProcessControlFlags(uint8_t flags)
|
||||||
|
{
|
||||||
|
_oggMixer->SetPausedFlag((flags & 0x01) == 0x01);
|
||||||
|
if(flags & 0x02) {
|
||||||
|
_oggMixer->StopBgm();
|
||||||
|
}
|
||||||
|
if(flags & 0x04) {
|
||||||
|
_oggMixer->StopSfx();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HdAudioDevice::GetMemoryRanges(MemoryRanges & ranges)
|
||||||
|
{
|
||||||
|
uint16_t baseRegisterAddr = (_hdData->OptionFlags & (int)HdPackOptions::AlternateRegisterRange) ? 0x5000 : 0x4000;
|
||||||
|
ranges.SetAllowOverride();
|
||||||
|
ranges.AddHandler(MemoryOperation::Any, baseRegisterAddr + 0xFF9, baseRegisterAddr + 0xFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HdAudioDevice::WriteRAM(uint16_t addr, uint8_t value)
|
||||||
|
{
|
||||||
|
switch(addr & 0xFFF) {
|
||||||
|
//Playback Options
|
||||||
|
//Bit 0: Loop BGM
|
||||||
|
//Bit 1-7: Unused, reserved - must be 0
|
||||||
|
case 0xFF9: _oggMixer->SetPlaybackOptions(value); break;
|
||||||
|
|
||||||
|
//Playback Control
|
||||||
|
//Bit 0: Toggle Pause/Resume (only affects BGM)
|
||||||
|
//Bit 1: Stop BGM
|
||||||
|
//Bit 2: Stop all SFX
|
||||||
|
//Bit 3-7: Unused, reserved - must be 0
|
||||||
|
case 0xFFA: ProcessControlFlags(value); break;
|
||||||
|
|
||||||
|
//BGM Volume: 0 = mute, 255 = max
|
||||||
|
//Also has an immediate effect on currently playing BGM
|
||||||
|
case 0xFFB: _oggMixer->SetBgmVolume(value); break;
|
||||||
|
|
||||||
|
//SFX Volume: 0 = mute, 255 = max
|
||||||
|
//Also has an immediate effect on all currently playing SFX
|
||||||
|
case 0xFFC: _oggMixer->SetSfxVolume(value); break;
|
||||||
|
|
||||||
|
//Album number: 0-255 (Allows for up to 64k BGM and SFX tracks)
|
||||||
|
//No immediate effect - only affects subsequent $4FFE/$4FFF writes
|
||||||
|
case 0xFFD: _album = value; break;
|
||||||
|
|
||||||
|
//Play BGM track (0-255 = track number)
|
||||||
|
//Stop the current BGM and starts a new track
|
||||||
|
case 0xFFE: _trackError = PlayBgmTrack(value); break;
|
||||||
|
|
||||||
|
//Play sound effect (0-255 = sfx number)
|
||||||
|
//Plays a new sound effect (no limit to the number of simultaneous sound effects)
|
||||||
|
case 0xFFF: _trackError = PlaySfx(value); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t HdAudioDevice::ReadRAM(uint16_t addr)
|
||||||
|
{
|
||||||
|
switch(addr & 0xFFF) {
|
||||||
|
case 0xFFA:
|
||||||
|
//Status
|
||||||
|
return (
|
||||||
|
(_oggMixer->IsBgmPlaying() ? 1 : 0) |
|
||||||
|
(_oggMixer->IsSfxPlaying() ? 2 : 0) |
|
||||||
|
(_trackError ? 4 : 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
case 0xFFC: return 'N'; //NES
|
||||||
|
case 0xFFD: return 'E'; //Enhanced
|
||||||
|
case 0xFFE: return 'A'; //Audio
|
||||||
|
case 0xFFF: return 1; //Revision
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
28
Core/HdAudioDevice.h
Normal file
28
Core/HdAudioDevice.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "IMemoryHandler.h"
|
||||||
|
#include "SoundMixer.h"
|
||||||
|
#include "OggMixer.h"
|
||||||
|
|
||||||
|
struct HdPackData;
|
||||||
|
|
||||||
|
class HdAudioDevice : public IMemoryHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
HdPackData *_hdData;
|
||||||
|
uint8_t _album;
|
||||||
|
uint8_t _flags;
|
||||||
|
bool _trackError;
|
||||||
|
OggMixer* _oggMixer;
|
||||||
|
|
||||||
|
bool PlayBgmTrack(uint8_t track);
|
||||||
|
bool PlaySfx(uint8_t sfxNumber);
|
||||||
|
void ProcessControlFlags(uint8_t flags);
|
||||||
|
|
||||||
|
public:
|
||||||
|
HdAudioDevice(HdPackData *hdData);
|
||||||
|
|
||||||
|
void GetMemoryRanges(MemoryRanges &ranges) override;
|
||||||
|
void WriteRAM(uint16_t addr, uint8_t value) override;
|
||||||
|
uint8_t ReadRAM(uint16_t addr) override;
|
||||||
|
};
|
|
@ -396,6 +396,8 @@ struct HdPackData
|
||||||
vector<unique_ptr<HdPackCondition>> Conditions;
|
vector<unique_ptr<HdPackCondition>> Conditions;
|
||||||
std::unordered_map<HdTileKey, vector<HdPackTileInfo*>> TileByKey;
|
std::unordered_map<HdTileKey, vector<HdPackTileInfo*>> TileByKey;
|
||||||
std::unordered_map<string, string> PatchesByHash;
|
std::unordered_map<string, string> PatchesByHash;
|
||||||
|
std::unordered_map<int, string> BgmFilesById;
|
||||||
|
std::unordered_map<int, string> SfxFilesById;
|
||||||
vector<uint32_t> Palette;
|
vector<uint32_t> Palette;
|
||||||
|
|
||||||
bool HasOverscanConfig = false;
|
bool HasOverscanConfig = false;
|
||||||
|
@ -416,4 +418,5 @@ enum class HdPackOptions
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
NoSpriteLimit = 1,
|
NoSpriteLimit = 1,
|
||||||
|
AlternateRegisterRange = 2,
|
||||||
};
|
};
|
|
@ -79,6 +79,20 @@ bool HdPackLoader::LoadHdNesPack(VirtualFile &romFile, HdPackData &outData)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HdPackLoader::CheckFile(string filename)
|
||||||
|
{
|
||||||
|
if(_loadFromZip) {
|
||||||
|
return _reader.CheckFile(filename);
|
||||||
|
} else {
|
||||||
|
ifstream file(FolderUtilities::CombinePath(_hdPackFolder, filename), ios::in | ios::binary);
|
||||||
|
if(file.good()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool HdPackLoader::LoadFile(string filename, vector<uint8_t> &fileData)
|
bool HdPackLoader::LoadFile(string filename, vector<uint8_t> &fileData)
|
||||||
{
|
{
|
||||||
fileData.clear();
|
fileData.clear();
|
||||||
|
@ -153,6 +167,12 @@ bool HdPackLoader::LoadPack()
|
||||||
} else if(lineContent.substr(0, 9) == "<options>") {
|
} else if(lineContent.substr(0, 9) == "<options>") {
|
||||||
tokens = StringUtilities::Split(lineContent.substr(9), ',');
|
tokens = StringUtilities::Split(lineContent.substr(9), ',');
|
||||||
ProcessOptionTag(tokens);
|
ProcessOptionTag(tokens);
|
||||||
|
} else if(lineContent.substr(0, 5) == "<bgm>") {
|
||||||
|
tokens = StringUtilities::Split(lineContent.substr(5), ',');
|
||||||
|
ProcessBgmTag(tokens);
|
||||||
|
} else if(lineContent.substr(0, 5) == "<sfx>") {
|
||||||
|
tokens = StringUtilities::Split(lineContent.substr(5), ',');
|
||||||
|
ProcessSfxTag(tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,6 +332,8 @@ void HdPackLoader::ProcessOptionTag(vector<string> &tokens)
|
||||||
for(string token : tokens) {
|
for(string token : tokens) {
|
||||||
if(token == "disableSpriteLimit") {
|
if(token == "disableSpriteLimit") {
|
||||||
_data->OptionFlags |= (int)HdPackOptions::NoSpriteLimit;
|
_data->OptionFlags |= (int)HdPackOptions::NoSpriteLimit;
|
||||||
|
} else if(token == "alternateRegisterRange") {
|
||||||
|
_data->OptionFlags |= (int)HdPackOptions::AlternateRegisterRange;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -402,6 +424,55 @@ void HdPackLoader::ProcessBackgroundTag(vector<string> &tokens, vector<HdPackCon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int HdPackLoader::ProcessSoundTrack(string albumString, string trackString, string filename)
|
||||||
|
{
|
||||||
|
int album = std::stoi(albumString);
|
||||||
|
if(album < 0 || album > 255) {
|
||||||
|
MessageManager::Log("[HDPack] Invalid album value: " + albumString);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int track = std::stoi(trackString);
|
||||||
|
if(track < 0 || track > 255) {
|
||||||
|
MessageManager::Log("[HDPack] Invalid track value: " + trackString);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!CheckFile(filename)) {
|
||||||
|
MessageManager::Log("[HDPack] OGG file not found: " + filename);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return album * 256 + track;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void HdPackLoader::ProcessBgmTag(vector<string> &tokens)
|
||||||
|
{
|
||||||
|
int trackId = ProcessSoundTrack(tokens[0], tokens[1], tokens[2]);
|
||||||
|
if(trackId >= 0) {
|
||||||
|
if(_loadFromZip) {
|
||||||
|
VirtualFile file(_hdPackFolder, tokens[2]);
|
||||||
|
_data->BgmFilesById[trackId] = file;
|
||||||
|
} else {
|
||||||
|
_data->BgmFilesById[trackId] = FolderUtilities::CombinePath(_hdPackFolder, tokens[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HdPackLoader::ProcessSfxTag(vector<string> &tokens)
|
||||||
|
{
|
||||||
|
int trackId = ProcessSoundTrack(tokens[0], tokens[1], tokens[2]);
|
||||||
|
if(trackId >= 0) {
|
||||||
|
if(_loadFromZip) {
|
||||||
|
VirtualFile file(_hdPackFolder, tokens[2]);
|
||||||
|
_data->SfxFilesById[trackId] = file;
|
||||||
|
} else {
|
||||||
|
_data->SfxFilesById[trackId] = FolderUtilities::CombinePath(_hdPackFolder, tokens[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
vector<HdPackCondition*> HdPackLoader::ParseConditionString(string conditionString, vector<unique_ptr<HdPackCondition>> &conditions)
|
vector<HdPackCondition*> HdPackLoader::ParseConditionString(string conditionString, vector<unique_ptr<HdPackCondition>> &conditions)
|
||||||
{
|
{
|
||||||
vector<string> conditionNames = StringUtilities::Split(conditionString, '&');
|
vector<string> conditionNames = StringUtilities::Split(conditionString, '&');
|
||||||
|
|
|
@ -22,6 +22,7 @@ private:
|
||||||
|
|
||||||
bool InitializeLoader(VirtualFile &romPath, HdPackData *data);
|
bool InitializeLoader(VirtualFile &romPath, HdPackData *data);
|
||||||
bool LoadFile(string filename, vector<uint8_t> &fileData);
|
bool LoadFile(string filename, vector<uint8_t> &fileData);
|
||||||
|
bool CheckFile(string filename);
|
||||||
|
|
||||||
bool LoadPack();
|
bool LoadPack();
|
||||||
void InitializeHdPack();
|
void InitializeHdPack();
|
||||||
|
@ -29,6 +30,7 @@ private:
|
||||||
|
|
||||||
void InitializeGlobalConditions();
|
void InitializeGlobalConditions();
|
||||||
|
|
||||||
|
//Video
|
||||||
bool ProcessImgTag(string src);
|
bool ProcessImgTag(string src);
|
||||||
void ProcessPatchTag(vector<string> &tokens);
|
void ProcessPatchTag(vector<string> &tokens);
|
||||||
void ProcessOverscanTag(vector<string> &tokens);
|
void ProcessOverscanTag(vector<string> &tokens);
|
||||||
|
@ -37,5 +39,10 @@ private:
|
||||||
void ProcessBackgroundTag(vector<string> &tokens, vector<HdPackCondition*> conditions);
|
void ProcessBackgroundTag(vector<string> &tokens, vector<HdPackCondition*> conditions);
|
||||||
void ProcessOptionTag(vector<string>& tokens);
|
void ProcessOptionTag(vector<string>& tokens);
|
||||||
|
|
||||||
|
//Audio
|
||||||
|
int ProcessSoundTrack(string albumString, string trackString, string filename);
|
||||||
|
void ProcessBgmTag(vector<string> &tokens);
|
||||||
|
void ProcessSfxTag(vector<string> &tokens);
|
||||||
|
|
||||||
vector<HdPackCondition*> ParseConditionString(string conditionString, vector<unique_ptr<HdPackCondition>> &conditions);
|
vector<HdPackCondition*> ParseConditionString(string conditionString, vector<unique_ptr<HdPackCondition>> &conditions);
|
||||||
};
|
};
|
110
Core/OggMixer.cpp
Normal file
110
Core/OggMixer.cpp
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "OggReader.h"
|
||||||
|
#include "OggMixer.h"
|
||||||
|
|
||||||
|
enum class OggPlaybackOptions
|
||||||
|
{
|
||||||
|
None = 0x00,
|
||||||
|
Loop = 0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
OggMixer::OggMixer()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OggMixer::Reset()
|
||||||
|
{
|
||||||
|
_bgm.reset();
|
||||||
|
_sfx.clear();
|
||||||
|
_sfxVolume = 128;
|
||||||
|
_bgmVolume = 45;
|
||||||
|
_options = 0;
|
||||||
|
_sampleRate = EmulationSettings::GetSampleRate();
|
||||||
|
_paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OggMixer::SetPlaybackOptions(uint8_t options)
|
||||||
|
{
|
||||||
|
_options = options;
|
||||||
|
|
||||||
|
bool loop = (options & (int)OggPlaybackOptions::Loop) != 0;
|
||||||
|
if(_bgm) {
|
||||||
|
_bgm->SetLoopFlag(loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OggMixer::SetPausedFlag(bool paused)
|
||||||
|
{
|
||||||
|
_paused = paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OggMixer::StopBgm()
|
||||||
|
{
|
||||||
|
_bgm.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OggMixer::StopSfx()
|
||||||
|
{
|
||||||
|
_sfx.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OggMixer::SetBgmVolume(uint8_t volume)
|
||||||
|
{
|
||||||
|
_bgmVolume = volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OggMixer::SetSfxVolume(uint8_t volume)
|
||||||
|
{
|
||||||
|
_sfxVolume = volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OggMixer::IsBgmPlaying()
|
||||||
|
{
|
||||||
|
return !_paused && _bgm;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OggMixer::IsSfxPlaying()
|
||||||
|
{
|
||||||
|
return _sfx.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OggMixer::SetSampleRate(int sampleRate)
|
||||||
|
{
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
if(_bgm) {
|
||||||
|
_bgm->SetSampleRate(sampleRate);
|
||||||
|
}
|
||||||
|
for(shared_ptr<OggReader> &sfx : _sfx) {
|
||||||
|
sfx->SetSampleRate(sampleRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OggMixer::Play(string filename, bool isSfx)
|
||||||
|
{
|
||||||
|
shared_ptr<OggReader> reader(new OggReader());
|
||||||
|
bool loop = !isSfx && (_options & (int)OggPlaybackOptions::Loop) != 0;
|
||||||
|
if(reader->Init(filename, loop, _sampleRate)) {
|
||||||
|
if(isSfx) {
|
||||||
|
_sfx.push_back(reader);
|
||||||
|
} else {
|
||||||
|
_bgm = reader;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OggMixer::ApplySamples(int16_t * buffer, size_t sampleCount)
|
||||||
|
{
|
||||||
|
if(_bgm && !_paused) {
|
||||||
|
_bgm->ApplySamples(buffer, sampleCount, _bgmVolume);
|
||||||
|
if(_bgm->IsPlaybackOver()) {
|
||||||
|
_bgm.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(shared_ptr<OggReader> &sfx : _sfx) {
|
||||||
|
sfx->ApplySamples(buffer, sampleCount, _sfxVolume);
|
||||||
|
}
|
||||||
|
_sfx.erase(std::remove_if(_sfx.begin(), _sfx.end(), [](const shared_ptr<OggReader>& o) { return o->IsPlaybackOver(); }), _sfx.end());
|
||||||
|
}
|
34
Core/OggMixer.h
Normal file
34
Core/OggMixer.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
class OggReader;
|
||||||
|
|
||||||
|
class OggMixer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
shared_ptr<OggReader> _bgm;
|
||||||
|
vector<shared_ptr<OggReader>> _sfx;
|
||||||
|
|
||||||
|
uint32_t _sampleRate;
|
||||||
|
uint8_t _bgmVolume;
|
||||||
|
uint8_t _sfxVolume;
|
||||||
|
uint8_t _options;
|
||||||
|
bool _paused;
|
||||||
|
|
||||||
|
public:
|
||||||
|
OggMixer();
|
||||||
|
|
||||||
|
void SetSampleRate(int sampleRate);
|
||||||
|
void ApplySamples(int16_t* buffer, size_t sampleCount);
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
bool Play(string filename, bool isSfx);
|
||||||
|
void SetPlaybackOptions(uint8_t options);
|
||||||
|
void SetPausedFlag(bool paused);
|
||||||
|
void StopBgm();
|
||||||
|
void StopSfx();
|
||||||
|
void SetBgmVolume(uint8_t volume);
|
||||||
|
void SetSfxVolume(uint8_t volume);
|
||||||
|
bool IsBgmPlaying();
|
||||||
|
bool IsSfxPlaying();
|
||||||
|
};
|
102
Core/OggReader.cpp
Normal file
102
Core/OggReader.cpp
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "OggReader.h"
|
||||||
|
|
||||||
|
OggReader::OggReader()
|
||||||
|
{
|
||||||
|
_done = false;
|
||||||
|
_blipLeft = blip_new(10000);
|
||||||
|
_blipRight = blip_new(10000);
|
||||||
|
_oggBuffer = new int16_t[OggReader::SamplesToRead * 2];
|
||||||
|
_outputBuffer = new int16_t[2000];
|
||||||
|
}
|
||||||
|
|
||||||
|
OggReader::~OggReader()
|
||||||
|
{
|
||||||
|
blip_delete(_blipLeft);
|
||||||
|
blip_delete(_blipRight);
|
||||||
|
delete[] _oggBuffer;
|
||||||
|
delete[] _outputBuffer;
|
||||||
|
|
||||||
|
if(_vorbis) {
|
||||||
|
stb_vorbis_close(_vorbis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OggReader::Init(string filename, bool loop, int sampleRate)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
VirtualFile file = filename;
|
||||||
|
_fileData = vector<uint8_t>(100000);
|
||||||
|
if(file.ReadFile(_fileData)) {
|
||||||
|
_vorbis = stb_vorbis_open_memory(_fileData.data(), (int)_fileData.size(), &error, nullptr);
|
||||||
|
if(_vorbis) {
|
||||||
|
_loop = loop;
|
||||||
|
_oggSampleRate = stb_vorbis_get_info(_vorbis).sample_rate;
|
||||||
|
blip_set_rates(_blipLeft, _oggSampleRate, sampleRate);
|
||||||
|
blip_set_rates(_blipRight, _oggSampleRate, sampleRate);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OggReader::IsPlaybackOver()
|
||||||
|
{
|
||||||
|
return _done && blip_samples_avail(_blipLeft) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OggReader::SetSampleRate(int sampleRate)
|
||||||
|
{
|
||||||
|
if(sampleRate != _sampleRate) {
|
||||||
|
blip_clear(_blipLeft);
|
||||||
|
blip_clear(_blipRight);
|
||||||
|
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
blip_set_rates(_blipLeft, _oggSampleRate, _sampleRate);
|
||||||
|
blip_set_rates(_blipRight, _oggSampleRate, _sampleRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OggReader::SetLoopFlag(bool loop)
|
||||||
|
{
|
||||||
|
_loop = loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OggReader::LoadSamples()
|
||||||
|
{
|
||||||
|
int samplesReturned = stb_vorbis_get_samples_short_interleaved(_vorbis, 2, _oggBuffer, OggReader::SamplesToRead * 2);
|
||||||
|
|
||||||
|
for(int i = 0; i < samplesReturned; i++) {
|
||||||
|
blip_add_delta(_blipLeft, i, i == 0 ? 0 : (_oggBuffer[i * 2] - _oggBuffer[i * 2 - 2]));
|
||||||
|
blip_add_delta(_blipRight, i, i == 0 ? 0 : (_oggBuffer[i * 2 + 1] - _oggBuffer[i * 2 - 1]));
|
||||||
|
}
|
||||||
|
blip_end_frame(_blipLeft, samplesReturned);
|
||||||
|
blip_end_frame(_blipRight, samplesReturned);
|
||||||
|
|
||||||
|
if(samplesReturned < OggReader::SamplesToRead) {
|
||||||
|
if(_loop) {
|
||||||
|
stb_vorbis_seek_start(_vorbis);
|
||||||
|
LoadSamples();
|
||||||
|
} else {
|
||||||
|
_done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return samplesReturned > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OggReader::ApplySamples(int16_t * buffer, size_t sampleCount, uint8_t volume)
|
||||||
|
{
|
||||||
|
while(blip_samples_avail(_blipLeft) < sampleCount) {
|
||||||
|
if(!LoadSamples()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int samplesRead = blip_read_samples(_blipLeft, _outputBuffer, (int)sampleCount, 1);
|
||||||
|
blip_read_samples(_blipRight, _outputBuffer + 1, (int)sampleCount, 1);
|
||||||
|
|
||||||
|
for(size_t i = 0, len = samplesRead * 2; i < len; i++) {
|
||||||
|
buffer[i] += (int16_t)(_outputBuffer[i] * (EmulationSettings::GetMasterVolume() * volume / 255 / 10));
|
||||||
|
}
|
||||||
|
}
|
39
Core/OggReader.h
Normal file
39
Core/OggReader.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "../Utilities/stb_vorbis.h"
|
||||||
|
#include "../Utilities/blip_buf.h"
|
||||||
|
#include "../Utilities/VirtualFile.h"
|
||||||
|
#include "EmulationSettings.h"
|
||||||
|
|
||||||
|
class OggReader
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const int SamplesToRead = 100;
|
||||||
|
|
||||||
|
stb_vorbis* _vorbis;
|
||||||
|
int16_t* _oggBuffer;
|
||||||
|
int16_t* _outputBuffer;
|
||||||
|
|
||||||
|
bool _loop;
|
||||||
|
bool _done;
|
||||||
|
|
||||||
|
blip_t* _blipLeft;
|
||||||
|
blip_t* _blipRight;
|
||||||
|
|
||||||
|
int _sampleRate;
|
||||||
|
int _oggSampleRate;
|
||||||
|
|
||||||
|
vector<uint8_t> _fileData;
|
||||||
|
|
||||||
|
bool LoadSamples();
|
||||||
|
|
||||||
|
public:
|
||||||
|
OggReader();
|
||||||
|
~OggReader();
|
||||||
|
|
||||||
|
bool Init(string filename, bool loop, int sampleRate);
|
||||||
|
bool IsPlaybackOver();
|
||||||
|
void SetSampleRate(int sampleRate);
|
||||||
|
void SetLoopFlag(bool loop);
|
||||||
|
void ApplySamples(int16_t* buffer, size_t sampleCount, uint8_t volume);
|
||||||
|
};
|
|
@ -1,20 +1,25 @@
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "../Utilities/orfanidis_eq.h"
|
#include "../Utilities/orfanidis_eq.h"
|
||||||
|
#include "../Utilities/stb_vorbis.h"
|
||||||
#include "SoundMixer.h"
|
#include "SoundMixer.h"
|
||||||
#include "APU.h"
|
#include "APU.h"
|
||||||
#include "CPU.h"
|
#include "CPU.h"
|
||||||
#include "VideoRenderer.h"
|
#include "VideoRenderer.h"
|
||||||
#include "RewindManager.h"
|
#include "RewindManager.h"
|
||||||
|
#include "WaveRecorder.h"
|
||||||
|
#include "OggMixer.h"
|
||||||
|
|
||||||
IAudioDevice* SoundMixer::AudioDevice = nullptr;
|
IAudioDevice* SoundMixer::AudioDevice = nullptr;
|
||||||
unique_ptr<WaveRecorder> SoundMixer::_waveRecorder;
|
unique_ptr<WaveRecorder> SoundMixer::_waveRecorder;
|
||||||
SimpleLock SoundMixer::_waveRecorderLock;
|
SimpleLock SoundMixer::_waveRecorderLock;
|
||||||
double SoundMixer::_fadeRatio;
|
double SoundMixer::_fadeRatio;
|
||||||
uint32_t SoundMixer::_muteFrameCount;
|
uint32_t SoundMixer::_muteFrameCount;
|
||||||
|
unique_ptr<OggMixer> SoundMixer::_oggMixer;
|
||||||
|
|
||||||
SoundMixer::SoundMixer()
|
SoundMixer::SoundMixer()
|
||||||
{
|
{
|
||||||
_eqFrequencyGrid.reset(new orfanidis_eq::freq_grid());
|
_eqFrequencyGrid.reset(new orfanidis_eq::freq_grid());
|
||||||
|
_oggMixer.reset();
|
||||||
_outputBuffer = new int16_t[SoundMixer::MaxSamplesPerFrame];
|
_outputBuffer = new int16_t[SoundMixer::MaxSamplesPerFrame];
|
||||||
_blipBufLeft = blip_new(SoundMixer::MaxSamplesPerFrame);
|
_blipBufLeft = blip_new(SoundMixer::MaxSamplesPerFrame);
|
||||||
_blipBufRight = blip_new(SoundMixer::MaxSamplesPerFrame);
|
_blipBufRight = blip_new(SoundMixer::MaxSamplesPerFrame);
|
||||||
|
@ -64,6 +69,9 @@ void SoundMixer::StopAudio(bool clearBuffer)
|
||||||
|
|
||||||
void SoundMixer::Reset()
|
void SoundMixer::Reset()
|
||||||
{
|
{
|
||||||
|
if(_oggMixer) {
|
||||||
|
_oggMixer->Reset();
|
||||||
|
}
|
||||||
_fadeRatio = 1.0;
|
_fadeRatio = 1.0;
|
||||||
_muteFrameCount = 0;
|
_muteFrameCount = 0;
|
||||||
|
|
||||||
|
@ -102,6 +110,10 @@ void SoundMixer::PlayAudioBuffer(uint32_t time)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_oggMixer) {
|
||||||
|
_oggMixer->ApplySamples(_outputBuffer, sampleCount);
|
||||||
|
}
|
||||||
|
|
||||||
//Apply low pass filter/volume reduction when in background (based on options)
|
//Apply low pass filter/volume reduction when in background (based on options)
|
||||||
if(!VideoRenderer::GetInstance()->IsRecording() && !_waveRecorder && !EmulationSettings::CheckFlag(EmulationFlags::NsfPlayerEnabled) && EmulationSettings::CheckFlag(EmulationFlags::InBackground)) {
|
if(!VideoRenderer::GetInstance()->IsRecording() && !_waveRecorder && !EmulationSettings::CheckFlag(EmulationFlags::NsfPlayerEnabled) && EmulationSettings::CheckFlag(EmulationFlags::InBackground)) {
|
||||||
if(EmulationSettings::CheckFlag(EmulationFlags::MuteSoundInBackground)) {
|
if(EmulationSettings::CheckFlag(EmulationFlags::MuteSoundInBackground)) {
|
||||||
|
@ -173,6 +185,9 @@ void SoundMixer::UpdateRates(bool forceUpdate)
|
||||||
_clockRate = newRate;
|
_clockRate = newRate;
|
||||||
blip_set_rates(_blipBufLeft, _clockRate, _sampleRate);
|
blip_set_rates(_blipBufLeft, _clockRate, _sampleRate);
|
||||||
blip_set_rates(_blipBufRight, _clockRate, _sampleRate);
|
blip_set_rates(_blipBufRight, _clockRate, _sampleRate);
|
||||||
|
if(_oggMixer) {
|
||||||
|
_oggMixer->SetSampleRate(_sampleRate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasPanning = false;
|
bool hasPanning = false;
|
||||||
|
@ -347,4 +362,12 @@ uint32_t SoundMixer::GetMuteFrameCount()
|
||||||
void SoundMixer::ResetMuteFrameCount()
|
void SoundMixer::ResetMuteFrameCount()
|
||||||
{
|
{
|
||||||
_muteFrameCount = 0;
|
_muteFrameCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
OggMixer* SoundMixer::GetOggMixer()
|
||||||
|
{
|
||||||
|
if(!_oggMixer) {
|
||||||
|
_oggMixer.reset(new OggMixer());
|
||||||
|
}
|
||||||
|
return _oggMixer.get();
|
||||||
}
|
}
|
|
@ -10,7 +10,9 @@
|
||||||
#include "StereoDelayFilter.h"
|
#include "StereoDelayFilter.h"
|
||||||
#include "ReverbFilter.h"
|
#include "ReverbFilter.h"
|
||||||
#include "CrossFeedFilter.h"
|
#include "CrossFeedFilter.h"
|
||||||
#include "WaveRecorder.h"
|
|
||||||
|
class WaveRecorder;
|
||||||
|
class OggMixer;
|
||||||
|
|
||||||
namespace orfanidis_eq {
|
namespace orfanidis_eq {
|
||||||
class freq_grid;
|
class freq_grid;
|
||||||
|
@ -28,9 +30,10 @@ private:
|
||||||
static SimpleLock _waveRecorderLock;
|
static SimpleLock _waveRecorderLock;
|
||||||
static double _fadeRatio;
|
static double _fadeRatio;
|
||||||
static uint32_t _muteFrameCount;
|
static uint32_t _muteFrameCount;
|
||||||
|
static unique_ptr<OggMixer> _oggMixer;
|
||||||
|
|
||||||
static IAudioDevice* AudioDevice;
|
static IAudioDevice* AudioDevice;
|
||||||
static const uint32_t MaxSampleRate = 48000;
|
static const uint32_t MaxSampleRate = 96000;
|
||||||
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 MaxSamplesPerFrame = MaxSampleRate / 60 * 4 * 2; //x4 to allow CPU overclocking up to 10x, x2 for panning stereo
|
||||||
static const uint32_t MaxChannelCount = 11;
|
static const uint32_t MaxChannelCount = 11;
|
||||||
|
|
||||||
|
@ -96,4 +99,6 @@ public:
|
||||||
|
|
||||||
static void StopAudio(bool clearBuffer = false);
|
static void StopAudio(bool clearBuffer = false);
|
||||||
static void RegisterAudioDevice(IAudioDevice *audioDevice);
|
static void RegisterAudioDevice(IAudioDevice *audioDevice);
|
||||||
|
|
||||||
|
static OggMixer* GetOggMixer();
|
||||||
};
|
};
|
||||||
|
|
|
@ -48,6 +48,12 @@ vector<string> ArchiveReader::GetFileList(std::initializer_list<string> extensio
|
||||||
return filenames;
|
return filenames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ArchiveReader::CheckFile(string filename)
|
||||||
|
{
|
||||||
|
vector<string> files = InternalGetFileList();
|
||||||
|
return std::find(files.begin(), files.end(), filename) != files.end();
|
||||||
|
}
|
||||||
|
|
||||||
bool ArchiveReader::LoadArchive(std::istream &in)
|
bool ArchiveReader::LoadArchive(std::istream &in)
|
||||||
{
|
{
|
||||||
in.seekg(0, std::ios::end);
|
in.seekg(0, std::ios::end);
|
||||||
|
|
|
@ -18,6 +18,7 @@ public:
|
||||||
std::stringstream GetStream(string filename);
|
std::stringstream GetStream(string filename);
|
||||||
|
|
||||||
vector<string> GetFileList(std::initializer_list<string> extensions = {});
|
vector<string> GetFileList(std::initializer_list<string> extensions = {});
|
||||||
|
bool CheckFile(string filename);
|
||||||
|
|
||||||
virtual bool ExtractFile(string filename, vector<uint8_t> &output) = 0;
|
virtual bool ExtractFile(string filename, vector<uint8_t> &output) = 0;
|
||||||
|
|
||||||
|
|
|
@ -349,6 +349,7 @@
|
||||||
<ClInclude Include="Scale2x\scale3x.h" />
|
<ClInclude Include="Scale2x\scale3x.h" />
|
||||||
<ClInclude Include="Scale2x\scalebit.h" />
|
<ClInclude Include="Scale2x\scalebit.h" />
|
||||||
<ClInclude Include="sha1.h" />
|
<ClInclude Include="sha1.h" />
|
||||||
|
<ClInclude Include="stb_vorbis.h" />
|
||||||
<ClInclude Include="StringUtilities.h" />
|
<ClInclude Include="StringUtilities.h" />
|
||||||
<ClInclude Include="SZReader.h" />
|
<ClInclude Include="SZReader.h" />
|
||||||
<ClInclude Include="UPnPPortMapper.h" />
|
<ClInclude Include="UPnPPortMapper.h" />
|
||||||
|
@ -485,6 +486,7 @@
|
||||||
<ClCompile Include="sha1.cpp" />
|
<ClCompile Include="sha1.cpp" />
|
||||||
<ClCompile Include="SimpleLock.cpp" />
|
<ClCompile Include="SimpleLock.cpp" />
|
||||||
<ClCompile Include="Socket.cpp" />
|
<ClCompile Include="Socket.cpp" />
|
||||||
|
<ClCompile Include="stb_vorbis.cpp" />
|
||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||||
|
|
|
@ -161,6 +161,9 @@
|
||||||
<ClInclude Include="VirtualFile.h">
|
<ClInclude Include="VirtualFile.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="stb_vorbis.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
|
@ -277,5 +280,8 @@
|
||||||
<ClCompile Include="sha1.cpp">
|
<ClCompile Include="sha1.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="stb_vorbis.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
5119
Utilities/stb_vorbis.cpp
Normal file
5119
Utilities/stb_vorbis.cpp
Normal file
File diff suppressed because it is too large
Load diff
333
Utilities/stb_vorbis.h
Normal file
333
Utilities/stb_vorbis.h
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// HEADER BEGINS HERE
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H
|
||||||
|
#define STB_VORBIS_INCLUDE_STB_VORBIS_H
|
||||||
|
|
||||||
|
#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO)
|
||||||
|
#define STB_VORBIS_NO_STDIO 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STB_VORBIS_NO_STDIO
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/////////// THREAD SAFETY
|
||||||
|
|
||||||
|
// Individual stb_vorbis* handles are not thread-safe; you cannot decode from
|
||||||
|
// them from multiple threads at the same time. However, you can have multiple
|
||||||
|
// stb_vorbis* handles and decode from them independently in multiple thrads.
|
||||||
|
|
||||||
|
|
||||||
|
/////////// MEMORY ALLOCATION
|
||||||
|
|
||||||
|
// normally stb_vorbis uses malloc() to allocate memory at startup,
|
||||||
|
// and alloca() to allocate temporary memory during a frame on the
|
||||||
|
// stack. (Memory consumption will depend on the amount of setup
|
||||||
|
// data in the file and how you set the compile flags for speed
|
||||||
|
// vs. size. In my test files the maximal-size usage is ~150KB.)
|
||||||
|
//
|
||||||
|
// You can modify the wrapper functions in the source (setup_malloc,
|
||||||
|
// setup_temp_malloc, temp_malloc) to change this behavior, or you
|
||||||
|
// can use a simpler allocation model: you pass in a buffer from
|
||||||
|
// which stb_vorbis will allocate _all_ its memory (including the
|
||||||
|
// temp memory). "open" may fail with a VORBIS_outofmem if you
|
||||||
|
// do not pass in enough data; there is no way to determine how
|
||||||
|
// much you do need except to succeed (at which point you can
|
||||||
|
// query get_info to find the exact amount required. yes I know
|
||||||
|
// this is lame).
|
||||||
|
//
|
||||||
|
// If you pass in a non-NULL buffer of the type below, allocation
|
||||||
|
// will occur from it as described above. Otherwise just pass NULL
|
||||||
|
// to use malloc()/alloca()
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *alloc_buffer;
|
||||||
|
int alloc_buffer_length_in_bytes;
|
||||||
|
} stb_vorbis_alloc;
|
||||||
|
|
||||||
|
|
||||||
|
/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES
|
||||||
|
|
||||||
|
typedef struct stb_vorbis stb_vorbis;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned int sample_rate;
|
||||||
|
int channels;
|
||||||
|
|
||||||
|
unsigned int setup_memory_required;
|
||||||
|
unsigned int setup_temp_memory_required;
|
||||||
|
unsigned int temp_memory_required;
|
||||||
|
|
||||||
|
int max_frame_size;
|
||||||
|
} stb_vorbis_info;
|
||||||
|
|
||||||
|
// get general information about the file
|
||||||
|
extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f);
|
||||||
|
|
||||||
|
// get the last error detected (clears it, too)
|
||||||
|
extern int stb_vorbis_get_error(stb_vorbis *f);
|
||||||
|
|
||||||
|
// close an ogg vorbis file and free all memory in use
|
||||||
|
extern void stb_vorbis_close(stb_vorbis *f);
|
||||||
|
|
||||||
|
// this function returns the offset (in samples) from the beginning of the
|
||||||
|
// file that will be returned by the next decode, if it is known, or -1
|
||||||
|
// otherwise. after a flush_pushdata() call, this may take a while before
|
||||||
|
// it becomes valid again.
|
||||||
|
// NOT WORKING YET after a seek with PULLDATA API
|
||||||
|
extern int stb_vorbis_get_sample_offset(stb_vorbis *f);
|
||||||
|
|
||||||
|
// returns the current seek point within the file, or offset from the beginning
|
||||||
|
// of the memory buffer. In pushdata mode it returns 0.
|
||||||
|
extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f);
|
||||||
|
|
||||||
|
/////////// PUSHDATA API
|
||||||
|
|
||||||
|
#ifndef STB_VORBIS_NO_PUSHDATA_API
|
||||||
|
|
||||||
|
// this API allows you to get blocks of data from any source and hand
|
||||||
|
// them to stb_vorbis. you have to buffer them; stb_vorbis will tell
|
||||||
|
// you how much it used, and you have to give it the rest next time;
|
||||||
|
// and stb_vorbis may not have enough data to work with and you will
|
||||||
|
// need to give it the same data again PLUS more. Note that the Vorbis
|
||||||
|
// specification does not bound the size of an individual frame.
|
||||||
|
|
||||||
|
extern stb_vorbis *stb_vorbis_open_pushdata(
|
||||||
|
const unsigned char * datablock, int datablock_length_in_bytes,
|
||||||
|
int *datablock_memory_consumed_in_bytes,
|
||||||
|
int *error,
|
||||||
|
const stb_vorbis_alloc *alloc_buffer);
|
||||||
|
// create a vorbis decoder by passing in the initial data block containing
|
||||||
|
// the ogg&vorbis headers (you don't need to do parse them, just provide
|
||||||
|
// the first N bytes of the file--you're told if it's not enough, see below)
|
||||||
|
// on success, returns an stb_vorbis *, does not set error, returns the amount of
|
||||||
|
// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes;
|
||||||
|
// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed
|
||||||
|
// if returns NULL and *error is VORBIS_need_more_data, then the input block was
|
||||||
|
// incomplete and you need to pass in a larger block from the start of the file
|
||||||
|
|
||||||
|
extern int stb_vorbis_decode_frame_pushdata(
|
||||||
|
stb_vorbis *f,
|
||||||
|
const unsigned char *datablock, int datablock_length_in_bytes,
|
||||||
|
int *channels, // place to write number of float * buffers
|
||||||
|
float ***output, // place to write float ** array of float * buffers
|
||||||
|
int *samples // place to write number of output samples
|
||||||
|
);
|
||||||
|
// decode a frame of audio sample data if possible from the passed-in data block
|
||||||
|
//
|
||||||
|
// return value: number of bytes we used from datablock
|
||||||
|
//
|
||||||
|
// possible cases:
|
||||||
|
// 0 bytes used, 0 samples output (need more data)
|
||||||
|
// N bytes used, 0 samples output (resynching the stream, keep going)
|
||||||
|
// N bytes used, M samples output (one frame of data)
|
||||||
|
// note that after opening a file, you will ALWAYS get one N-bytes,0-sample
|
||||||
|
// frame, because Vorbis always "discards" the first frame.
|
||||||
|
//
|
||||||
|
// Note that on resynch, stb_vorbis will rarely consume all of the buffer,
|
||||||
|
// instead only datablock_length_in_bytes-3 or less. This is because it wants
|
||||||
|
// to avoid missing parts of a page header if they cross a datablock boundary,
|
||||||
|
// without writing state-machiney code to record a partial detection.
|
||||||
|
//
|
||||||
|
// The number of channels returned are stored in *channels (which can be
|
||||||
|
// NULL--it is always the same as the number of channels reported by
|
||||||
|
// get_info). *output will contain an array of float* buffers, one per
|
||||||
|
// channel. In other words, (*output)[0][0] contains the first sample from
|
||||||
|
// the first channel, and (*output)[1][0] contains the first sample from
|
||||||
|
// the second channel.
|
||||||
|
|
||||||
|
extern void stb_vorbis_flush_pushdata(stb_vorbis *f);
|
||||||
|
// inform stb_vorbis that your next datablock will not be contiguous with
|
||||||
|
// previous ones (e.g. you've seeked in the data); future attempts to decode
|
||||||
|
// frames will cause stb_vorbis to resynchronize (as noted above), and
|
||||||
|
// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it
|
||||||
|
// will begin decoding the _next_ frame.
|
||||||
|
//
|
||||||
|
// if you want to seek using pushdata, you need to seek in your file, then
|
||||||
|
// call stb_vorbis_flush_pushdata(), then start calling decoding, then once
|
||||||
|
// decoding is returning you data, call stb_vorbis_get_sample_offset, and
|
||||||
|
// if you don't like the result, seek your file again and repeat.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
////////// PULLING INPUT API
|
||||||
|
|
||||||
|
#ifndef STB_VORBIS_NO_PULLDATA_API
|
||||||
|
// This API assumes stb_vorbis is allowed to pull data from a source--
|
||||||
|
// either a block of memory containing the _entire_ vorbis stream, or a
|
||||||
|
// FILE * that you or it create, or possibly some other reading mechanism
|
||||||
|
// if you go modify the source to replace the FILE * case with some kind
|
||||||
|
// of callback to your code. (But if you don't support seeking, you may
|
||||||
|
// just want to go ahead and use pushdata.)
|
||||||
|
|
||||||
|
#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
|
||||||
|
extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output);
|
||||||
|
#endif
|
||||||
|
#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
|
||||||
|
extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output);
|
||||||
|
#endif
|
||||||
|
// decode an entire file and output the data interleaved into a malloc()ed
|
||||||
|
// buffer stored in *output. The return value is the number of samples
|
||||||
|
// decoded, or -1 if the file could not be opened or was not an ogg vorbis file.
|
||||||
|
// When you're done with it, just free() the pointer returned in *output.
|
||||||
|
|
||||||
|
extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len,
|
||||||
|
int *error, const stb_vorbis_alloc *alloc_buffer);
|
||||||
|
// create an ogg vorbis decoder from an ogg vorbis stream in memory (note
|
||||||
|
// this must be the entire stream!). on failure, returns NULL and sets *error
|
||||||
|
|
||||||
|
#ifndef STB_VORBIS_NO_STDIO
|
||||||
|
extern stb_vorbis * stb_vorbis_open_filename(const char *filename,
|
||||||
|
int *error, const stb_vorbis_alloc *alloc_buffer);
|
||||||
|
// create an ogg vorbis decoder from a filename via fopen(). on failure,
|
||||||
|
// returns NULL and sets *error (possibly to VORBIS_file_open_failure).
|
||||||
|
|
||||||
|
extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close,
|
||||||
|
int *error, const stb_vorbis_alloc *alloc_buffer);
|
||||||
|
// create an ogg vorbis decoder from an open FILE *, looking for a stream at
|
||||||
|
// the _current_ seek point (ftell). on failure, returns NULL and sets *error.
|
||||||
|
// note that stb_vorbis must "own" this stream; if you seek it in between
|
||||||
|
// calls to stb_vorbis, it will become confused. Morever, if you attempt to
|
||||||
|
// perform stb_vorbis_seek_*() operations on this file, it will assume it
|
||||||
|
// owns the _entire_ rest of the file after the start point. Use the next
|
||||||
|
// function, stb_vorbis_open_file_section(), to limit it.
|
||||||
|
|
||||||
|
extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close,
|
||||||
|
int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len);
|
||||||
|
// create an ogg vorbis decoder from an open FILE *, looking for a stream at
|
||||||
|
// the _current_ seek point (ftell); the stream will be of length 'len' bytes.
|
||||||
|
// on failure, returns NULL and sets *error. note that stb_vorbis must "own"
|
||||||
|
// this stream; if you seek it in between calls to stb_vorbis, it will become
|
||||||
|
// confused.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number);
|
||||||
|
extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number);
|
||||||
|
// these functions seek in the Vorbis file to (approximately) 'sample_number'.
|
||||||
|
// after calling seek_frame(), the next call to get_frame_*() will include
|
||||||
|
// the specified sample. after calling stb_vorbis_seek(), the next call to
|
||||||
|
// stb_vorbis_get_samples_* will start with the specified sample. If you
|
||||||
|
// do not need to seek to EXACTLY the target sample when using get_samples_*,
|
||||||
|
// you can also use seek_frame().
|
||||||
|
|
||||||
|
extern int stb_vorbis_seek_start(stb_vorbis *f);
|
||||||
|
// this function is equivalent to stb_vorbis_seek(f,0)
|
||||||
|
|
||||||
|
extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f);
|
||||||
|
extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f);
|
||||||
|
// these functions return the total length of the vorbis stream
|
||||||
|
|
||||||
|
extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output);
|
||||||
|
// decode the next frame and return the number of samples. the number of
|
||||||
|
// channels returned are stored in *channels (which can be NULL--it is always
|
||||||
|
// the same as the number of channels reported by get_info). *output will
|
||||||
|
// contain an array of float* buffers, one per channel. These outputs will
|
||||||
|
// be overwritten on the next call to stb_vorbis_get_frame_*.
|
||||||
|
//
|
||||||
|
// You generally should not intermix calls to stb_vorbis_get_frame_*()
|
||||||
|
// and stb_vorbis_get_samples_*(), since the latter calls the former.
|
||||||
|
|
||||||
|
#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
|
||||||
|
extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts);
|
||||||
|
extern int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples);
|
||||||
|
#endif
|
||||||
|
// decode the next frame and return the number of *samples* per channel.
|
||||||
|
// Note that for interleaved data, you pass in the number of shorts (the
|
||||||
|
// size of your array), but the return value is the number of samples per
|
||||||
|
// channel, not the total number of samples.
|
||||||
|
//
|
||||||
|
// The data is coerced to the number of channels you request according to the
|
||||||
|
// channel coercion rules (see below). You must pass in the size of your
|
||||||
|
// buffer(s) so that stb_vorbis will not overwrite the end of the buffer.
|
||||||
|
// The maximum buffer size needed can be gotten from get_info(); however,
|
||||||
|
// the Vorbis I specification implies an absolute maximum of 4096 samples
|
||||||
|
// per channel.
|
||||||
|
|
||||||
|
// Channel coercion rules:
|
||||||
|
// Let M be the number of channels requested, and N the number of channels present,
|
||||||
|
// and Cn be the nth channel; let stereo L be the sum of all L and center channels,
|
||||||
|
// and stereo R be the sum of all R and center channels (channel assignment from the
|
||||||
|
// vorbis spec).
|
||||||
|
// M N output
|
||||||
|
// 1 k sum(Ck) for all k
|
||||||
|
// 2 * stereo L, stereo R
|
||||||
|
// k l k > l, the first l channels, then 0s
|
||||||
|
// k l k <= l, the first k channels
|
||||||
|
// Note that this is not _good_ surround etc. mixing at all! It's just so
|
||||||
|
// you get something useful.
|
||||||
|
|
||||||
|
extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats);
|
||||||
|
extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples);
|
||||||
|
// gets num_samples samples, not necessarily on a frame boundary--this requires
|
||||||
|
// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES.
|
||||||
|
// Returns the number of samples stored per channel; it may be less than requested
|
||||||
|
// at the end of the file. If there are no more samples in the file, returns 0.
|
||||||
|
|
||||||
|
#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
|
||||||
|
extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts);
|
||||||
|
extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples);
|
||||||
|
#endif
|
||||||
|
// gets num_samples samples, not necessarily on a frame boundary--this requires
|
||||||
|
// buffering so you have to supply the buffers. Applies the coercion rules above
|
||||||
|
// to produce 'channels' channels. Returns the number of samples stored per channel;
|
||||||
|
// it may be less than requested at the end of the file. If there are no more
|
||||||
|
// samples in the file, returns 0.
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//////// ERROR CODES
|
||||||
|
|
||||||
|
enum STBVorbisError
|
||||||
|
{
|
||||||
|
VORBIS__no_error,
|
||||||
|
|
||||||
|
VORBIS_need_more_data = 1, // not a real error
|
||||||
|
|
||||||
|
VORBIS_invalid_api_mixing, // can't mix API modes
|
||||||
|
VORBIS_outofmem, // not enough memory
|
||||||
|
VORBIS_feature_not_supported, // uses floor 0
|
||||||
|
VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small
|
||||||
|
VORBIS_file_open_failure, // fopen() failed
|
||||||
|
VORBIS_seek_without_length, // can't seek in unknown-length file
|
||||||
|
|
||||||
|
VORBIS_unexpected_eof = 10, // file is truncated?
|
||||||
|
VORBIS_seek_invalid, // seek past EOF
|
||||||
|
|
||||||
|
// decoding errors (corrupt/invalid stream) -- you probably
|
||||||
|
// don't care about the exact details of these
|
||||||
|
|
||||||
|
// vorbis errors:
|
||||||
|
VORBIS_invalid_setup = 20,
|
||||||
|
VORBIS_invalid_stream,
|
||||||
|
|
||||||
|
// ogg errors:
|
||||||
|
VORBIS_missing_capture_pattern = 30,
|
||||||
|
VORBIS_invalid_stream_structure_version,
|
||||||
|
VORBIS_continued_packet_flag_invalid,
|
||||||
|
VORBIS_incorrect_stream_serial_number,
|
||||||
|
VORBIS_invalid_first_page,
|
||||||
|
VORBIS_bad_packet_type,
|
||||||
|
VORBIS_cant_find_last_page,
|
||||||
|
VORBIS_seek_failed
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H
|
||||||
|
//
|
||||||
|
// HEADER ENDS HERE
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Loading…
Add table
Reference in a new issue