diff --git a/Config/Games.xml b/Config/Games.xml
index 621b210..180ae98 100644
--- a/Config/Games.xml
+++ b/Config/Games.xml
@@ -174,6 +174,7 @@
Wheel
24
true
+
@@ -266,6 +267,7 @@
DSB2
Wheel
true
+
@@ -551,6 +553,7 @@
Sega Model 3
2.1
Wheel
+
@@ -632,6 +635,7 @@
Sega Model 3
2.1
Wheel
+
@@ -678,6 +682,7 @@
Sega Model 3
2.1
Wheel
+
@@ -711,6 +716,7 @@
Sega Model 3
2.1
Wheel
+
@@ -1030,6 +1036,7 @@
1.5
Wheel
true
+
@@ -1360,6 +1367,7 @@
DSB1
Wheel
true
+
@@ -1444,6 +1452,7 @@
DSB1
Wheel
true
+
@@ -1478,6 +1487,7 @@
DSB1
Wheel
true
+
@@ -1550,6 +1560,7 @@
DSB1
Wheel
true
+
@@ -1597,6 +1608,7 @@
Wheel
MPC106
true
+
@@ -1891,6 +1903,7 @@
DSB2
Wheel
true
+
@@ -1989,6 +2002,7 @@
2.0
DSB2
Wheel
+
@@ -2098,6 +2112,7 @@
DSB2
Wheel
48
+
diff --git a/Src/Game.h b/Src/Game.h
index 4ffbf33..eea1c45 100644
--- a/Src/Game.h
+++ b/Src/Game.h
@@ -14,6 +14,18 @@ struct Game
unsigned year = 0;
std::string stepping;
std::string mpeg_board;
+ enum AudioTypes
+ {
+ MONO = 0, // Merge DSB+SCSP1+SCSP2 to 1 channel mono
+ STEREO_LR, // Merge DSP+SCSP1+SCSP2 to 2 channels stereo Left/Right (most common)
+ STEREO_RL, // Merge DSP+SCSP1+SCSP2 to 2 channels stereo reversed Right/Left
+ QUAD_1_FLR_2_RLR, // Split DSB+SCSP1 to FrontLeft/FrontRight and SCSP2 to RearLeft/RearRight (Daytona2)
+ QUAD_1_FRL_2_RRL, // Split DSB+SCSP1 to FrontRight/FrontLeft and SCSP2 to RearRight/RearLeft
+ QUAD_1_RLR_2_FLR, // Split DSB+SCSP1 to RearLeft/RearRight and SCSP2 to FrontLeft/FrontRight
+ QUAD_1_RRL_2_FRL, // Split DSB+SCSP1 to RearRight/RearLeft and SCSP2 to FrontRight/FrontLeft
+ QUAD_1_LR_2_FR_MIX, // Specific srally2: Split SCSP2 and mix first channel to DSB+SCP11 Front Left/Right and second to Read Left/Right
+ };
+ AudioTypes audio = STEREO_LR;
std::string pci_bridge; // overrides default PCI bridge type for stepping (empty string for default)
uint32_t real3d_pci_id = 0; // overrides default Real3D PCI ID for stepping (0 for default)
float real3d_status_bit_set_percent_of_frame = 0; // overrides default status bit timing (0 for default)
diff --git a/Src/GameLoader.cpp b/Src/GameLoader.cpp
index 152fed5..5db434e 100644
--- a/Src/GameLoader.cpp
+++ b/Src/GameLoader.cpp
@@ -198,6 +198,20 @@ static void PopulateGameInfo(Game *game, const Util::Config::Node &game_node)
game->year = game_node["identity/year"].ValueAsDefault(0);
game->stepping = game_node["hardware/stepping"].ValueAsDefault("");
game->mpeg_board = game_node["hardware/mpeg_board"].ValueAsDefault("");
+ std::map audio_types
+ {
+ { "", Game::STEREO_LR }, // default to stereo
+ { "Mono", Game::MONO },
+ { "Stereo", Game::STEREO_LR },
+ { "StereoReversed", Game::STEREO_RL },
+ { "QuadFrontRear", Game::QUAD_1_FLR_2_RLR },
+ { "QuadFrontRearReversed", Game::QUAD_1_FRL_2_RRL },
+ { "QuadRearFront", Game::QUAD_1_RLR_2_FLR },
+ { "QuadRearFrontReversed", Game::QUAD_1_RRL_2_FRL },
+ { "QuadMix", Game::QUAD_1_LR_2_FR_MIX}
+ };
+ std::string audio_type = game_node["hardware/audio"].ValueAsDefault(std::string());
+ game->audio = audio_types[audio_type];
game->pci_bridge = game_node["hardware/pci_bridge"].ValueAsDefault("");
game->real3d_pci_id = game_node["hardware/real3d_pci_id"].ValueAsDefault(0);
game->real3d_status_bit_set_percent_of_frame = game_node["hardware/real3d_status_bit_set_percent_of_frame"].ValueAsDefault(0);
@@ -250,80 +264,80 @@ static void PopulateGameInfo(Game *game, const Util::Config::Node &game_node)
}
bool GameLoader::LoadGamesFromXML(const Util::Config::Node &xml)
-{
- for (auto it = xml.begin(); it != xml.end(); ++it)
- {
- // Root games node
- auto &root_node = *it;
- if (root_node.Key() != "games")
- continue;
- for (auto &game_node: root_node)
- {
- // Game node
- if (game_node.Key() != "game")
- continue;
- if (game_node["name"].Empty())
- {
- //TODO: associate line numbers in config
- //ErrorLog("%s: Ignoring tag with missing 'name' attribute.", m_xml_filename.c_str());
- continue;
- }
- std::string game_name = game_node["name"].ValueAs();
- if (m_regions_by_game.find(game_name) != m_regions_by_game.end())
- {
- ErrorLog("%s: Ignoring redefinition of game '%s'.", m_xml_filename.c_str(), game_name.c_str());
- continue;
- }
+{
+ for (auto it = xml.begin(); it != xml.end(); ++it)
+ {
+ // Root games node
+ auto &root_node = *it;
+ if (root_node.Key() != "games")
+ continue;
+ for (auto &game_node: root_node)
+ {
+ // Game node
+ if (game_node.Key() != "game")
+ continue;
+ if (game_node["name"].Empty())
+ {
+ //TODO: associate line numbers in config
+ //ErrorLog("%s: Ignoring tag with missing 'name' attribute.", m_xml_filename.c_str());
+ continue;
+ }
+ std::string game_name = game_node["name"].ValueAs();
+ if (m_regions_by_game.find(game_name) != m_regions_by_game.end())
+ {
+ ErrorLog("%s: Ignoring redefinition of game '%s'.", m_xml_filename.c_str(), game_name.c_str());
+ continue;
+ }
RegionsByName_t ®ions_by_name = m_regions_by_game[game_name];
- PatchesByRegion_t &patches_by_region = m_patches_by_game[game_name];
- PopulateGameInfo(&m_game_info_by_game[game_name], game_node);
-
- for (auto &roms_node: game_node)
- {
- if (roms_node.Key() != "roms")
- continue;
-
- /*
- * Regions define contiguous memory areas that individual ROM files are
- * loaded into. It is possible to have multiple region tags identifying
- * the same region. They will be aggregated. This is useful for parent
- * and child ROM sets, which each may need to define the same region,
- * with the child set loading in different files to overwrite the parent
- * set.
- */
- for (auto ®ion_node: roms_node)
- {
- if (region_node.Key() != "region")
- continue;
-
- // Look up region structure or create new one if needed
- std::string region_name = region_node["name"].Value();
- auto it = regions_by_name.find(region_name);
- Region::ptr_t region = (it != regions_by_name.end()) ? it->second : Region::Create(*this, region_node);
- if (!region)
- continue;
-
- /*
- * Files are defined by the offset they are loaded at. Normally, there
- * should be one file per offset but parent/child ROM sets will violate
- * this, and so it is allowed.
- */
- std::vector &files = region->files;
- for (auto &file_node: region_node)
- {
- if (file_node.Key() != "file")
- continue;
- File::ptr_t file = File::Create(*this, file_node);
- if (!file)
- continue;
- files.push_back(file);
- }
-
- // Check to ensure that some files were defined in the region
- if (files.empty())
- ErrorLog("%s: No files defined in region '%s' of '%s'.", m_xml_filename.c_str(), region->region_name.c_str(), game_name.c_str());
- else
- regions_by_name[region->region_name] = region;
+ PatchesByRegion_t &patches_by_region = m_patches_by_game[game_name];
+ PopulateGameInfo(&m_game_info_by_game[game_name], game_node);
+
+ for (auto &roms_node: game_node)
+ {
+ if (roms_node.Key() != "roms")
+ continue;
+
+ /*
+ * Regions define contiguous memory areas that individual ROM files are
+ * loaded into. It is possible to have multiple region tags identifying
+ * the same region. They will be aggregated. This is useful for parent
+ * and child ROM sets, which each may need to define the same region,
+ * with the child set loading in different files to overwrite the parent
+ * set.
+ */
+ for (auto ®ion_node: roms_node)
+ {
+ if (region_node.Key() != "region")
+ continue;
+
+ // Look up region structure or create new one if needed
+ std::string region_name = region_node["name"].Value();
+ auto it = regions_by_name.find(region_name);
+ Region::ptr_t region = (it != regions_by_name.end()) ? it->second : Region::Create(*this, region_node);
+ if (!region)
+ continue;
+
+ /*
+ * Files are defined by the offset they are loaded at. Normally, there
+ * should be one file per offset but parent/child ROM sets will violate
+ * this, and so it is allowed.
+ */
+ std::vector &files = region->files;
+ for (auto &file_node: region_node)
+ {
+ if (file_node.Key() != "file")
+ continue;
+ File::ptr_t file = File::Create(*this, file_node);
+ if (!file)
+ continue;
+ files.push_back(file);
+ }
+
+ // Check to ensure that some files were defined in the region
+ if (files.empty())
+ ErrorLog("%s: No files defined in region '%s' of '%s'.", m_xml_filename.c_str(), region->region_name.c_str(), game_name.c_str());
+ else
+ regions_by_name[region->region_name] = region;
}
// ROM patches, if any
@@ -348,17 +362,17 @@ bool GameLoader::LoadGamesFromXML(const Util::Config::Node &xml)
else
patches_by_region[region].push_back(ROM::BigEndianPatch(offset, value, bits));
}
- }
- }
-
- // Check to ensure that some ROM regions were defined for the game
- if (regions_by_name.empty())
- ErrorLog("%s: No ROM regions defined for '%s'.", m_xml_filename.c_str(), game_name.c_str());
- }
- }
- // Check to ensure some games were defined
- if (m_regions_by_game.empty())
- {
+ }
+ }
+
+ // Check to ensure that some ROM regions were defined for the game
+ if (regions_by_name.empty())
+ ErrorLog("%s: No ROM regions defined for '%s'.", m_xml_filename.c_str(), game_name.c_str());
+ }
+ }
+ // Check to ensure some games were defined
+ if (m_regions_by_game.empty())
+ {
ErrorLog("%s: No games defined.", m_xml_filename.c_str());
return true;
}
diff --git a/Src/Graphics/Legacy3D/Models.cpp b/Src/Graphics/Legacy3D/Models.cpp
index f86db71..ceb3683 100644
--- a/Src/Graphics/Legacy3D/Models.cpp
+++ b/Src/Graphics/Legacy3D/Models.cpp
@@ -725,7 +725,7 @@ void CLegacy3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P,
// Specular shininess
GLfloat specularCoefficient = (GLfloat) ((P->header[0]>>26) & 0x3F) * (1.0f/63.0f);
int shinyBits = (P->header[6] >> 5) & 3;
- float shininess = pow(2.0f, 1+shinyBits);
+ float shininess = std::pow(2.0f, 1.f + shinyBits);
if (!(P->header[0]&0x80)) //|| (shininess == 0)) // bit 0x80 seems to enable specular lighting
{
specularCoefficient = 0.; // disable
diff --git a/Src/Model3/IEmulator.h b/Src/Model3/IEmulator.h
index e1f2942..46ab331 100644
--- a/Src/Model3/IEmulator.h
+++ b/Src/Model3/IEmulator.h
@@ -163,7 +163,7 @@ public:
virtual void AttachInputs(CInputs *InputsPtr) = 0;
/*
- * AttachInputs(InputsPtr):
+ * AttachOutputs(InputsPtr):
*
* Attaches OSD-managed outputs (cabinet lamps, etc.)
*
diff --git a/Src/Model3/Model3.cpp b/Src/Model3/Model3.cpp
index 6d6811c..5250c32 100644
--- a/Src/Model3/Model3.cpp
+++ b/Src/Model3/Model3.cpp
@@ -472,6 +472,11 @@ UINT8 CModel3::ReadInputs(unsigned reg)
}
return data;
+ case 0x10: // Drive board
+ return OutputRegister[0];
+ case 0x14: // Lamps
+ return OutputRegister[1];
+
case 0x0C: // game-specific inputs
data = 0xFF;
@@ -651,9 +656,11 @@ void CModel3::WriteInputs(unsigned reg, UINT8 data)
break;
case 0x10: // Drive board
- DriveBoard->Write(data);
+ if (DriveBoard->IsAttached())
+ DriveBoard->Write(data);
if (NULL != Outputs) // TODO - check gameInputs
Outputs->SetValue(OutputRawDrive, data);
+ OutputRegister[0] = data;
break;
case 0x14: // Lamp outputs (Daytona/Scud Race/Sega Rally/Le Mans 24)
@@ -667,6 +674,7 @@ void CModel3::WriteInputs(unsigned reg, UINT8 data)
Outputs->SetValue(OutputLampLeader, !!(data&0x80));
Outputs->SetValue(OutputRawLamps, data);
}
+ OutputRegister[1] = data;
break;
case 0x24: // Serial FIFO 1
@@ -2017,7 +2025,8 @@ void CModel3::RunFrame(void)
}
timings.frameTicks = CThread::GetTicks() - start;
-
+ // Frame counter
+ timings.frameId++;
return;
ThreadError:
@@ -2810,7 +2819,8 @@ void CModel3::Reset(void)
NetBoard->Reset();
#endif
timings.frameTicks = 0;
-
+ timings.frameId = 0;
+
DebugLog("Model 3 reset\n");
}
@@ -3176,7 +3186,7 @@ INetBoard *CModel3::GetNetBoard(void)
}
#endif
-CModel3::CModel3(const Util::Config::Node &config)
+CModel3::CModel3(Util::Config::Node &config)
: m_config(config),
m_multiThreaded(config["MultiThreaded"].ValueAs()),
m_gpuMultiThreaded(config["GPUMultiThreaded"].ValueAs()),
diff --git a/Src/Model3/Model3.h b/Src/Model3/Model3.h
index fe6471a..07a515c 100644
--- a/Src/Model3/Model3.h
+++ b/Src/Model3/Model3.h
@@ -63,6 +63,7 @@ struct FrameTimings
UINT32 netTicks;
#endif
UINT32 frameTicks;
+ UINT64 frameId;
};
/*
@@ -95,6 +96,8 @@ public:
void AttachInputs(CInputs *InputsPtr);
void AttachOutputs(COutputs *OutputsPtr);
bool Init(void);
+ // For Scripting tweaks
+ Util::Config::Node& GetConfig() { return this->m_config; }
// IPCIDevice interface
UINT32 ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width);
@@ -184,7 +187,7 @@ public:
* config Run-time configuration. The reference should be held because
* this changes at run-time.
*/
- CModel3(const Util::Config::Node &config);
+ CModel3(Util::Config::Node &config);
~CModel3(void);
/*
@@ -228,7 +231,7 @@ private:
int RunDriveBoardThread(void); // Runs drive board thread (sync'd in step with render thread)
// Runtime configuration
- const Util::Config::Node &m_config;
+ Util::Config::Node &m_config;
bool m_multiThreaded;
bool m_gpuMultiThreaded;
@@ -262,6 +265,7 @@ private:
UINT8 *driveROM; // 32 KB drive board ROM (Z80 program) (optional)
UINT8 *netRAM; // 64 KB RAM
UINT8 *netBuffer; // 128 KB buffer
+ UINT8 OutputRegister[2]; // Input/output register for driveboard and lamps
// Banked CROM
UINT8 *cromBank; // currently mapped in CROM bank
@@ -321,7 +325,6 @@ private:
INetBoard *NetBoard; // Net board
bool m_runNetBoard;
#endif
-
};
diff --git a/Src/Model3/SoundBoard.cpp b/Src/Model3/SoundBoard.cpp
index 52f0c30..2ae6cff 100644
--- a/Src/Model3/SoundBoard.cpp
+++ b/Src/Model3/SoundBoard.cpp
@@ -350,16 +350,25 @@ bool CSoundBoard::RunFrame(void)
}
else
{
- memset(audioL, 0, 44100/60*sizeof(INT16));
- memset(audioR, 0, 44100/60*sizeof(INT16));
+ memset(audioFL, 0, 44100/60*sizeof(INT16));
+ memset(audioFR, 0, 44100/60*sizeof(INT16));
+ memset(audioRL, 0, 44100/60*sizeof(INT16));
+ memset(audioRR, 0, 44100/60*sizeof(INT16));
}
// Run DSB and mix with existing audio
- if (NULL != DSB)
- DSB->RunFrame(audioL, audioR);
+ if (NULL != DSB) {
+ // Will need to mix with proper front, rear channels or both (game specific)
+ bool mixDSBWithFront = true; // Everything to front channels for now
+ // Case "both" not handled for now
+ if (mixDSBWithFront)
+ DSB->RunFrame(audioFL, audioFR);
+ else
+ DSB->RunFrame(audioRL, audioRR);
+ }
// Output the audio buffers
- bool bufferFull = OutputAudio(44100/60, audioL, audioR, m_config["FlipStereo"].ValueAs());
+ bool bufferFull = OutputAudio(44100/60, audioFL, audioFR, audioRL, audioRR, m_config["FlipStereo"].ValueAs());
#ifdef SUPERMODEL_LOG_AUDIO
// Output to binary file
@@ -446,9 +455,13 @@ void CSoundBoard::AttachDSB(CDSB *DSBPtr)
// Offsets of memory regions within sound board's pool
#define OFFSET_RAM1 0 // 1 MB SCSP1 RAM
#define OFFSET_RAM2 0x100000 // 1 MB SCSP2 RAM
-#define OFFSET_AUDIO_LEFT 0x200000 // 1470 bytes (16 bits, 44.1 KHz, 1/60th second) left audio channel
-#define OFFSET_AUDIO_RIGHT 0x2005BE // 1470 bytes right audio channel
-#define MEMORY_POOL_SIZE (0x100000 + 0x100000 + 0x5BE + 0x5BE)
+#define LENGTH_CHANNEL_BUFFER 0x5BE
+#define OFFSET_AUDIO_FRONTLEFT 0x200000 // 1470 bytes (16 bits, 44.1 KHz, 1/60th second) left audio channel
+#define OFFSET_AUDIO_FRONTRIGHT (OFFSET_AUDIO_FRONTLEFT + LENGTH_CHANNEL_BUFFER) // 1470 bytes right audio channel
+#define OFFSET_AUDIO_REARLEFT (OFFSET_AUDIO_FRONTRIGHT + LENGTH_CHANNEL_BUFFER) // 1470 bytes (16 bits, 44.1 KHz, 1/60th second) left audio channel
+#define OFFSET_AUDIO_REARRIGHT (OFFSET_AUDIO_REARLEFT + LENGTH_CHANNEL_BUFFER) // 1470 bytes right audio channel
+
+#define MEMORY_POOL_SIZE (0x100000 + 0x100000 + 4*LENGTH_CHANNEL_BUFFER)
bool CSoundBoard::Init(const UINT8 *soundROMPtr, const UINT8 *sampleROMPtr)
{
@@ -469,9 +482,11 @@ bool CSoundBoard::Init(const UINT8 *soundROMPtr, const UINT8 *sampleROMPtr)
// Set up memory pointers
ram1 = &memoryPool[OFFSET_RAM1];
ram2 = &memoryPool[OFFSET_RAM2];
- audioL = (INT16 *) &memoryPool[OFFSET_AUDIO_LEFT];
- audioR = (INT16 *) &memoryPool[OFFSET_AUDIO_RIGHT];
-
+ audioFL = (INT16*)&memoryPool[OFFSET_AUDIO_FRONTLEFT];
+ audioFR = (INT16*)&memoryPool[OFFSET_AUDIO_FRONTRIGHT];
+ audioRL = (INT16*)&memoryPool[OFFSET_AUDIO_REARLEFT];
+ audioRR = (INT16*)&memoryPool[OFFSET_AUDIO_REARRIGHT];
+
// Initialize 68K core
M68KSetContext(&M68K);
M68KInit();
@@ -480,7 +495,7 @@ bool CSoundBoard::Init(const UINT8 *soundROMPtr, const UINT8 *sampleROMPtr)
M68KGetContext(&M68K);
// Initialize SCSPs
- SCSP_SetBuffers(audioL, audioR, 44100/60);
+ SCSP_SetBuffers(audioFL, audioFR, audioRL, audioRR, 44100/60);
SCSP_SetCB(SCSP68KRunCallback, SCSP68KIRQCallback);
if (OKAY != SCSP_Init(m_config, 2))
return FAIL;
@@ -514,8 +529,10 @@ CSoundBoard::CSoundBoard(const Util::Config::Node &config)
memoryPool = NULL;
ram1 = NULL;
ram2 = NULL;
- audioL = NULL;
- audioR = NULL;
+ audioFL = NULL;
+ audioFR = NULL;
+ audioRL = NULL;
+ audioRR = NULL;
soundROM = NULL;
sampleROM = NULL;
@@ -553,8 +570,10 @@ CSoundBoard::~CSoundBoard(void)
}
ram1 = NULL;
ram2 = NULL;
- audioL = NULL;
- audioR = NULL;
+ audioFL = NULL;
+ audioFR = NULL;
+ audioRL = NULL;
+ audioRR = NULL;
soundROM = NULL;
sampleROM = NULL;
diff --git a/Src/Model3/SoundBoard.h b/Src/Model3/SoundBoard.h
index f840d12..a8730ec 100644
--- a/Src/Model3/SoundBoard.h
+++ b/Src/Model3/SoundBoard.h
@@ -203,7 +203,8 @@ private:
UINT8 ctrlReg; // control register: ROM banking
// Audio
- INT16 *audioL, *audioR; // left and right audio channels (1/60th second, 44.1 KHz)
+ INT16* audioFL, * audioFR; // left and right front audio channels (1/60th second, 44.1 KHz)
+ INT16* audioRL, * audioRR; // left and right rear audio channels (1/60th second, 44.1 KHz)
};
diff --git a/Src/OSD/Audio.h b/Src/OSD/Audio.h
index 7f732b4..c49bdd2 100755
--- a/Src/OSD/Audio.h
+++ b/Src/OSD/Audio.h
@@ -25,36 +25,39 @@
* Header file for OS-dependent audio playback interface.
*/
-#ifndef INCLUDED_AUDIO_H
-#define INCLUDED_AUDIO_H
-
-#include "Types.h"
+#ifndef INCLUDED_AUDIO_H
+#define INCLUDED_AUDIO_H
+
+#include "Types.h"
+#include "Util/NewConfig.h"
+#include "Game.h"
typedef void (*AudioCallbackFPtr)(void *data);
extern void SetAudioCallback(AudioCallbackFPtr callback, void *data);
extern void SetAudioEnabled(bool enabled);
+extern void SetAudioType(Game::AudioTypes type);
/*
* OpenAudio()
*
* Initializes the audio system.
*/
-extern bool OpenAudio();
+extern bool OpenAudio(const Util::Config::Node& config);
/*
* OutputAudio(unsigned numSamples, *INT16 leftBuffer, *INT16 rightBuffer)
*
* Sends a chunk of two-channel audio with the given number of samples to the audio system.
*/
-extern bool OutputAudio(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer, bool flipStereo);
+extern bool OutputAudio(unsigned numSamples, INT16* leftFrontBuffer, INT16* rightFrontBuffer, INT16* leftRearBuffer, INT16* rightRearBuffer, bool flipStereo);
/*
* CloseAudio()
*
* Shuts down the audio system.
*/
-extern void CloseAudio();
-
-#endif // INCLUDED_AUDIO_H
+extern void CloseAudio();
+
+#endif // INCLUDED_AUDIO_H
diff --git a/Src/OSD/Outputs.cpp b/Src/OSD/Outputs.cpp
index a944e90..2fd16e5 100755
--- a/Src/OSD/Outputs.cpp
+++ b/Src/OSD/Outputs.cpp
@@ -104,3 +104,12 @@ void COutputs::SetValue(EOutputs output, UINT8 value)
if (firstSet || value != prevValue)
SendOutput(output, prevValue, value);
}
+
+bool COutputs::HasValue(EOutputs output)
+{
+ int idx = (unsigned)output;
+ if (idx < 0 || idx >= NUM_OUTPUTS)
+ return false;
+ return !m_first[output];
+}
+
diff --git a/Src/OSD/Outputs.h b/Src/OSD/Outputs.h
index e2ed37a..23e3066 100755
--- a/Src/OSD/Outputs.h
+++ b/Src/OSD/Outputs.h
@@ -124,6 +124,14 @@ public:
*/
void SetValue(EOutputs output, UINT8 value);
+ /*
+ * HasValue(EOutputs output)
+ *
+ * Returns if the value has been set at least once,
+ * meaning it is used by the game.
+ */
+ bool HasValue(EOutputs output);
+
protected:
/*
* COutputs():
diff --git a/Src/OSD/SDL/Audio.cpp b/Src/OSD/SDL/Audio.cpp
index 725dbd5..b8b71cc 100755
--- a/Src/OSD/SDL/Audio.cpp
+++ b/Src/OSD/SDL/Audio.cpp
@@ -19,16 +19,39 @@
** with Supermodel. If not, see .
**/
-/*
- * Audio.cpp
- *
- * SDL audio playback. Implements the OSD audio interface.
- *
- * Buffer sizes and read/write positions must be sample-aligned. A sample is
- * defined to encompass both channels so for, e.g., 16-bit audio as used here,
- * a sample is 4 bytes. Static assertions are employed to ensure that the
- * initial set up of the buffer is correct.
- */
+ /*
+ * Audio.cpp
+ *
+ * SDL audio playback. Implements the OSD audio interface.
+ *
+ * Buffer sizes and read/write positions must be sample-aligned. A sample is
+ * defined to encompass both channels so for, e.g., 16-bit audio as used here,
+ * a sample is 4 bytes. Static assertions are employed to ensure that the
+ * initial set up of the buffer is correct.
+ *
+ * Model 3 Audio is always 4 channels. SCSP1 is usually for each front
+ * channels (on CN8 connector) and SCSP2 for rear channels (on CN7).
+ * The downmix to 2 channels will be performed here in case supermodel audio
+ * subsystem does not allow such playback.
+ * The default DSB board is supposed to be plug and mixed with the front output
+ * channel. The rear channel is usually plugged to the gullbow speakers that
+ * are present in most model 3 racing cabinets, while front speakers are only
+ * present in Daytona2, Scud Race, Sega Rally 2.
+ * As each cabinet as its own wiring, with different mixing, the games.xml
+ * database will provide which can of mixing should be applied for each game.
+ *
+ * From twin uk cabinet diagrams, at least:
+ * - lemans24: only stereo on rear speakers (gullbox) on front channels SCSP1/CN8.
+ * - scud race: DSB mixed with SCSP2/CN7 for rear (gullbox) speakers, front
+ * connected to SCSP1/CN8.
+ * - daytona2: front on SCSP1/CN8, rear on SCSP2/CN7
+ * - srally2: SCSP2/CN7 gives left/right, and SCSP1/CN8 is split in 2 channels:
+ * mono front, mono rear. These two channels are them mixed with L/R to give
+ * the quad output.
+ * - oceanhun: SCSP1/CN8 and SCSP2/CN7 are mixed together for stereo output.
+ * Others are unknown so far, but it is expected that most Step 2.x games should
+ * have quad output.
+ */
#include "Audio.h"
@@ -38,16 +61,38 @@
#include
#include
-// Model3 audio output is 44.1KHz 2-channel sound and frame rate is 60fps
-#define SAMPLE_RATE 44100
-#define NUM_CHANNELS 2
-#define SUPERMODEL_FPS 60
+ // Model3 audio output is 44.1KHz 4-channel sound and frame rate is 60fps
+#define SAMPLE_RATE_M3 (44100)
+#define SUPERMODEL_FPS (60.0f)
+#define MODEL3_FPS (57.53f)
-#define BYTES_PER_SAMPLE (NUM_CHANNELS * sizeof(INT16))
-#define SAMPLES_PER_FRAME (SAMPLE_RATE / SUPERMODEL_FPS)
-#define BYTES_PER_FRAME (SAMPLES_PER_FRAME * BYTES_PER_SAMPLE)
+#define MAX_SND_FREQ (75)
+#define MIN_SND_FREQ (45)
+#define MAX_LATENCY (100)
-#define MAX_LATENCY 100
+#define NUM_CHANNELS_M3 (4)
+
+Game::AudioTypes AudioType;
+int nbHostAudioChannels = NUM_CHANNELS_M3; // Number of channels on host
+
+#define SAMPLES_PER_FRAME_M3 (INT32)(SAMPLE_RATE_M3 / MODEL3_FPS)
+
+#define BYTES_PER_SAMPLE_M3 (NUM_CHANNELS_M3 * sizeof(INT16))
+#define BYTES_PER_FRAME_M3 (SAMPLES_PER_FRAME_M3 * BYTES_PER_SAMPLE_M3)
+
+
+static int samples_per_frame_host = SAMPLES_PER_FRAME_M3;
+static int bytes_per_sample_host = BYTES_PER_SAMPLE_M3;
+static int bytes_per_frame_host = BYTES_PER_FRAME_M3;
+
+// Balance percents for mixer
+float BalanceLeftRight = 0; // 0 mid balance, 100: left only, -100:right only
+float BalanceFrontRear = 0; // 0 mid balance, 100: front only, -100:right only
+// Mixer factor (depends on values above)
+float balanceFactorFrontLeft = 1.0f;
+float balanceFactorFrontRight = 1.0f;
+float balanceFactorRearLeft = 1.0f;
+float balanceFactorRearRight = 1.0f;
static bool enabled = true; // True if sound output is enabled
static constexpr unsigned latency = 20; // Audio latency to use (ie size of audio buffer) as percentage of max buffer size
@@ -56,7 +101,7 @@ static constexpr bool underRunLoop = true; // True if should loop back to beg
static constexpr unsigned playSamples = 512; // Size (in samples) of callback play buffer
static UINT32 audioBufferSize = 0; // Size (in bytes) of audio buffer
-static INT8 *audioBuffer = NULL; // Audio buffer
+static INT8* audioBuffer = NULL; // Audio buffer
static UINT32 writePos = 0; // Current position at which writing into buffer
static UINT32 playPos = 0; // Current position at which playing data in buffer via callback
@@ -67,378 +112,572 @@ static unsigned underRuns = 0; // Number of buffer under-runs that have occ
static unsigned overRuns = 0; // Number of buffer over-runs that have occured
static AudioCallbackFPtr callback = NULL; // Pointer to audio callback that is called when audio buffer is less than half empty
-static void *callbackData = NULL; // Pointer to data to be passed to audio callback when it is called
+static void* callbackData = NULL; // Pointer to data to be passed to audio callback when it is called
-void SetAudioCallback(AudioCallbackFPtr newCallback, void *newData)
+static const Util::Config::Node* s_config = 0;
+
+
+void SetAudioCallback(AudioCallbackFPtr newCallback, void* newData)
{
- // Lock audio whilst changing callback pointers
- SDL_LockAudio();
+ // Lock audio whilst changing callback pointers
+ SDL_LockAudio();
- callback = newCallback;
- callbackData = newData;
+ callback = newCallback;
+ callbackData = newData;
- SDL_UnlockAudio();
+ SDL_UnlockAudio();
}
void SetAudioEnabled(bool newEnabled)
{
- enabled = newEnabled;
+ enabled = newEnabled;
}
-static void PlayCallback(void *data, Uint8 *stream, int len)
+///
+/// Set game audio mixing type
+///
+///
+void SetAudioType(Game::AudioTypes type)
{
- //printf("PlayCallback(%d) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u]\n",
- // len, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize);
+ AudioType = type;
+}
- // Get current write position and adjust it if write has wrapped but play position has not
- UINT32 adjWritePos = writePos;
- if (writeWrapped)
- adjWritePos += audioBufferSize;
+static INT16 AddAndClampINT16(INT32 x, INT32 y)
+{
+ INT32 sum = x + y;
+ if (sum > INT16_MAX) {
+ sum = INT16_MAX;
+ }
+ if (sum < INT16_MIN) {
+ sum = INT16_MIN;
+ }
+ return (INT16)sum;
+}
- // Check if play position overlaps write position (ie buffer under-run)
+static void PlayCallback(void* data, Uint8* stream, int len)
+{
+ //printf("PlayCallback(%d) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u]\n",
+ // len, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize);
+
+ // Get current write position and adjust it if write has wrapped but play position has not
+ UINT32 adjWritePos = writePos;
+ if (writeWrapped)
+ adjWritePos += audioBufferSize;
+
+ // Check if play position overlaps write position (ie buffer under-run)
if (playPos + len > adjWritePos)
{
- underRuns++;
+ underRuns++;
- //printf("Audio buffer under-run #%u in PlayCallback(%d) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u]\n",
- // underRuns, len, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize);
+ //printf("Audio buffer under-run #%u in PlayCallback(%d) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u]\n",
+ // underRuns, len, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize);
- // See what action to take on under-run
+ // See what action to take on under-run
if (underRunLoop)
{
- // If loop, then move play position back to beginning of data in buffer
- playPos = adjWritePos + BYTES_PER_FRAME;
+ // If loop, then move play position back to beginning of data in buffer
+ playPos = adjWritePos + bytes_per_frame_host;
- // Check if play position has moved past end of buffer
- if (playPos >= audioBufferSize)
- // If so, wrap it around to beginning again (but keep write wrapped flag as before)
- playPos -= audioBufferSize;
- else
- // Otherwise, set write wrapped flag as will now appear as if write has wrapped but play position has not
- writeWrapped = true;
+ // Check if play position has moved past end of buffer
+ if (playPos >= audioBufferSize)
+ // If so, wrap it around to beginning again (but keep write wrapped flag as before)
+ playPos -= audioBufferSize;
+ else
+ // Otherwise, set write wrapped flag as will now appear as if write has wrapped but play position has not
+ writeWrapped = true;
}
else
{
- // Otherwise, just copy silence to audio output stream and exit
- memset(stream, 0, len);
- return;
- }
- }
+ // Otherwise, just copy silence to audio output stream and exit
+ memset(stream, 0, len);
+ return;
+ }
+ }
- INT8* src1;
- INT8* src2;
- UINT32 len1;
- UINT32 len2;
+ INT8* src1;
+ INT8* src2;
+ UINT32 len1;
+ UINT32 len2;
- // Check if play region extends past end of buffer
+ // Check if play region extends past end of buffer
if (playPos + len > audioBufferSize)
{
- // If so, split play region into two
- src1 = audioBuffer + playPos;
- src2 = audioBuffer;
- len1 = audioBufferSize - playPos;
- len2 = len - len1;
+ // If so, split play region into two
+ src1 = audioBuffer + playPos;
+ src2 = audioBuffer;
+ len1 = audioBufferSize - playPos;
+ len2 = len - len1;
}
else
{
- // Otherwise, just copy whole region
- src1 = audioBuffer + playPos;
- src2 = 0;
- len1 = len;
- len2 = 0;
- }
+ // Otherwise, just copy whole region
+ src1 = audioBuffer + playPos;
+ src2 = 0;
+ len1 = len;
+ len2 = 0;
+ }
- // Check if audio is enabled
+ // Check if audio is enabled
if (enabled)
{
- // If so, copy play region into audio output stream
- memcpy(stream, src1, len1);
+ // If so, copy play region into audio output stream
+ memcpy(stream, src1, len1);
- // Also, if not looping on under-runs then blank region out
- if (!underRunLoop)
- memset(src1, 0, len1);
+ // Also, if not looping on under-runs then blank region out
+ if (!underRunLoop)
+ memset(src1, 0, len1);
if (len2)
{
- // If region was split into two, copy second half into audio output stream as well
- memcpy(stream + len1, src2, len2);
+ // If region was split into two, copy second half into audio output stream as well
+ memcpy(stream + len1, src2, len2);
- // Also, if not looping on under-runs then blank region out
- if (!underRunLoop)
- memset(src2, 0, len2);
- }
+ // Also, if not looping on under-runs then blank region out
+ if (!underRunLoop)
+ memset(src2, 0, len2);
+ }
}
else
- // Otherwise, just copy silence to audio output stream
- memset(stream, 0, len);
+ // Otherwise, just copy silence to audio output stream
+ memset(stream, 0, len);
- // Move play position forward for next time
- playPos += len;
+ // Move play position forward for next time
+ playPos += len;
- bool bufferFull = adjWritePos + 2 * BYTES_PER_FRAME > playPos + audioBufferSize;
+ bool bufferFull = adjWritePos + 2 * bytes_per_frame_host > playPos + audioBufferSize;
- // Check if play position has moved past end of buffer
+ // Check if play position has moved past end of buffer
if (playPos >= audioBufferSize)
{
- // If so, wrap it around to beginning again and reset write wrapped flag
- playPos -= audioBufferSize;
- writeWrapped = false;
- }
+ // If so, wrap it around to beginning again and reset write wrapped flag
+ playPos -= audioBufferSize;
+ writeWrapped = false;
+ }
- // If buffer is not full then call audio callback
- if (callback && !bufferFull)
- callback(callbackData);
+ // If buffer is not full then call audio callback
+ if (callback && !bufferFull)
+ callback(callbackData);
}
-static void MixChannels(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer, void *dest, bool flipStereo)
+static void MixChannels(unsigned numSamples, INT16* leftFrontBuffer, INT16* rightFrontBuffer, INT16* leftRearBuffer, INT16* rightRearBuffer, void* dest, bool flipStereo)
{
- INT16 *p = (INT16*)dest;
+ INT16* p = (INT16*)dest;
-#if (NUM_CHANNELS == 1)
- for (unsigned i = 0; i < numSamples; i++)
- *p++ = leftBuffer[i] + rightBuffer[i]; // TODO: these should probably be clipped!
-#else
- if (flipStereo) // swap left and right channels
- {
- for (unsigned i = 0; i < numSamples; i++)
- {
- *p++ = rightBuffer[i];
- *p++ = leftBuffer[i];
- }
- }
- else // correct stereo
- {
- for (unsigned i = 0; i < numSamples; i++)
- {
- *p++ = leftBuffer[i];
- *p++ = rightBuffer[i];
- }
- }
-#endif // NUM_CHANNELS
+ if (nbHostAudioChannels == 1) {
+ for (unsigned i = 0; i < numSamples; i++) {
+ INT16 monovalue = AddAndClampINT16(
+ (INT32)(leftFrontBuffer[i] * balanceFactorFrontLeft) + (INT32)(rightFrontBuffer[i] * balanceFactorFrontRight),
+ (INT32)(leftRearBuffer[i] * balanceFactorRearLeft) + (INT32)(rightRearBuffer[i] * balanceFactorRearRight));
+ *p++ = monovalue;
+ }
+ } else {
+ // Flip again left/right if configured in audio
+ switch (AudioType) {
+ case Game::STEREO_RL:
+ case Game::QUAD_1_FRL_2_RRL:
+ case Game::QUAD_1_RRL_2_FRL:
+ flipStereo = !flipStereo;
+ break;
+ }
+
+ // Now order channels according to audio type
+ if (nbHostAudioChannels == 2) {
+ for (unsigned i = 0; i < numSamples; i++) {
+ INT16 leftvalue = AddAndClampINT16((INT32)(leftFrontBuffer[i] * balanceFactorFrontLeft), (INT32)(leftRearBuffer[i] * balanceFactorRearLeft));
+ INT16 rightvalue = AddAndClampINT16((INT32)(rightFrontBuffer[i]*balanceFactorFrontRight), (INT32)(rightRearBuffer[i]*balanceFactorRearRight));
+ if (flipStereo) // swap left and right channels
+ {
+ *p++ = rightvalue;
+ *p++ = leftvalue;
+ } else {
+ *p++ = leftvalue;
+ *p++ = rightvalue;
+ }
+ }
+ } else if (nbHostAudioChannels == 4) {
+ for (unsigned i = 0; i < numSamples; i++) {
+ INT16 frontLeftValue = (INT16)(leftFrontBuffer[i]*balanceFactorFrontLeft);
+ INT16 frontRightValue = (INT16)(rightFrontBuffer[i]*balanceFactorFrontRight);
+ INT16 rearLeftValue = (INT16)(leftRearBuffer[i]*balanceFactorRearLeft);
+ INT16 rearRightValue = (INT16)(rightRearBuffer[i]*balanceFactorRearRight);
+
+ // Check game audio type
+ switch (AudioType) {
+ case Game::MONO: {
+ INT16 monovalue = AddAndClampINT16(AddAndClampINT16(frontLeftValue, frontRightValue), AddAndClampINT16(rearLeftValue, rearRightValue));
+ *p++ = monovalue;
+ *p++ = monovalue;
+ *p++ = monovalue;
+ *p++ = monovalue;
+ } break;
+
+ case Game::STEREO_LR:
+ case Game::STEREO_RL: {
+ INT16 leftvalue = AddAndClampINT16(frontLeftValue, frontRightValue);
+ INT16 rightvalue = AddAndClampINT16(rearLeftValue, rearRightValue);
+ if (flipStereo) // swap left and right channels
+ {
+ *p++ = rightvalue;
+ *p++ = leftvalue;
+ *p++ = rightvalue;
+ *p++ = leftvalue;
+ } else {
+ *p++ = leftvalue;
+ *p++ = rightvalue;
+ *p++ = leftvalue;
+ *p++ = rightvalue;
+ }
+ } break;
+
+ case Game::QUAD_1_FLR_2_RLR:
+ case Game::QUAD_1_FRL_2_RRL: {
+ // Normal channels Front Left/Right then Rear Left/Right
+ if (flipStereo) // swap left and right channels
+ {
+ *p++ = frontRightValue;
+ *p++ = frontLeftValue;
+ *p++ = rearRightValue;
+ *p++ = rearLeftValue;
+ } else {
+ *p++ = frontLeftValue;
+ *p++ = frontRightValue;
+ *p++ = rearLeftValue;
+ *p++ = rearRightValue;
+ }
+ } break;
+
+ case Game::QUAD_1_RLR_2_FLR:
+ case Game::QUAD_1_RRL_2_FRL:
+ // Reversed channels Front/Rear Left then Front/Rear Right
+ if (flipStereo) // swap left and right channels
+ {
+ *p++ = rearRightValue;
+ *p++ = rearLeftValue;
+ *p++ = frontRightValue;
+ *p++ = frontLeftValue;
+ } else {
+ *p++ = rearLeftValue;
+ *p++ = rearRightValue;
+ *p++ = frontLeftValue;
+ *p++ = frontRightValue;
+ }
+ break;
+
+ case Game::QUAD_1_LR_2_FR_MIX:
+ // Split mix: one goes to left/right, other front/rear (mono)
+ // =>Remix all!
+ INT16 newfrontLeftValue = AddAndClampINT16(frontLeftValue, rearLeftValue);
+ INT16 newfrontRightValue = AddAndClampINT16(frontLeftValue, rearRightValue);
+ INT16 newrearLeftValue = AddAndClampINT16(frontRightValue, rearLeftValue);
+ INT16 newrearRightValue = AddAndClampINT16(frontRightValue, rearRightValue);
+
+ if (flipStereo) // swap left and right channels
+ {
+ *p++ = newfrontRightValue;
+ *p++ = newfrontLeftValue;
+ *p++ = newrearRightValue;
+ *p++ = newrearLeftValue;
+ } else {
+ *p++ = newfrontLeftValue;
+ *p++ = newfrontRightValue;
+ *p++ = newrearLeftValue;
+ *p++ = newrearRightValue;
+ }
+ break;
+ }
+ }
+ }
+ }
}
/*
static void LogAudioInfo(SDL_AudioSpec *fmt)
{
- InfoLog("Audio device information:");
- InfoLog(" Frequency: %d", fmt->freq);
- InfoLog(" Channels: %d", fmt->channels);
- InfoLog("Sample Format: %d", fmt->format);
- InfoLog("");
+ InfoLog("Audio device information:");
+ InfoLog(" Frequency: %d", fmt->freq);
+ InfoLog(" Channels: %d", fmt->channels);
+ InfoLog("Sample Format: %d", fmt->format);
+ InfoLog("");
}
*/
-bool OpenAudio()
+///
+/// Prepare audio subsystem on host.
+/// The requested channels is deduced, and SDL will make sure it is compatible with this.
+///
+///
+///
+bool OpenAudio(const Util::Config::Node& config)
{
- // Initialize SDL audio sub-system
- if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0)
- return ErrorLog("Unable to initialize SDL audio sub-system: %s\n", SDL_GetError());
+ s_config = &config;
+ // Initialize SDL audio sub-system
+ if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0)
+ return ErrorLog("Unable to initialize SDL audio sub-system: %s\n", SDL_GetError());
- // Set up audio specification
- SDL_AudioSpec fmt;
- memset(&fmt, 0, sizeof(SDL_AudioSpec));
- fmt.freq = SAMPLE_RATE;
- fmt.channels = NUM_CHANNELS;
- fmt.format = AUDIO_S16SYS;
- fmt.samples = playSamples;
- fmt.callback = PlayCallback;
+ // Number of channels requested in config (default is 4)
+ nbHostAudioChannels = (int)s_config->Get("NbSoundChannels").ValueAs();
- // Force SDL to use the format we requested; it will convert if necessary
- if (SDL_OpenAudio(&fmt, nullptr) < 0)
- return ErrorLog("Unable to open 44.1KHz 2-channel audio with SDL: %s\n", SDL_GetError());
+ // If game is only stereo or mono, enforce host to reduce number of channels
+ switch (AudioType) {
+ case Game::MONO:
+ nbHostAudioChannels = std::min(nbHostAudioChannels, 1);
+ break;
+ case Game::STEREO_LR:
+ case Game::STEREO_RL:
+ nbHostAudioChannels = std::min(nbHostAudioChannels, 2);
+ break;
+ }
+ // Mixer Balance
+ float balancelr = (float)s_config->Get("BalanceLeftRight").ValueAs();
+ if (balancelr < -100.0f)
+ balancelr = -100.0f;
+ else if (balancelr > 100.0f)
+ balancelr = 100.0f;
+ balancelr *= 0.01f;
+ BalanceLeftRight = balancelr;
- // Create audio buffer
- constexpr uint32_t bufferSize = SAMPLE_RATE * BYTES_PER_SAMPLE * latency / MAX_LATENCY;
- static_assert(bufferSize % BYTES_PER_SAMPLE == 0, "must be an integer multiple of the sample size");
- audioBufferSize = bufferSize;
-
- int minBufferSize = 3 * BYTES_PER_FRAME;
- audioBufferSize = std::max(minBufferSize, audioBufferSize);
- audioBuffer = new(std::nothrow) INT8[audioBufferSize];
- if (audioBuffer == NULL)
- {
- float audioBufMB = (float)audioBufferSize / (float)0x100000;
- return ErrorLog("Insufficient memory for audio latency buffer (need %1.1f MB).", audioBufMB);
- }
- memset(audioBuffer, 0, sizeof(INT8) * audioBufferSize);
+ float balancefr = (float)s_config->Get("BalanceFrontRear").ValueAs();
+ if (balancefr < -100.0f)
+ balancefr = -100.0f;
+ else if (balancefr > 100.0f)
+ balancefr = 100.0f;
+ balancefr *= 0.01f;
+ BalanceFrontRear = balancefr;
- // Set initial play position to be beginning of buffer and initial write position to be half-way into buffer
- playPos = 0;
- constexpr uint32_t endOfBuffer = bufferSize - BYTES_PER_FRAME;
- constexpr uint32_t midpointAfterFirstFrameUnaligned = BYTES_PER_FRAME + (bufferSize - BYTES_PER_FRAME) / 2;
- constexpr uint32_t extraPaddingNeeded = (BYTES_PER_SAMPLE - midpointAfterFirstFrameUnaligned % BYTES_PER_SAMPLE) % BYTES_PER_SAMPLE;
- constexpr uint32_t midpointAfterFirstFrame = midpointAfterFirstFrameUnaligned + extraPaddingNeeded;
- static_assert(endOfBuffer % BYTES_PER_SAMPLE == 0, "make sure we are aligned to a sample boundary otherwise underrun/overrun adjustment will end up shifting playback by one channel causing stereo to flip");
- static_assert(midpointAfterFirstFrame % BYTES_PER_SAMPLE == 0,"error");
- writePos = std::min(endOfBuffer, midpointAfterFirstFrame);
- writeWrapped = false;
+ balanceFactorFrontLeft = (BalanceLeftRight < 0.f ? 1.f + BalanceLeftRight : 1.f) * (BalanceFrontRear < 0 ? 1.f + BalanceFrontRear : 1.f);
+ balanceFactorFrontRight = (BalanceLeftRight > 0.f ? 1.f - BalanceLeftRight : 1.f) * (BalanceFrontRear < 0 ? 1.f + BalanceFrontRear : 1.f);
+ balanceFactorRearLeft = (BalanceLeftRight < 0.f ? 1.f + BalanceLeftRight : 1.f) * (BalanceFrontRear > 0 ? 1.f - BalanceFrontRear : 1.f);
+ balanceFactorRearRight = (BalanceLeftRight > 0.f ? 1.f - BalanceLeftRight : 1.f) * (BalanceFrontRear > 0 ? 1.f - BalanceFrontRear : 1.f);
- // Reset counters
- underRuns = 0;
- overRuns = 0;
+ // Set up audio specification
+ SDL_AudioSpec desired;
+ memset(&desired, 0, sizeof(SDL_AudioSpec));
+ desired.freq = SAMPLE_RATE_M3;
+ // Number of host channels to use (choice limited to 1,2,4)
+ desired.channels = nbHostAudioChannels;
+ desired.format = AUDIO_S16SYS;
+ desired.samples = playSamples;
+ desired.callback = PlayCallback;
- // Start audio playing
- SDL_PauseAudio(0);
- return OKAY;
+ // Now force SDL to use the format we requested (nullptr); it will convert if necessary
+ if (SDL_OpenAudio(&desired, nullptr) < 0) {
+ if (desired.channels==2) {
+ return ErrorLog("Unable to open 44.1KHz 2-channel audio with SDL: %s\n", SDL_GetError());
+ } else if (desired.channels==4) {
+ return ErrorLog("Unable to open 44.1KHz 4-channel audio with SDL: %s\n", SDL_GetError());
+ } else {
+ return ErrorLog("Unable to open 44.1KHz channel audio with SDL: %s\n", SDL_GetError());
+ }
+ }
+
+ float soundFreq_Hz = (float)s_config->Get("SoundFreq").ValueAs();
+ if (soundFreq_Hz>MAX_SND_FREQ)
+ soundFreq_Hz = MAX_SND_FREQ;
+ if (soundFreq_Hz(minBufferSize, audioBufferSize);
+ audioBuffer = new(std::nothrow) INT8[audioBufferSize];
+ if (audioBuffer == NULL) {
+ float audioBufMB = (float)audioBufferSize / (float)0x100000;
+ return ErrorLog("Insufficient memory for audio latency buffer (need %1.1f MB).", audioBufMB);
+ }
+ memset(audioBuffer, 0, sizeof(INT8) * audioBufferSize);
+
+ // Set initial play position to be beginning of buffer and initial write position to be half-way into buffer
+ playPos = 0;
+ uint32_t endOfBuffer = bufferSize - bytes_per_frame_host;
+ uint32_t midpointAfterFirstFrameUnaligned = bytes_per_frame_host + (bufferSize - bytes_per_frame_host) / 2;
+ uint32_t extraPaddingNeeded = (bytes_per_frame_host - midpointAfterFirstFrameUnaligned % bytes_per_frame_host) % bytes_per_frame_host;
+ uint32_t midpointAfterFirstFrame = midpointAfterFirstFrameUnaligned + extraPaddingNeeded;
+ if (!((endOfBuffer % (nbHostAudioChannels*sizeof(INT16))) == 0)) {
+ return ErrorLog("must be an integer multiple of the sample size\n");
+ }
+ if (!((midpointAfterFirstFrame % nbHostAudioChannels*sizeof(INT16)) == 0)) {
+ return ErrorLog("must be an integer multiple of the sample size\n");
+ }
+
+ writePos = std::min(endOfBuffer, midpointAfterFirstFrame);
+ writeWrapped = false;
+
+ // Reset counters
+ underRuns = 0;
+ overRuns = 0;
+
+ // Start audio playing
+ SDL_PauseAudio(0);
+ return OKAY;
}
-bool OutputAudio(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer, bool flipStereo)
+bool OutputAudio(unsigned numSamples, INT16* leftFrontBuffer, INT16* rightFrontBuffer, INT16* leftRearBuffer, INT16* rightRearBuffer, bool flipStereo)
{
- //printf("OutputAudio(%u) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u]\n",
- // numSamples, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize);
+ //printf("OutputAudio(%u) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u]\n",
+ // numSamples, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize);
- UINT32 bytesRemaining;
- UINT32 bytesToCopy;
- INT16 *src;
+ UINT32 bytesRemaining;
+ UINT32 bytesToCopy;
+ INT16* src;
- // Number of samples should never be more than max number of samples per frame
- if (numSamples > SAMPLES_PER_FRAME)
- numSamples = SAMPLES_PER_FRAME;
+ // Number of samples should never be more than max number of samples per frame
+ if (numSamples > (unsigned)samples_per_frame_host)
+ numSamples = samples_per_frame_host;
- // Mix together left and right channels into single chunk of data
- INT16 mixBuffer[NUM_CHANNELS * SAMPLES_PER_FRAME];
- MixChannels(numSamples, leftBuffer, rightBuffer, mixBuffer, flipStereo);
+ // Mix together left and right channels into single chunk of data
+ INT16 mixBuffer[NUM_CHANNELS_M3 * (SAMPLE_RATE_M3 / MIN_SND_FREQ)];
+ MixChannels(numSamples, leftFrontBuffer, rightFrontBuffer, leftRearBuffer, rightRearBuffer, mixBuffer, flipStereo);
- // Lock SDL audio callback so that it doesn't interfere with following code
- SDL_LockAudio();
+ // Lock SDL audio callback so that it doesn't interfere with following code
+ SDL_LockAudio();
- // Calculate number of bytes for current sound chunk
- UINT32 numBytes = numSamples * BYTES_PER_SAMPLE;
+ // Calculate number of bytes for current sound chunk
+ UINT32 numBytes = numSamples * bytes_per_sample_host;
- // Get end of current play region (writing must occur past this point)
- UINT32 playEndPos = playPos + BYTES_PER_FRAME;
+ // Get end of current play region (writing must occur past this point)
+ UINT32 playEndPos = playPos + bytes_per_frame_host;
- // Undo any wrap-around of the write position that may have occured to create following ordering: playPos < playEndPos < writePos
- if (playEndPos > writePos && writeWrapped)
- writePos += audioBufferSize;
+ // Undo any wrap-around of the write position that may have occured to create following ordering: playPos < playEndPos < writePos
+ if (playEndPos > writePos && writeWrapped)
+ writePos += audioBufferSize;
- // Check if play region has caught up with write position and now overlaps it (ie buffer under-run)
+ // Check if play region has caught up with write position and now overlaps it (ie buffer under-run)
if (playEndPos > writePos)
{
- underRuns++;
+ underRuns++;
- //printf("Audio buffer under-run #%u in OutputAudio(%u) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u, numBytes = %u]\n",
- // underRuns, numSamples, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize, numBytes);
+ //printf("Audio buffer under-run #%u in OutputAudio(%u) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u, numBytes = %u]\n",
+ // underRuns, numSamples, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize, numBytes);
- // See what action to take on under-run
+ // See what action to take on under-run
if (underRunLoop)
{
- // If loop, then move play position back to beginning of data in buffer
- playPos = writePos + numBytes + BYTES_PER_FRAME;
+ // If loop, then move play position back to beginning of data in buffer
+ playPos = writePos + numBytes + bytes_per_frame_host;
- // Check if play position has moved past end of buffer
- if (playPos >= audioBufferSize)
- // If so, wrap it around to beginning again (but keep write wrapped flag as before)
- playPos -= audioBufferSize;
+ // Check if play position has moved past end of buffer
+ if (playPos >= audioBufferSize)
+ // If so, wrap it around to beginning again (but keep write wrapped flag as before)
+ playPos -= audioBufferSize;
else
{
- // Otherwise, set write wrapped flag as will now appear as if write has wrapped but play position has not
- writeWrapped = true;
- writePos += audioBufferSize;
- }
+ // Otherwise, set write wrapped flag as will now appear as if write has wrapped but play position has not
+ writeWrapped = true;
+ writePos += audioBufferSize;
+ }
}
else
{
- // Otherwise, bump write position forward in chunks until it is past end of play region
+ // Otherwise, bump write position forward in chunks until it is past end of play region
do
{
- writePos += numBytes;
+ writePos += numBytes;
}
while (playEndPos > writePos);
- }
- }
+ }
+ }
- // Check if write position has caught up with play region and now overlaps it (ie buffer over-run)
- bool overRun = writePos + numBytes > playPos + audioBufferSize;
+ // Check if write position has caught up with play region and now overlaps it (ie buffer over-run)
+ bool overRun = writePos + numBytes > playPos + audioBufferSize;
- bool bufferFull = writePos + 2 * BYTES_PER_FRAME > playPos + audioBufferSize;
+ bool bufferFull = writePos + 2 * bytes_per_frame_host > playPos + audioBufferSize;
- // Move write position back to within buffer
- if (writePos >= audioBufferSize)
- writePos -= audioBufferSize;
+ // Move write position back to within buffer
+ if (writePos >= audioBufferSize)
+ writePos -= audioBufferSize;
- // Handle buffer over-run
+ // Handle buffer over-run
if (overRun)
{
- overRuns++;
+ overRuns++;
- //printf("Audio buffer over-run #%u in OutputAudio(%u) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u, numBytes = %u]\n",
- // overRuns, numSamples, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize, numBytes);
+ //printf("Audio buffer over-run #%u in OutputAudio(%u) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u, numBytes = %u]\n",
+ // overRuns, numSamples, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize, numBytes);
- bufferFull = true;
+ bufferFull = true;
- // Discard current chunk of data
- goto Finish;
- }
+ // Discard current chunk of data
+ goto Finish;
+ }
- src = mixBuffer;
- INT8 *dst1;
- INT8 *dst2;
- UINT32 len1;
- UINT32 len2;
+ src = mixBuffer;
+ INT8* dst1;
+ INT8* dst2;
+ UINT32 len1;
+ UINT32 len2;
- // Check if write region extends past end of buffer
+ // Check if write region extends past end of buffer
if (writePos + numBytes > audioBufferSize)
{
- // If so, split write region into two
- dst1 = audioBuffer + writePos;
- dst2 = audioBuffer;
- len1 = audioBufferSize - writePos;
- len2 = numBytes - len1;
+ // If so, split write region into two
+ dst1 = audioBuffer + writePos;
+ dst2 = audioBuffer;
+ len1 = audioBufferSize - writePos;
+ len2 = numBytes - len1;
}
else
{
- // Otherwise, just copy whole region
- dst1 = audioBuffer + writePos;
- dst2 = 0;
- len1 = numBytes;
- len2 = 0;
- }
+ // Otherwise, just copy whole region
+ dst1 = audioBuffer + writePos;
+ dst2 = 0;
+ len1 = numBytes;
+ len2 = 0;
+ }
- // Copy chunk to write position in buffer
- bytesRemaining = numBytes;
- bytesToCopy = (bytesRemaining > len1 ? len1 : bytesRemaining);
- memcpy(dst1, src, bytesToCopy);
+ // Copy chunk to write position in buffer
+ bytesRemaining = numBytes;
+ bytesToCopy = (bytesRemaining > len1 ? len1 : bytesRemaining);
+ memcpy(dst1, src, bytesToCopy);
- // Adjust for number of bytes copied
- bytesRemaining -= bytesToCopy;
- src = (INT16*)((UINT8*)src + bytesToCopy);
+ // Adjust for number of bytes copied
+ bytesRemaining -= bytesToCopy;
+ src = (INT16*)((UINT8*)src + bytesToCopy);
if (bytesRemaining)
{
- // If write region was split into two, copy second half of chunk into buffer as well
- bytesToCopy = (bytesRemaining > len2 ? len2 : bytesRemaining);
- memcpy(dst2, src, bytesToCopy);
- }
+ // If write region was split into two, copy second half of chunk into buffer as well
+ bytesToCopy = (bytesRemaining > len2 ? len2 : bytesRemaining);
+ memcpy(dst2, src, bytesToCopy);
+ }
- // Move write position forward for next time
- writePos += numBytes;
+ // Move write position forward for next time
+ writePos += numBytes;
- // Check if write position has moved past end of buffer
+ // Check if write position has moved past end of buffer
if (writePos >= audioBufferSize)
{
- // If so, wrap it around to beginning again and set write wrapped flag
- writePos -= audioBufferSize;
- writeWrapped = true;
- }
+ // If so, wrap it around to beginning again and set write wrapped flag
+ writePos -= audioBufferSize;
+ writeWrapped = true;
+ }
Finish:
- // Unlock SDL audio callback
- SDL_UnlockAudio();
+ // Unlock SDL audio callback
+ SDL_UnlockAudio();
- // Return whether buffer is half full
- return bufferFull;
+ // Return whether buffer is half full
+ return bufferFull;
}
void CloseAudio()
{
- // Close SDL audio output
- SDL_CloseAudio();
+ // Close SDL audio output
+ SDL_CloseAudio();
- // Delete audio buffer
+ // Delete audio buffer
if (audioBuffer != NULL)
{
- delete[] audioBuffer;
- audioBuffer = NULL;
- }
+ delete[] audioBuffer;
+ audioBuffer = NULL;
+ }
}
\ No newline at end of file
diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp
index 53b2bc4..fac66f2 100644
--- a/Src/OSD/SDL/Main.cpp
+++ b/Src/OSD/SDL/Main.cpp
@@ -58,16 +58,17 @@
#include
#include
+#ifdef SUPERMODEL_WIN32
+#include "DirectInputSystem.h"
+#include "WinOutputs.h"
+#endif
+
#include "Supermodel.h"
#include "Util/Format.h"
#include "Util/NewConfig.h"
#include "Util/ConfigBuilders.h"
#include "GameLoader.h"
#include "SDLInputSystem.h"
-#ifdef SUPERMODEL_WIN32
-#include "DirectInputSystem.h"
-#include "WinOutputs.h"
-#endif
#include "SDLIncludes.h"
#include "Debugger/SupermodelDebugger.h"
#include "Graphics/Legacy3D/Legacy3D.h"
@@ -91,7 +92,7 @@ static Util::Config::Node s_runtime_config("Global");
Display Management
******************************************************************************/
-static SDL_Window *s_window = nullptr;
+SDL_Window *s_window = nullptr;
/*
* Position and size of rectangular region within OpenGL display to render to.
@@ -837,7 +838,7 @@ static void SuperSleep(UINT32 time)
******************************************************************************/
#ifdef SUPERMODEL_DEBUGGER
-int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *Inputs, COutputs *Outputs, std::shared_ptr Debugger)
+int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *Inputs, COutputs *Outputs, IScripting* scripting, std::shared_ptr Debugger)
{
std::shared_ptr oldLogger;
#else
@@ -880,7 +881,8 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In
PrintGLInfo(false, true, false);
// Initialize audio system
- if (OKAY != OpenAudio())
+ SetAudioType(game.audio);
+ if (OKAY != OpenAudio(s_runtime_config))
return 1;
// Hide mouse if fullscreen, enable crosshairs for gun games
@@ -908,7 +910,7 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In
if (OKAY != Render3D->Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes))
goto QuitError;
Model3->AttachRenderers(Render2D,Render3D);
-
+
// Reset emulator
Model3->Reset();
@@ -1388,6 +1390,10 @@ static Util::Config::Node DefaultConfig()
// CSoundBoard
config.Set("EmulateSound", true);
config.Set("Balance", "0");
+ config.Set("BalanceLeftRight", "0");
+ config.Set("BalanceFrontRear", "0");
+ config.Set("NbSoundChannels", "4");
+ config.Set("SoundFreq", "57.6"); // 60.0f? 57.524160f?
// CDSB
config.Set("EmulateDSB", true);
config.Set("SoundVolume", "100");
@@ -1454,8 +1460,8 @@ static Util::Config::Node DefaultConfig()
static void Title(void)
{
puts("Supermodel: A Sega Model 3 Arcade Emulator (Version " SUPERMODEL_VERSION ")");
- puts("Copyright 2011-2021 by Bart Trzynadlowski, Nik Henson, Ian Curtis,");
- puts(" Harry Tuttle, and Spindizzi\n");
+ puts("Copyright 2011-2022 by Bart Trzynadlowski, Nik Henson, Ian Curtis, Harry Tuttle,");
+ puts(" Spindizzi, gm_mathew and njz3\n");
}
static void Help(void)
@@ -1510,6 +1516,7 @@ static void Help(void)
puts(" when Digital Sound Board is present [Default: 100]");
puts(" -music-volume= Digital Sound Board volume in % [Default: 100]");
puts(" -balance= Relative front/rear balance in % [Default: 0]");
+ puts(" -channels= Number of sound channels to use on host [Default: 4]");
puts(" -flip-stereo Swap left and right audio channels");
puts(" -no-sound Disable sound board emulation (sound effects)");
puts(" -no-dsb Disable Digital Sound Board (MPEG music)");
@@ -1585,6 +1592,8 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv)
{ "-sound-volume", "SoundVolume" },
{ "-music-volume", "MusicVolume" },
{ "-balance", "Balance" },
+ { "-channels", "NbSoundChannels" },
+ { "-soundfreq", "SoundFreq" },
{ "-input-system", "InputSystem" },
{ "-outputs", "Outputs" },
{ "-log-output", "LogOutput" },
diff --git a/Src/OSD/Windows/WinOutputs.cpp b/Src/OSD/Windows/WinOutputs.cpp
index 7f48f47..63a619a 100755
--- a/Src/OSD/Windows/WinOutputs.cpp
+++ b/Src/OSD/Windows/WinOutputs.cpp
@@ -57,6 +57,7 @@ CWinOutputs::~CWinOutputs()
// Broadcast a shutdown message
if (m_hwnd)
PostMessage(HWND_BROADCAST, m_onStop, (WPARAM)m_hwnd, 0);
+ DeleteWindowClass();
}
bool CWinOutputs::Initialize()
@@ -143,7 +144,19 @@ bool CWinOutputs::CreateWindowClass()
return false;
}
+bool CWinOutputs::DeleteWindowClass()
+{
+ if (!s_createdClass)
+ return true;
+ // Register class
+ if (UnregisterClass(OUTPUT_WINDOW_CLASS, GetModuleHandle(NULL))) {
+ s_createdClass = false;
+ return true;
+ }
+
+ return false;
+}
bool CWinOutputs::AllocateMessageId(UINT ®Id, LPCSTR str)
{
regId = RegisterWindowMessage(str);
diff --git a/Src/OSD/Windows/WinOutputs.h b/Src/OSD/Windows/WinOutputs.h
index 28aabde..6fe61bb 100755
--- a/Src/OSD/Windows/WinOutputs.h
+++ b/Src/OSD/Windows/WinOutputs.h
@@ -25,17 +25,17 @@
* Implementation of COutputs that sends MAMEHooker compatible messages via Windows messages.
*/
-#ifndef INCLUDED_WINOUTPUTS_H
+#ifndef INCLUDED_WINOUTPUTS_H
#define INCLUDED_WINOUTPUTS_H
#define WIN32_LEAN_AND_MEAN
-#include
-
-#include "OSD/Outputs.h"
-
-#include
-
-using namespace std;
+#include
+
+#include "OSD/Outputs.h"
+
+#include
+
+using namespace std;
// Struct that represents a client (eg MAMEHooker) currently registered with the emulator
struct RegisteredClient
@@ -50,57 +50,58 @@ struct CopyDataIdString
char string[1]; // String containing data
};
-class CWinOutputs : public COutputs
-{
-public:
- /*
- * CWinOutputs():
- * ~CWinOutputs():
- *
- * Constructor and destructor.
- */
- CWinOutputs();
-
- virtual ~CWinOutputs();
-
- /*
- * Initialize():
- *
- * Initializes this class.
- */
- bool Initialize();
-
- /*
- * Attached():
- *
- * Lets the class know that it has been attached to the emulator.
- */
- void Attached();
-
-protected:
- /*
- * SendOutput():
- *
- * Sends the appropriate output message to all registered clients.
- */
- void SendOutput(EOutputs output, UINT8 prevValue, UINT8 value);
-
+class CWinOutputs : public COutputs
+{
+public:
+ /*
+ * CWinOutputs():
+ * ~CWinOutputs():
+ *
+ * Constructor and destructor.
+ */
+ CWinOutputs();
+
+ virtual ~CWinOutputs();
+
+ /*
+ * Initialize():
+ *
+ * Initializes this class.
+ */
+ bool Initialize();
+
+ /*
+ * Attached():
+ *
+ * Lets the class know that it has been attached to the emulator.
+ */
+ void Attached();
+
+protected:
+ /*
+ * SendOutput():
+ *
+ * Sends the appropriate output message to all registered clients.
+ */
+ void SendOutput(EOutputs output, UINT8 prevValue, UINT8 value);
+
private:
static bool s_createdClass;
- /*
- * CreateWindowClass():
- *
- * Registers the window class and sets up OutputWindowProcCallback to process all messages sent to the emulator window.
- */
+ /*
+ * CreateWindowClass():
+ *
+ * Registers the window class and sets up OutputWindowProcCallback to process all messages sent to the emulator window.
+ */
static bool CreateWindowClass();
+ static bool DeleteWindowClass();
- /*
- * OutputWindowProcCallback(hwnd, msg, wParam, lParam):
- *
- * Receives all messages sent to the emulator window and passes them on to the CWinOutputs object (whose pointer is passed
- * via GWLP_USERDATA).
- */
+ /*
+ * OutputWindowProcCallback(hwnd, msg, wParam, lParam):
+ *
+ * Receives all messages sent to the emulator window and passes them on to the CWinOutputs object (whose pointer is passed
+ * via GWLP_USERDATA).
+ */
static LRESULT CALLBACK OutputWindowProcCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HWND m_hwnd;
@@ -127,49 +128,49 @@ private:
* Processes the messages sent to the emulator window.
*/
LRESULT OutputWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
-
- /*
- * RegisterClient(hwnd, id):
- *
- * Registers a client (eg MAMEHooker) with the emulator.
- */
- LRESULT RegisterClient(HWND hwnd, LPARAM id);
-
- /*
- * SendAllToClient(client):
- *
- * Sends the current state of all the outputs to the given registered client.
- * Called whenever a client is registered with the emulator.
- */
- void SendAllToClient(RegisteredClient &client);
-
- /*
- * UnregisterClient(hwnd, id):
- *
- * Unregisters a client from the emulator.
- */
- LRESULT UnregisterClient(HWND hwnd, LPARAM id);
-
- /*
- * SendIdString(hwnd, id):
- *
- * Sends the name of the requested output back to a client, or the name of the current running game if an id of zero is requested.
- */
- LRESULT SendIdString(HWND hwnd, LPARAM id);
-
- /*
- * MapIdToName(id):
- *
- * Maps the given id to an output's name.
- */
- const char *MapIdToName(LPARAM id);
-
- /*
- * MapNameToId(name):
- *
- * Maps the given name to an output's id.
- */
- LPARAM MapNameToId(const char *name);
-};
-
-#endif // INCLUDED_WINOUTPUTS_H
+
+ /*
+ * RegisterClient(hwnd, id):
+ *
+ * Registers a client (eg MAMEHooker) with the emulator.
+ */
+ LRESULT RegisterClient(HWND hwnd, LPARAM id);
+
+ /*
+ * SendAllToClient(client):
+ *
+ * Sends the current state of all the outputs to the given registered client.
+ * Called whenever a client is registered with the emulator.
+ */
+ void SendAllToClient(RegisteredClient &client);
+
+ /*
+ * UnregisterClient(hwnd, id):
+ *
+ * Unregisters a client from the emulator.
+ */
+ LRESULT UnregisterClient(HWND hwnd, LPARAM id);
+
+ /*
+ * SendIdString(hwnd, id):
+ *
+ * Sends the name of the requested output back to a client, or the name of the current running game if an id of zero is requested.
+ */
+ LRESULT SendIdString(HWND hwnd, LPARAM id);
+
+ /*
+ * MapIdToName(id):
+ *
+ * Maps the given id to an output's name.
+ */
+ const char *MapIdToName(LPARAM id);
+
+ /*
+ * MapNameToId(name):
+ *
+ * Maps the given name to an output's id.
+ */
+ LPARAM MapNameToId(const char *name);
+};
+
+#endif // INCLUDED_WINOUTPUTS_H
diff --git a/Src/Sound/SCSP.cpp b/Src/Sound/SCSP.cpp
index 8b3e68a..a463861 100644
--- a/Src/Sound/SCSP.cpp
+++ b/Src/Sound/SCSP.cpp
@@ -89,12 +89,15 @@ bool legacySound; // For LegacySound (SCSP DSP) config option.
// These globals control the operation of the SCSP, they are no longer extern and are set through SCSP_SetBuffers(). --Bart
float SoundClock; // Originally titled SysFPS; seems to be for the sound CPU.
const float Freq = 76;
-signed short *bufferl;
-signed short *bufferr;
+signed short* bufferfl;
+signed short* bufferfr;
+signed short* bufferrl;
+signed short* bufferrr;
int length;
int cnts;
-signed int *buffertmpl,*buffertmpr; // these are allocated inside this file
+signed int* buffertmpfl, * buffertmpfr; // these are allocated inside this file
+signed int* buffertmprl, * buffertmprr; // these are allocated inside this file
unsigned int srate=44100;
@@ -312,11 +315,11 @@ struct _SCSP
_SLOT Slots[32];
signed short RINGBUF[64];
unsigned char BUFPTR;
-#if FM_DELAY
- signed short DELAYBUF[FM_DELAY];
- BYTE DELAYPTR;
+#if FM_DELAY
+ signed short DELAYBUF[FM_DELAY];
+ BYTE DELAYPTR;
#endif
- unsigned char *SCSPRAM;
+ unsigned char *SCSPRAM;
UINT32 SCSPRAM_LENGTH;
char Master;
#ifdef USEDSP
@@ -625,8 +628,8 @@ bool SCSP_Init(const Util::Config::Node &config, int n)
SCSPDSP_Init(&SCSP->DSP);
#endif
SCSP->Master=1;
- SCSP->SCSPRAM_LENGTH = 512 * 1024;
- SCSP->DSP.SCSPRAM = (UINT16 *)SCSP->SCSPRAM;
+ SCSP->SCSPRAM_LENGTH = 512 * 1024;
+ SCSP->DSP.SCSPRAM = (UINT16 *)SCSP->SCSPRAM;
SCSP->DSP.SCSPRAM_LENGTH = (512 * 1024) / 2;
MidiR=MidiW=0;
MidiOutR=MidiOutW=0;
@@ -759,19 +762,32 @@ bool SCSP_Init(const Util::Config::Node &config, int n)
#endif
LFO_Init();
- buffertmpl = NULL;
- buffertmpr = NULL;
- buffertmpl=(signed int*) malloc(44100*sizeof(signed int));
- if (NULL == buffertmpl)
+ buffertmpfl = NULL;
+ buffertmpfr = NULL;
+ buffertmprl = NULL;
+ buffertmprr = NULL;
+ buffertmpfl=(signed int*) malloc(44100*sizeof(signed int));
+ if (NULL == buffertmpfl)
return ErrorLog("Insufficient memory for internal SCSP buffers.");
- buffertmpr=(signed int*) malloc(44100*sizeof(signed int));
- if (NULL == buffertmpl)
+ buffertmpfr=(signed int*) malloc(44100*sizeof(signed int));
+ if (NULL == buffertmpfr)
{
- free(buffertmpl);
+ free(buffertmpfr);
return ErrorLog("Insufficient memory for internal SCSP buffers.");
}
- memset(buffertmpl,0,44100*sizeof(signed int));
- memset(buffertmpr,0,44100*sizeof(signed int));
+
+ buffertmprl=(signed int*)malloc(44100*sizeof(signed int));
+ if (NULL == buffertmprl)
+ return ErrorLog("Insufficient memory for internal SCSP buffers.");
+ buffertmprr=(signed int*)malloc(44100*sizeof(signed int));
+ if (NULL == buffertmprr)
+ return ErrorLog("Insufficient memory for internal SCSP buffers.");
+
+
+ memset(buffertmpfl, 0, 44100*sizeof(signed int));
+ memset(buffertmpfr, 0, 44100*sizeof(signed int));
+ memset(buffertmprl, 0, 44100*sizeof(signed int));
+ memset(buffertmprr, 0, 44100*sizeof(signed int));
SCSPs->data[0x20 / 2] = 0;
TimCnt[0] = 0xffff;
TimCnt[1] = 0xffff;
@@ -781,8 +797,10 @@ bool SCSP_Init(const Util::Config::Node &config, int n)
MIDILock = CThread::CreateMutex();
if (NULL == MIDILock)
{
- free(buffertmpl);
- free(buffertmpr);
+ free(buffertmpfl);
+ free(buffertmpfr);
+ free(buffertmprl);
+ free(buffertmprr);
return ErrorLog("Unable to create MIDI mutex!");
}
@@ -1111,7 +1129,7 @@ void SCSP_w16(unsigned int addr,unsigned short val)
SCSP_UpdateReg(addr & 0x3f);
}
}
- else if (addr < 0x700)
+ else if (addr < 0x700)
SCSP->RINGBUF[(addr - 0x600) / 2] = val;
else
{
@@ -1544,21 +1562,25 @@ void SCSP_DoMasterSamples(int nsamples)
balance /= 100.0f;
float masterBalance = 1.0f + balance;
float slaveBalance = 1.0f - balance;
- signed short *bufl, *bufr;
+ signed short* buffl, * buffr;
+ signed short* bufrl, * bufrr;
INT32 sl, s, i;
- bufl = bufferl;
- bufr = bufferr;
+ buffl = bufferfl;
+ buffr = bufferfr;
+ bufrl = bufferrl;
+ bufrr = bufferrr;
/*
* Generate samples
*/
for (s = 0; s < nsamples; ++s)
{
- signed int smpl = 0, smpr = 0;
+ signed int smpfl = 0, smpfr = 0;
+ signed int smprl = 0, smprr = 0;
- for (sl = 0; sl < 32; ++sl)
+ for (sl = 0; sl < 32; ++sl)
{
#if FM_DELAY
RBUFDST = SCSPs[0].DELAYBUF + SCSPs[0].DELAYPTR;
@@ -1578,12 +1600,12 @@ void SCSP_DoMasterSamples(int nsamples)
SCSPDSP_SetSample(&SCSPs[0].DSP, (sample*LPANTABLE[Enc]) >> (SHIFT - 2), ISEL(slot), IMXL(slot));
Enc = ((TL(slot)) << 0x0) | ((DIPAN(slot)) << 0x8) | ((DISDL(slot)) << 0xd);
#ifdef RB_VOLUME
- smpl += (sample * volume[TL(slot) + pan_left[DIPAN(slot)]]) >> 17;
- smpr += (sample * volume[TL(slot) + pan_right[DIPAN(slot)]]) >> 17;
+ smpfl += (sample * volume[TL(slot) + pan_left[DIPAN(slot)]]) >> 17;
+ smpfr += (sample * volume[TL(slot) + pan_right[DIPAN(slot)]]) >> 17;
#else
{
- smpl += (sample*LPANTABLE[Enc]) >> SHIFT;
- smpr += (sample*RPANTABLE[Enc]) >> SHIFT;
+ smpfl += (sample*LPANTABLE[Enc]) >> SHIFT;
+ smpfr += (sample*RPANTABLE[Enc]) >> SHIFT;
}
#endif
}
@@ -1615,11 +1637,11 @@ void SCSP_DoMasterSamples(int nsamples)
Enc = ((TL(slot)) << 0x0) | ((DIPAN(slot)) << 0x8) | ((DISDL(slot)) << 0xd);
{
#ifdef RB_VOLUME
- smpl += (sample * volume[TL(slot) + pan_left[DIPAN(slot)]]) >> 17;
- smpr += (sample * volume[TL(slot) + pan_right[DIPAN(slot)]]) >> 17;
+ smprl += (sample * volume[TL(slot) + pan_left[DIPAN(slot)]]) >> 17;
+ smprr += (sample * volume[TL(slot) + pan_right[DIPAN(slot)]]) >> 17;
#else
- smpl += (sample*LPANTABLE[Enc]) >> SHIFT;
- smpr += (sample*RPANTABLE[Enc]) >> SHIFT;
+ smprl += (sample*LPANTABLE[Enc]) >> SHIFT;
+ smprr += (sample*RPANTABLE[Enc]) >> SHIFT;
}
#endif
}
@@ -1650,8 +1672,8 @@ void SCSP_DoMasterSamples(int nsamples)
{
// For legacy option, 14 is the most reasonable value I can set at the moment for the EFSDL slot. - Paul
UINT16 Enc = ((EFPAN(slot)) << 0x8) | ((EFSDL(slot)) << 0xe);
- smpl += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT)));
- smpr += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT)));
+ smpfl += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT)));
+ smpfr += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT)));
}
if (HasSlaveSCSP)
{
@@ -1659,8 +1681,8 @@ void SCSP_DoMasterSamples(int nsamples)
if (EFSDL(slot))
{
UINT16 Enc = ((EFPAN(slot)) << 0x8) | ((EFSDL(slot)) << 0xe);
- smpl += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT)));
- smpr += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT)));
+ smprl += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT)));
+ smprr += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT)));
}
}
}
@@ -1668,8 +1690,8 @@ void SCSP_DoMasterSamples(int nsamples)
if (EFSDL(slot))
{
UINT16 Enc = ((EFPAN(slot)) << 0x8) | ((EFSDL(slot)) << 0xd);
- smpl += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT)));
- smpr += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT)));
+ smpfl += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT)));
+ smpfr += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT)));
}
if (HasSlaveSCSP)
{
@@ -1677,32 +1699,47 @@ void SCSP_DoMasterSamples(int nsamples)
if (EFSDL(slot))
{
UINT16 Enc = ((EFPAN(slot)) << 0x8) | ((EFSDL(slot)) << 0xd);
- smpl += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT)));
- smpr += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT)));
+ smprl += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT)));
+ smprr += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT)));
}
}
}
}
- if (DAC18B(SCSP))
+ if (DAC18B((&SCSP[0])))
{
- smpl = ICLIP18(smpl);
- smpr = ICLIP18(smpr);
+ smpfl = ICLIP18(smpfl);
+ smpfr = ICLIP18(smpfr);
}
else
{
- smpl = ICLIP16(smpl >> 2);
- smpr = ICLIP16(smpr >> 2);
+ smpfl = ICLIP16(smpfl >> 2);
+ smpfr = ICLIP16(smpfr >> 2);
}
- *bufl++ = ICLIP16(smpl);
- *bufr++ = ICLIP16(smpr);
+ *buffl++ = ICLIP16(smpfl);
+ *buffr++ = ICLIP16(smpfr);
+ if (HasSlaveSCSP)
+ {
+ if (DAC18B((&SCSPs[1])))
+ {
+ smprl = ICLIP18(smprl);
+ smprr = ICLIP18(smprr);
+ }
+ else
+ {
+ smprl = ICLIP16(smprl >> 2);
+ smprr = ICLIP16(smprr >> 2);
+ }
+ }
+ *bufrl++ = ICLIP16(smprl);
+ *bufrr++ = ICLIP16(smprr);
SCSP_TimersAddTicks(1);
CheckPendingIRQ();
lastdiff = Run68kCB(slice - lastdiff);
- }
}
+}
void SCSP_Update()
{
@@ -2107,11 +2144,14 @@ void SCSP_LoadState(CBlockFile *StateFile)
}
}
-void SCSP_SetBuffers(INT16 *leftBufferPtr, INT16 *rightBufferPtr, int bufferLength)
+void SCSP_SetBuffers(INT16 *leftBufferPtr, INT16 *rightBufferPtr, INT16* leftRearBufferPtr, INT16* rightRearBufferPtr, int bufferLength)
{
SoundClock = 76;
- bufferl = leftBufferPtr;
- bufferr = rightBufferPtr;
+ bufferfl = leftBufferPtr;
+ bufferfr = rightBufferPtr;
+ bufferrl = leftRearBufferPtr;
+ bufferrr = rightRearBufferPtr;
+
length = bufferLength;
cnts = 0; // what is this for? seems unimportant but need to find out
}
@@ -2121,10 +2161,14 @@ void SCSP_Deinit(void)
#ifdef USEDSP
free(SCSP->MIXBuf);
#endif
- free(buffertmpl);
- free(buffertmpr);
+ free(buffertmpfl);
+ free(buffertmpfr);
+ free(buffertmprl);
+ free(buffertmprr);
delete MIDILock;
- buffertmpl = NULL;
- buffertmpr = NULL;
+ buffertmpfl = NULL;
+ buffertmpfr = NULL;
+ buffertmprl = NULL;
+ buffertmprr = NULL;
MIDILock = NULL;
}
diff --git a/Src/Sound/SCSP.h b/Src/Sound/SCSP.h
index 3b46b64..137b35b 100644
--- a/Src/Sound/SCSP.h
+++ b/Src/Sound/SCSP.h
@@ -83,7 +83,7 @@ UINT32 SCSP_Slave_r32(UINT32 addr);
// Supermodel interface functions
void SCSP_SaveState(CBlockFile *StateFile);
void SCSP_LoadState(CBlockFile *StateFile);
-void SCSP_SetBuffers(INT16 *leftBufferPtr, INT16 *rightBufferPtr, int bufferLength);
+void SCSP_SetBuffers(INT16 *leftBufferPtr, INT16 *rightBufferPtr, INT16* leftRearBufferPtr, INT16* rightRearBufferPtr, int bufferLength);
void SCSP_Deinit(void);
diff --git a/VS2008/Musashi68K/Musashi68K.vcxproj b/VS2008/Musashi68K/Musashi68K.vcxproj
index 873642d..5ffa5a1 100644
--- a/VS2008/Musashi68K/Musashi68K.vcxproj
+++ b/VS2008/Musashi68K/Musashi68K.vcxproj
@@ -21,28 +21,29 @@
{1248CF7C-B122-461C-9624-196AEFAE5046}
Musashi68K
+ 10.0
Application
- v141
+ v142
MultiByte
true
Application
- v141
+ v142
MultiByte
Application
- v141
+ v142
MultiByte
true
Application
- v141
+ v142
MultiByte
diff --git a/VS2008/SDL/SDL.vcxproj b/VS2008/SDL/SDL.vcxproj
index 1c63541..595bb0d 100644
--- a/VS2008/SDL/SDL.vcxproj
+++ b/VS2008/SDL/SDL.vcxproj
@@ -22,24 +22,24 @@
SDL2
{81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}
SDL
- 8.1
+ 10.0
DynamicLibrary
- v141
+ v142
DynamicLibrary
- v141
+ v142
DynamicLibrary
- v141
+ v142
DynamicLibrary
- v141
+ v142
diff --git a/VS2008/SDLmain/SDLmain.vcxproj b/VS2008/SDLmain/SDLmain.vcxproj
index 09d149c..13e9151 100644
--- a/VS2008/SDLmain/SDLmain.vcxproj
+++ b/VS2008/SDLmain/SDLmain.vcxproj
@@ -22,24 +22,24 @@
SDL2main
{DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}
SDLmain
- 8.1
+ 10.0
StaticLibrary
- v141
+ v142
StaticLibrary
- v141
+ v142
StaticLibrary
- v141
+ v142
StaticLibrary
- v141
+ v142
diff --git a/VS2008/SDLnet/SDL_net.vcxproj b/VS2008/SDLnet/SDL_net.vcxproj
index 6b1300d..56896e3 100644
--- a/VS2008/SDLnet/SDL_net.vcxproj
+++ b/VS2008/SDLnet/SDL_net.vcxproj
@@ -22,24 +22,24 @@
SDL2_net
{8AB3504F-5E58-4910-AFE8-7A1E595AC3F4}
SDL_net
- 8.1
+ 10.0
DynamicLibrary
- v141
+ v142
DynamicLibrary
- v141
+ v142
DynamicLibrary
- v141
+ v142
DynamicLibrary
- v141
+ v142
diff --git a/VS2008/Supermodel.vcxproj b/VS2008/Supermodel.vcxproj
index 86578d9..9a7eaf4 100644
--- a/VS2008/Supermodel.vcxproj
+++ b/VS2008/Supermodel.vcxproj
@@ -22,29 +22,29 @@
{B114BBD9-8AEA-4DAE-B367-A66A804CB3DD}
Supermodel
Win32Proj
- 8.1
+ 10.0
Application
- v141
+ v142
Unicode
true
Application
- v141
+ v142
Unicode
Application
- v141
+ v142
Unicode
true
Application
- v141
+ v142
Unicode
diff --git a/VS2008/ZLib/ZLib.vcxproj b/VS2008/ZLib/ZLib.vcxproj
index 03516ae..bef52d7 100644
--- a/VS2008/ZLib/ZLib.vcxproj
+++ b/VS2008/ZLib/ZLib.vcxproj
@@ -25,22 +25,22 @@
StaticLibrary
- v141
+ v142
false
StaticLibrary
- v141
+ v142
false
StaticLibrary
- v141
+ v142
false
StaticLibrary
- v141
+ v142
false