njz3: Proper quadrophonic audio support. Need 4+ speakers to take advantage otherwise down mixed to stereo.

This commit is contained in:
Ian Curtis 2022-06-09 21:10:39 +00:00
parent 5e61e2f388
commit 83e7b5f45d
24 changed files with 982 additions and 581 deletions

View file

@ -174,6 +174,7 @@
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<real3d_status_bit_set_percent_of_frame>24</real3d_status_bit_set_percent_of_frame> <real3d_status_bit_set_percent_of_frame>24</real3d_status_bit_set_percent_of_frame>
<netboard>true</netboard> <netboard>true</netboard>
<audio>QuadFrontRear</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -266,6 +267,7 @@
<mpeg_board>DSB2</mpeg_board> <mpeg_board>DSB2</mpeg_board>
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<netboard>true</netboard> <netboard>true</netboard>
<audio>QuadFrontRear</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -551,6 +553,7 @@
<platform>Sega Model 3</platform> <platform>Sega Model 3</platform>
<stepping>2.1</stepping> <stepping>2.1</stepping>
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<audio>Stereo</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -632,6 +635,7 @@
<platform>Sega Model 3</platform> <platform>Sega Model 3</platform>
<stepping>2.1</stepping> <stepping>2.1</stepping>
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<audio>Stereo</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -678,6 +682,7 @@
<platform>Sega Model 3</platform> <platform>Sega Model 3</platform>
<stepping>2.1</stepping> <stepping>2.1</stepping>
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<audio>Stereo</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -711,6 +716,7 @@
<platform>Sega Model 3</platform> <platform>Sega Model 3</platform>
<stepping>2.1</stepping> <stepping>2.1</stepping>
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<audio>Stereo</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -1030,6 +1036,7 @@
<stepping>1.5</stepping> <stepping>1.5</stepping>
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<netboard>true</netboard> <netboard>true</netboard>
<audio>Stereo</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -1360,6 +1367,7 @@
<mpeg_board>DSB1</mpeg_board> <mpeg_board>DSB1</mpeg_board>
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<netboard>true</netboard> <netboard>true</netboard>
<audio>QuadFrontRear</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -1444,6 +1452,7 @@
<mpeg_board>DSB1</mpeg_board> <mpeg_board>DSB1</mpeg_board>
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<netboard>true</netboard> <netboard>true</netboard>
<audio>QuadFrontRear</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -1478,6 +1487,7 @@
<mpeg_board>DSB1</mpeg_board> <mpeg_board>DSB1</mpeg_board>
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<netboard>true</netboard> <netboard>true</netboard>
<audio>QuadFrontRear</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -1550,6 +1560,7 @@
<mpeg_board>DSB1</mpeg_board> <mpeg_board>DSB1</mpeg_board>
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<netboard>true</netboard> <netboard>true</netboard>
<audio>QuadFrontRear</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -1597,6 +1608,7 @@
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<pci_bridge>MPC106</pci_bridge> <pci_bridge>MPC106</pci_bridge>
<netboard>true</netboard> <netboard>true</netboard>
<audio>QuadFrontRear</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -1891,6 +1903,7 @@
<mpeg_board>DSB2</mpeg_board> <mpeg_board>DSB2</mpeg_board>
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<netboard>true</netboard> <netboard>true</netboard>
<audio>QuadMix</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -1989,6 +2002,7 @@
<stepping>2.0</stepping> <stepping>2.0</stepping>
<mpeg_board>DSB2</mpeg_board> <mpeg_board>DSB2</mpeg_board>
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<audio>QuadMix</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />
@ -2098,6 +2112,7 @@
<mpeg_board>DSB2</mpeg_board> <mpeg_board>DSB2</mpeg_board>
<drive_board>Wheel</drive_board> <drive_board>Wheel</drive_board>
<real3d_status_bit_set_percent_of_frame>48</real3d_status_bit_set_percent_of_frame> <real3d_status_bit_set_percent_of_frame>48</real3d_status_bit_set_percent_of_frame>
<audio>QuadMix</audio>
<inputs> <inputs>
<input type="common" /> <input type="common" />
<input type="vehicle" /> <input type="vehicle" />

View file

@ -14,6 +14,18 @@ struct Game
unsigned year = 0; unsigned year = 0;
std::string stepping; std::string stepping;
std::string mpeg_board; 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) 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) 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) float real3d_status_bit_set_percent_of_frame = 0; // overrides default status bit timing (0 for default)

View file

@ -198,6 +198,20 @@ static void PopulateGameInfo(Game *game, const Util::Config::Node &game_node)
game->year = game_node["identity/year"].ValueAsDefault<unsigned>(0); game->year = game_node["identity/year"].ValueAsDefault<unsigned>(0);
game->stepping = game_node["hardware/stepping"].ValueAsDefault<std::string>(""); game->stepping = game_node["hardware/stepping"].ValueAsDefault<std::string>("");
game->mpeg_board = game_node["hardware/mpeg_board"].ValueAsDefault<std::string>(""); game->mpeg_board = game_node["hardware/mpeg_board"].ValueAsDefault<std::string>("");
std::map<std::string, Game::AudioTypes> 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>(std::string());
game->audio = audio_types[audio_type];
game->pci_bridge = game_node["hardware/pci_bridge"].ValueAsDefault<std::string>(""); game->pci_bridge = game_node["hardware/pci_bridge"].ValueAsDefault<std::string>("");
game->real3d_pci_id = game_node["hardware/real3d_pci_id"].ValueAsDefault<uint32_t>(0); game->real3d_pci_id = game_node["hardware/real3d_pci_id"].ValueAsDefault<uint32_t>(0);
game->real3d_status_bit_set_percent_of_frame = game_node["hardware/real3d_status_bit_set_percent_of_frame"].ValueAsDefault<float>(0); game->real3d_status_bit_set_percent_of_frame = game_node["hardware/real3d_status_bit_set_percent_of_frame"].ValueAsDefault<float>(0);
@ -250,80 +264,80 @@ static void PopulateGameInfo(Game *game, const Util::Config::Node &game_node)
} }
bool GameLoader::LoadGamesFromXML(const Util::Config::Node &xml) bool GameLoader::LoadGamesFromXML(const Util::Config::Node &xml)
{ {
for (auto it = xml.begin(); it != xml.end(); ++it) for (auto it = xml.begin(); it != xml.end(); ++it)
{ {
// Root games node // Root games node
auto &root_node = *it; auto &root_node = *it;
if (root_node.Key() != "games") if (root_node.Key() != "games")
continue; continue;
for (auto &game_node: root_node) for (auto &game_node: root_node)
{ {
// Game node // Game node
if (game_node.Key() != "game") if (game_node.Key() != "game")
continue; continue;
if (game_node["name"].Empty()) if (game_node["name"].Empty())
{ {
//TODO: associate line numbers in config //TODO: associate line numbers in config
//ErrorLog("%s: Ignoring <game> tag with missing 'name' attribute.", m_xml_filename.c_str()); //ErrorLog("%s: Ignoring <game> tag with missing 'name' attribute.", m_xml_filename.c_str());
continue; continue;
} }
std::string game_name = game_node["name"].ValueAs<std::string>(); std::string game_name = game_node["name"].ValueAs<std::string>();
if (m_regions_by_game.find(game_name) != m_regions_by_game.end()) 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()); ErrorLog("%s: Ignoring redefinition of game '%s'.", m_xml_filename.c_str(), game_name.c_str());
continue; continue;
} }
RegionsByName_t &regions_by_name = m_regions_by_game[game_name]; RegionsByName_t &regions_by_name = m_regions_by_game[game_name];
PatchesByRegion_t &patches_by_region = m_patches_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); PopulateGameInfo(&m_game_info_by_game[game_name], game_node);
for (auto &roms_node: game_node) for (auto &roms_node: game_node)
{ {
if (roms_node.Key() != "roms") if (roms_node.Key() != "roms")
continue; continue;
/* /*
* Regions define contiguous memory areas that individual ROM files are * Regions define contiguous memory areas that individual ROM files are
* loaded into. It is possible to have multiple region tags identifying * loaded into. It is possible to have multiple region tags identifying
* the same region. They will be aggregated. This is useful for parent * 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, * 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 * with the child set loading in different files to overwrite the parent
* set. * set.
*/ */
for (auto &region_node: roms_node) for (auto &region_node: roms_node)
{ {
if (region_node.Key() != "region") if (region_node.Key() != "region")
continue; continue;
// Look up region structure or create new one if needed // Look up region structure or create new one if needed
std::string region_name = region_node["name"].Value<std::string>(); std::string region_name = region_node["name"].Value<std::string>();
auto it = regions_by_name.find(region_name); auto it = regions_by_name.find(region_name);
Region::ptr_t region = (it != regions_by_name.end()) ? it->second : Region::Create(*this, region_node); Region::ptr_t region = (it != regions_by_name.end()) ? it->second : Region::Create(*this, region_node);
if (!region) if (!region)
continue; continue;
/* /*
* Files are defined by the offset they are loaded at. Normally, there * 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 * should be one file per offset but parent/child ROM sets will violate
* this, and so it is allowed. * this, and so it is allowed.
*/ */
std::vector<File::ptr_t> &files = region->files; std::vector<File::ptr_t> &files = region->files;
for (auto &file_node: region_node) for (auto &file_node: region_node)
{ {
if (file_node.Key() != "file") if (file_node.Key() != "file")
continue; continue;
File::ptr_t file = File::Create(*this, file_node); File::ptr_t file = File::Create(*this, file_node);
if (!file) if (!file)
continue; continue;
files.push_back(file); files.push_back(file);
} }
// Check to ensure that some files were defined in the region // Check to ensure that some files were defined in the region
if (files.empty()) 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()); 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 else
regions_by_name[region->region_name] = region; regions_by_name[region->region_name] = region;
} }
// ROM patches, if any // ROM patches, if any
@ -348,17 +362,17 @@ bool GameLoader::LoadGamesFromXML(const Util::Config::Node &xml)
else else
patches_by_region[region].push_back(ROM::BigEndianPatch(offset, value, bits)); patches_by_region[region].push_back(ROM::BigEndianPatch(offset, value, bits));
} }
} }
} }
// Check to ensure that some ROM regions were defined for the game // Check to ensure that some ROM regions were defined for the game
if (regions_by_name.empty()) if (regions_by_name.empty())
ErrorLog("%s: No ROM regions defined for '%s'.", m_xml_filename.c_str(), game_name.c_str()); ErrorLog("%s: No ROM regions defined for '%s'.", m_xml_filename.c_str(), game_name.c_str());
} }
} }
// Check to ensure some games were defined // Check to ensure some games were defined
if (m_regions_by_game.empty()) if (m_regions_by_game.empty())
{ {
ErrorLog("%s: No games defined.", m_xml_filename.c_str()); ErrorLog("%s: No games defined.", m_xml_filename.c_str());
return true; return true;
} }

View file

@ -725,7 +725,7 @@ void CLegacy3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P,
// Specular shininess // Specular shininess
GLfloat specularCoefficient = (GLfloat) ((P->header[0]>>26) & 0x3F) * (1.0f/63.0f); GLfloat specularCoefficient = (GLfloat) ((P->header[0]>>26) & 0x3F) * (1.0f/63.0f);
int shinyBits = (P->header[6] >> 5) & 3; 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 if (!(P->header[0]&0x80)) //|| (shininess == 0)) // bit 0x80 seems to enable specular lighting
{ {
specularCoefficient = 0.; // disable specularCoefficient = 0.; // disable

View file

@ -163,7 +163,7 @@ public:
virtual void AttachInputs(CInputs *InputsPtr) = 0; virtual void AttachInputs(CInputs *InputsPtr) = 0;
/* /*
* AttachInputs(InputsPtr): * AttachOutputs(InputsPtr):
* *
* Attaches OSD-managed outputs (cabinet lamps, etc.) * Attaches OSD-managed outputs (cabinet lamps, etc.)
* *

View file

@ -472,6 +472,11 @@ UINT8 CModel3::ReadInputs(unsigned reg)
} }
return data; return data;
case 0x10: // Drive board
return OutputRegister[0];
case 0x14: // Lamps
return OutputRegister[1];
case 0x0C: // game-specific inputs case 0x0C: // game-specific inputs
data = 0xFF; data = 0xFF;
@ -651,9 +656,11 @@ void CModel3::WriteInputs(unsigned reg, UINT8 data)
break; break;
case 0x10: // Drive board case 0x10: // Drive board
DriveBoard->Write(data); if (DriveBoard->IsAttached())
DriveBoard->Write(data);
if (NULL != Outputs) // TODO - check gameInputs if (NULL != Outputs) // TODO - check gameInputs
Outputs->SetValue(OutputRawDrive, data); Outputs->SetValue(OutputRawDrive, data);
OutputRegister[0] = data;
break; break;
case 0x14: // Lamp outputs (Daytona/Scud Race/Sega Rally/Le Mans 24) 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(OutputLampLeader, !!(data&0x80));
Outputs->SetValue(OutputRawLamps, data); Outputs->SetValue(OutputRawLamps, data);
} }
OutputRegister[1] = data;
break; break;
case 0x24: // Serial FIFO 1 case 0x24: // Serial FIFO 1
@ -2017,7 +2025,8 @@ void CModel3::RunFrame(void)
} }
timings.frameTicks = CThread::GetTicks() - start; timings.frameTicks = CThread::GetTicks() - start;
// Frame counter
timings.frameId++;
return; return;
ThreadError: ThreadError:
@ -2810,7 +2819,8 @@ void CModel3::Reset(void)
NetBoard->Reset(); NetBoard->Reset();
#endif #endif
timings.frameTicks = 0; timings.frameTicks = 0;
timings.frameId = 0;
DebugLog("Model 3 reset\n"); DebugLog("Model 3 reset\n");
} }
@ -3176,7 +3186,7 @@ INetBoard *CModel3::GetNetBoard(void)
} }
#endif #endif
CModel3::CModel3(const Util::Config::Node &config) CModel3::CModel3(Util::Config::Node &config)
: m_config(config), : m_config(config),
m_multiThreaded(config["MultiThreaded"].ValueAs<bool>()), m_multiThreaded(config["MultiThreaded"].ValueAs<bool>()),
m_gpuMultiThreaded(config["GPUMultiThreaded"].ValueAs<bool>()), m_gpuMultiThreaded(config["GPUMultiThreaded"].ValueAs<bool>()),

View file

@ -63,6 +63,7 @@ struct FrameTimings
UINT32 netTicks; UINT32 netTicks;
#endif #endif
UINT32 frameTicks; UINT32 frameTicks;
UINT64 frameId;
}; };
/* /*
@ -95,6 +96,8 @@ public:
void AttachInputs(CInputs *InputsPtr); void AttachInputs(CInputs *InputsPtr);
void AttachOutputs(COutputs *OutputsPtr); void AttachOutputs(COutputs *OutputsPtr);
bool Init(void); bool Init(void);
// For Scripting tweaks
Util::Config::Node& GetConfig() { return this->m_config; }
// IPCIDevice interface // IPCIDevice interface
UINT32 ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width); 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 * config Run-time configuration. The reference should be held because
* this changes at run-time. * this changes at run-time.
*/ */
CModel3(const Util::Config::Node &config); CModel3(Util::Config::Node &config);
~CModel3(void); ~CModel3(void);
/* /*
@ -228,7 +231,7 @@ private:
int RunDriveBoardThread(void); // Runs drive board thread (sync'd in step with render thread) int RunDriveBoardThread(void); // Runs drive board thread (sync'd in step with render thread)
// Runtime configuration // Runtime configuration
const Util::Config::Node &m_config; Util::Config::Node &m_config;
bool m_multiThreaded; bool m_multiThreaded;
bool m_gpuMultiThreaded; bool m_gpuMultiThreaded;
@ -262,6 +265,7 @@ private:
UINT8 *driveROM; // 32 KB drive board ROM (Z80 program) (optional) UINT8 *driveROM; // 32 KB drive board ROM (Z80 program) (optional)
UINT8 *netRAM; // 64 KB RAM UINT8 *netRAM; // 64 KB RAM
UINT8 *netBuffer; // 128 KB buffer UINT8 *netBuffer; // 128 KB buffer
UINT8 OutputRegister[2]; // Input/output register for driveboard and lamps
// Banked CROM // Banked CROM
UINT8 *cromBank; // currently mapped in CROM bank UINT8 *cromBank; // currently mapped in CROM bank
@ -321,7 +325,6 @@ private:
INetBoard *NetBoard; // Net board INetBoard *NetBoard; // Net board
bool m_runNetBoard; bool m_runNetBoard;
#endif #endif
}; };

View file

@ -350,16 +350,25 @@ bool CSoundBoard::RunFrame(void)
} }
else else
{ {
memset(audioL, 0, 44100/60*sizeof(INT16)); memset(audioFL, 0, 44100/60*sizeof(INT16));
memset(audioR, 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 // Run DSB and mix with existing audio
if (NULL != DSB) if (NULL != DSB) {
DSB->RunFrame(audioL, audioR); // 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 // Output the audio buffers
bool bufferFull = OutputAudio(44100/60, audioL, audioR, m_config["FlipStereo"].ValueAs<bool>()); bool bufferFull = OutputAudio(44100/60, audioFL, audioFR, audioRL, audioRR, m_config["FlipStereo"].ValueAs<bool>());
#ifdef SUPERMODEL_LOG_AUDIO #ifdef SUPERMODEL_LOG_AUDIO
// Output to binary file // Output to binary file
@ -446,9 +455,13 @@ void CSoundBoard::AttachDSB(CDSB *DSBPtr)
// Offsets of memory regions within sound board's pool // Offsets of memory regions within sound board's pool
#define OFFSET_RAM1 0 // 1 MB SCSP1 RAM #define OFFSET_RAM1 0 // 1 MB SCSP1 RAM
#define OFFSET_RAM2 0x100000 // 1 MB SCSP2 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 LENGTH_CHANNEL_BUFFER 0x5BE
#define OFFSET_AUDIO_RIGHT 0x2005BE // 1470 bytes right audio channel #define OFFSET_AUDIO_FRONTLEFT 0x200000 // 1470 bytes (16 bits, 44.1 KHz, 1/60th second) left audio channel
#define MEMORY_POOL_SIZE (0x100000 + 0x100000 + 0x5BE + 0x5BE) #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) 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 // Set up memory pointers
ram1 = &memoryPool[OFFSET_RAM1]; ram1 = &memoryPool[OFFSET_RAM1];
ram2 = &memoryPool[OFFSET_RAM2]; ram2 = &memoryPool[OFFSET_RAM2];
audioL = (INT16 *) &memoryPool[OFFSET_AUDIO_LEFT]; audioFL = (INT16*)&memoryPool[OFFSET_AUDIO_FRONTLEFT];
audioR = (INT16 *) &memoryPool[OFFSET_AUDIO_RIGHT]; audioFR = (INT16*)&memoryPool[OFFSET_AUDIO_FRONTRIGHT];
audioRL = (INT16*)&memoryPool[OFFSET_AUDIO_REARLEFT];
audioRR = (INT16*)&memoryPool[OFFSET_AUDIO_REARRIGHT];
// Initialize 68K core // Initialize 68K core
M68KSetContext(&M68K); M68KSetContext(&M68K);
M68KInit(); M68KInit();
@ -480,7 +495,7 @@ bool CSoundBoard::Init(const UINT8 *soundROMPtr, const UINT8 *sampleROMPtr)
M68KGetContext(&M68K); M68KGetContext(&M68K);
// Initialize SCSPs // Initialize SCSPs
SCSP_SetBuffers(audioL, audioR, 44100/60); SCSP_SetBuffers(audioFL, audioFR, audioRL, audioRR, 44100/60);
SCSP_SetCB(SCSP68KRunCallback, SCSP68KIRQCallback); SCSP_SetCB(SCSP68KRunCallback, SCSP68KIRQCallback);
if (OKAY != SCSP_Init(m_config, 2)) if (OKAY != SCSP_Init(m_config, 2))
return FAIL; return FAIL;
@ -514,8 +529,10 @@ CSoundBoard::CSoundBoard(const Util::Config::Node &config)
memoryPool = NULL; memoryPool = NULL;
ram1 = NULL; ram1 = NULL;
ram2 = NULL; ram2 = NULL;
audioL = NULL; audioFL = NULL;
audioR = NULL; audioFR = NULL;
audioRL = NULL;
audioRR = NULL;
soundROM = NULL; soundROM = NULL;
sampleROM = NULL; sampleROM = NULL;
@ -553,8 +570,10 @@ CSoundBoard::~CSoundBoard(void)
} }
ram1 = NULL; ram1 = NULL;
ram2 = NULL; ram2 = NULL;
audioL = NULL; audioFL = NULL;
audioR = NULL; audioFR = NULL;
audioRL = NULL;
audioRR = NULL;
soundROM = NULL; soundROM = NULL;
sampleROM = NULL; sampleROM = NULL;

View file

@ -203,7 +203,8 @@ private:
UINT8 ctrlReg; // control register: ROM banking UINT8 ctrlReg; // control register: ROM banking
// Audio // 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)
}; };

View file

@ -25,36 +25,39 @@
* Header file for OS-dependent audio playback interface. * Header file for OS-dependent audio playback interface.
*/ */
#ifndef INCLUDED_AUDIO_H #ifndef INCLUDED_AUDIO_H
#define INCLUDED_AUDIO_H #define INCLUDED_AUDIO_H
#include "Types.h" #include "Types.h"
#include "Util/NewConfig.h"
#include "Game.h"
typedef void (*AudioCallbackFPtr)(void *data); typedef void (*AudioCallbackFPtr)(void *data);
extern void SetAudioCallback(AudioCallbackFPtr callback, void *data); extern void SetAudioCallback(AudioCallbackFPtr callback, void *data);
extern void SetAudioEnabled(bool enabled); extern void SetAudioEnabled(bool enabled);
extern void SetAudioType(Game::AudioTypes type);
/* /*
* OpenAudio() * OpenAudio()
* *
* Initializes the audio system. * Initializes the audio system.
*/ */
extern bool OpenAudio(); extern bool OpenAudio(const Util::Config::Node& config);
/* /*
* OutputAudio(unsigned numSamples, *INT16 leftBuffer, *INT16 rightBuffer) * OutputAudio(unsigned numSamples, *INT16 leftBuffer, *INT16 rightBuffer)
* *
* Sends a chunk of two-channel audio with the given number of samples to the audio system. * 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() * CloseAudio()
* *
* Shuts down the audio system. * Shuts down the audio system.
*/ */
extern void CloseAudio(); extern void CloseAudio();
#endif // INCLUDED_AUDIO_H #endif // INCLUDED_AUDIO_H

View file

@ -104,3 +104,12 @@ void COutputs::SetValue(EOutputs output, UINT8 value)
if (firstSet || value != prevValue) if (firstSet || value != prevValue)
SendOutput(output, prevValue, value); 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];
}

View file

@ -124,6 +124,14 @@ public:
*/ */
void SetValue(EOutputs output, UINT8 value); 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: protected:
/* /*
* COutputs(): * COutputs():

View file

@ -19,16 +19,39 @@
** with Supermodel. If not, see <http://www.gnu.org/licenses/>. ** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/ **/
/* /*
* Audio.cpp * Audio.cpp
* *
* SDL audio playback. Implements the OSD audio interface. * SDL audio playback. Implements the OSD audio interface.
* *
* Buffer sizes and read/write positions must be sample-aligned. A sample is * 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, * 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 * a sample is 4 bytes. Static assertions are employed to ensure that the
* initial set up of the buffer is correct. * 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" #include "Audio.h"
@ -38,16 +61,38 @@
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
// Model3 audio output is 44.1KHz 2-channel sound and frame rate is 60fps // Model3 audio output is 44.1KHz 4-channel sound and frame rate is 60fps
#define SAMPLE_RATE 44100 #define SAMPLE_RATE_M3 (44100)
#define NUM_CHANNELS 2 #define SUPERMODEL_FPS (60.0f)
#define SUPERMODEL_FPS 60 #define MODEL3_FPS (57.53f)
#define BYTES_PER_SAMPLE (NUM_CHANNELS * sizeof(INT16)) #define MAX_SND_FREQ (75)
#define SAMPLES_PER_FRAME (SAMPLE_RATE / SUPERMODEL_FPS) #define MIN_SND_FREQ (45)
#define BYTES_PER_FRAME (SAMPLES_PER_FRAME * BYTES_PER_SAMPLE) #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 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 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 constexpr unsigned playSamples = 512; // Size (in samples) of callback play buffer
static UINT32 audioBufferSize = 0; // Size (in bytes) of audio 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 writePos = 0; // Current position at which writing into buffer
static UINT32 playPos = 0; // Current position at which playing data in buffer via callback 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 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 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 // Lock audio whilst changing callback pointers
SDL_LockAudio(); SDL_LockAudio();
callback = newCallback; callback = newCallback;
callbackData = newData; callbackData = newData;
SDL_UnlockAudio(); SDL_UnlockAudio();
} }
void SetAudioEnabled(bool newEnabled) void SetAudioEnabled(bool newEnabled)
{ {
enabled = newEnabled; enabled = newEnabled;
} }
static void PlayCallback(void *data, Uint8 *stream, int len) /// <summary>
/// Set game audio mixing type
/// </summary>
/// <param name="type"></param>
void SetAudioType(Game::AudioTypes type)
{ {
//printf("PlayCallback(%d) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u]\n", AudioType = type;
// len, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize); }
// Get current write position and adjust it if write has wrapped but play position has not static INT16 AddAndClampINT16(INT32 x, INT32 y)
UINT32 adjWritePos = writePos; {
if (writeWrapped) INT32 sum = x + y;
adjWritePos += audioBufferSize; 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) if (playPos + len > adjWritePos)
{ {
underRuns++; underRuns++;
//printf("Audio buffer under-run #%u in PlayCallback(%d) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u]\n", //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); // 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 (underRunLoop)
{ {
// If loop, then move play position back to beginning of data in buffer // If loop, then move play position back to beginning of data in buffer
playPos = adjWritePos + BYTES_PER_FRAME; playPos = adjWritePos + bytes_per_frame_host;
// Check if play position has moved past end of buffer // Check if play position has moved past end of buffer
if (playPos >= audioBufferSize) if (playPos >= audioBufferSize)
// If so, wrap it around to beginning again (but keep write wrapped flag as before) // If so, wrap it around to beginning again (but keep write wrapped flag as before)
playPos -= audioBufferSize; playPos -= audioBufferSize;
else else
// Otherwise, set write wrapped flag as will now appear as if write has wrapped but play position has not // Otherwise, set write wrapped flag as will now appear as if write has wrapped but play position has not
writeWrapped = true; writeWrapped = true;
} }
else else
{ {
// Otherwise, just copy silence to audio output stream and exit // Otherwise, just copy silence to audio output stream and exit
memset(stream, 0, len); memset(stream, 0, len);
return; return;
} }
} }
INT8* src1; INT8* src1;
INT8* src2; INT8* src2;
UINT32 len1; UINT32 len1;
UINT32 len2; 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 (playPos + len > audioBufferSize)
{ {
// If so, split play region into two // If so, split play region into two
src1 = audioBuffer + playPos; src1 = audioBuffer + playPos;
src2 = audioBuffer; src2 = audioBuffer;
len1 = audioBufferSize - playPos; len1 = audioBufferSize - playPos;
len2 = len - len1; len2 = len - len1;
} }
else else
{ {
// Otherwise, just copy whole region // Otherwise, just copy whole region
src1 = audioBuffer + playPos; src1 = audioBuffer + playPos;
src2 = 0; src2 = 0;
len1 = len; len1 = len;
len2 = 0; len2 = 0;
} }
// Check if audio is enabled // Check if audio is enabled
if (enabled) if (enabled)
{ {
// If so, copy play region into audio output stream // If so, copy play region into audio output stream
memcpy(stream, src1, len1); memcpy(stream, src1, len1);
// Also, if not looping on under-runs then blank region out // Also, if not looping on under-runs then blank region out
if (!underRunLoop) if (!underRunLoop)
memset(src1, 0, len1); memset(src1, 0, len1);
if (len2) if (len2)
{ {
// If region was split into two, copy second half into audio output stream as well // If region was split into two, copy second half into audio output stream as well
memcpy(stream + len1, src2, len2); memcpy(stream + len1, src2, len2);
// Also, if not looping on under-runs then blank region out // Also, if not looping on under-runs then blank region out
if (!underRunLoop) if (!underRunLoop)
memset(src2, 0, len2); memset(src2, 0, len2);
} }
} }
else else
// Otherwise, just copy silence to audio output stream // Otherwise, just copy silence to audio output stream
memset(stream, 0, len); memset(stream, 0, len);
// Move play position forward for next time // Move play position forward for next time
playPos += len; 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 (playPos >= audioBufferSize)
{ {
// If so, wrap it around to beginning again and reset write wrapped flag // If so, wrap it around to beginning again and reset write wrapped flag
playPos -= audioBufferSize; playPos -= audioBufferSize;
writeWrapped = false; writeWrapped = false;
} }
// If buffer is not full then call audio callback // If buffer is not full then call audio callback
if (callback && !bufferFull) if (callback && !bufferFull)
callback(callbackData); 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) if (nbHostAudioChannels == 1) {
for (unsigned i = 0; i < numSamples; i++) for (unsigned i = 0; i < numSamples; i++) {
*p++ = leftBuffer[i] + rightBuffer[i]; // TODO: these should probably be clipped! INT16 monovalue = AddAndClampINT16(
#else (INT32)(leftFrontBuffer[i] * balanceFactorFrontLeft) + (INT32)(rightFrontBuffer[i] * balanceFactorFrontRight),
if (flipStereo) // swap left and right channels (INT32)(leftRearBuffer[i] * balanceFactorRearLeft) + (INT32)(rightRearBuffer[i] * balanceFactorRearRight));
{ *p++ = monovalue;
for (unsigned i = 0; i < numSamples; i++) }
{ } else {
*p++ = rightBuffer[i]; // Flip again left/right if configured in audio
*p++ = leftBuffer[i]; switch (AudioType) {
} case Game::STEREO_RL:
} case Game::QUAD_1_FRL_2_RRL:
else // correct stereo case Game::QUAD_1_RRL_2_FRL:
{ flipStereo = !flipStereo;
for (unsigned i = 0; i < numSamples; i++) break;
{ }
*p++ = leftBuffer[i];
*p++ = rightBuffer[i]; // Now order channels according to audio type
} if (nbHostAudioChannels == 2) {
} for (unsigned i = 0; i < numSamples; i++) {
#endif // NUM_CHANNELS 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) static void LogAudioInfo(SDL_AudioSpec *fmt)
{ {
InfoLog("Audio device information:"); InfoLog("Audio device information:");
InfoLog(" Frequency: %d", fmt->freq); InfoLog(" Frequency: %d", fmt->freq);
InfoLog(" Channels: %d", fmt->channels); InfoLog(" Channels: %d", fmt->channels);
InfoLog("Sample Format: %d", fmt->format); InfoLog("Sample Format: %d", fmt->format);
InfoLog(""); InfoLog("");
} }
*/ */
bool OpenAudio() /// <summary>
/// Prepare audio subsystem on host.
/// The requested channels is deduced, and SDL will make sure it is compatible with this.
/// </summary>
/// <param name="config"></param>
/// <returns></returns>
bool OpenAudio(const Util::Config::Node& config)
{ {
// Initialize SDL audio sub-system s_config = &config;
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) // Initialize SDL audio sub-system
return ErrorLog("Unable to initialize SDL audio sub-system: %s\n", SDL_GetError()); if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0)
return ErrorLog("Unable to initialize SDL audio sub-system: %s\n", SDL_GetError());
// Set up audio specification // Number of channels requested in config (default is 4)
SDL_AudioSpec fmt; nbHostAudioChannels = (int)s_config->Get("NbSoundChannels").ValueAs<int>();
memset(&fmt, 0, sizeof(SDL_AudioSpec));
fmt.freq = SAMPLE_RATE;
fmt.channels = NUM_CHANNELS;
fmt.format = AUDIO_S16SYS;
fmt.samples = playSamples;
fmt.callback = PlayCallback;
// Force SDL to use the format we requested; it will convert if necessary // If game is only stereo or mono, enforce host to reduce number of channels
if (SDL_OpenAudio(&fmt, nullptr) < 0) switch (AudioType) {
return ErrorLog("Unable to open 44.1KHz 2-channel audio with SDL: %s\n", SDL_GetError()); 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<float>();
if (balancelr < -100.0f)
balancelr = -100.0f;
else if (balancelr > 100.0f)
balancelr = 100.0f;
balancelr *= 0.01f;
BalanceLeftRight = balancelr;
// Create audio buffer float balancefr = (float)s_config->Get("BalanceFrontRear").ValueAs<float>();
constexpr uint32_t bufferSize = SAMPLE_RATE * BYTES_PER_SAMPLE * latency / MAX_LATENCY; if (balancefr < -100.0f)
static_assert(bufferSize % BYTES_PER_SAMPLE == 0, "must be an integer multiple of the sample size"); balancefr = -100.0f;
audioBufferSize = bufferSize; else if (balancefr > 100.0f)
balancefr = 100.0f;
int minBufferSize = 3 * BYTES_PER_FRAME; balancefr *= 0.01f;
audioBufferSize = std::max<int>(minBufferSize, audioBufferSize); BalanceFrontRear = balancefr;
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 balanceFactorFrontLeft = (BalanceLeftRight < 0.f ? 1.f + BalanceLeftRight : 1.f) * (BalanceFrontRear < 0 ? 1.f + BalanceFrontRear : 1.f);
playPos = 0; balanceFactorFrontRight = (BalanceLeftRight > 0.f ? 1.f - BalanceLeftRight : 1.f) * (BalanceFrontRear < 0 ? 1.f + BalanceFrontRear : 1.f);
constexpr uint32_t endOfBuffer = bufferSize - BYTES_PER_FRAME; balanceFactorRearLeft = (BalanceLeftRight < 0.f ? 1.f + BalanceLeftRight : 1.f) * (BalanceFrontRear > 0 ? 1.f - BalanceFrontRear : 1.f);
constexpr uint32_t midpointAfterFirstFrameUnaligned = BYTES_PER_FRAME + (bufferSize - BYTES_PER_FRAME) / 2; balanceFactorRearRight = (BalanceLeftRight > 0.f ? 1.f - BalanceLeftRight : 1.f) * (BalanceFrontRear > 0 ? 1.f - BalanceFrontRear : 1.f);
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<int>(endOfBuffer, midpointAfterFirstFrame);
writeWrapped = false;
// Reset counters // Set up audio specification
underRuns = 0; SDL_AudioSpec desired;
overRuns = 0; 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 // Now force SDL to use the format we requested (nullptr); it will convert if necessary
SDL_PauseAudio(0); if (SDL_OpenAudio(&desired, nullptr) < 0) {
return OKAY; 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<float>();
if (soundFreq_Hz>MAX_SND_FREQ)
soundFreq_Hz = MAX_SND_FREQ;
if (soundFreq_Hz<MIN_SND_FREQ)
soundFreq_Hz = MIN_SND_FREQ;
samples_per_frame_host = (INT32)(SAMPLE_RATE_M3 / soundFreq_Hz);
bytes_per_sample_host = (nbHostAudioChannels * sizeof(INT16));
bytes_per_frame_host = (samples_per_frame_host * bytes_per_sample_host);
// Create audio buffer
uint32_t bufferSize = ((SAMPLE_RATE_M3 * latency) / MAX_LATENCY) * bytes_per_sample_host;
if (!(bufferSize % bytes_per_sample_host == 0)) {
return ErrorLog("must be an integer multiple of the sample size\n");
}
audioBufferSize = bufferSize;
int minBufferSize = 3 * bytes_per_frame_host;
audioBufferSize = std::max<int>(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<int>(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", //printf("OutputAudio(%u) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u]\n",
// numSamples, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize); // numSamples, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize);
UINT32 bytesRemaining; UINT32 bytesRemaining;
UINT32 bytesToCopy; UINT32 bytesToCopy;
INT16 *src; INT16* src;
// Number of samples should never be more than max number of samples per frame // Number of samples should never be more than max number of samples per frame
if (numSamples > SAMPLES_PER_FRAME) if (numSamples > (unsigned)samples_per_frame_host)
numSamples = SAMPLES_PER_FRAME; numSamples = samples_per_frame_host;
// Mix together left and right channels into single chunk of data // Mix together left and right channels into single chunk of data
INT16 mixBuffer[NUM_CHANNELS * SAMPLES_PER_FRAME]; INT16 mixBuffer[NUM_CHANNELS_M3 * (SAMPLE_RATE_M3 / MIN_SND_FREQ)];
MixChannels(numSamples, leftBuffer, rightBuffer, mixBuffer, flipStereo); MixChannels(numSamples, leftFrontBuffer, rightFrontBuffer, leftRearBuffer, rightRearBuffer, mixBuffer, flipStereo);
// Lock SDL audio callback so that it doesn't interfere with following code // Lock SDL audio callback so that it doesn't interfere with following code
SDL_LockAudio(); SDL_LockAudio();
// Calculate number of bytes for current sound chunk // Calculate number of bytes for current sound chunk
UINT32 numBytes = numSamples * BYTES_PER_SAMPLE; UINT32 numBytes = numSamples * bytes_per_sample_host;
// Get end of current play region (writing must occur past this point) // Get end of current play region (writing must occur past this point)
UINT32 playEndPos = playPos + BYTES_PER_FRAME; 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 // Undo any wrap-around of the write position that may have occured to create following ordering: playPos < playEndPos < writePos
if (playEndPos > writePos && writeWrapped) if (playEndPos > writePos && writeWrapped)
writePos += audioBufferSize; 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) 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", //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); // 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 (underRunLoop)
{ {
// If loop, then move play position back to beginning of data in buffer // If loop, then move play position back to beginning of data in buffer
playPos = writePos + numBytes + BYTES_PER_FRAME; playPos = writePos + numBytes + bytes_per_frame_host;
// Check if play position has moved past end of buffer // Check if play position has moved past end of buffer
if (playPos >= audioBufferSize) if (playPos >= audioBufferSize)
// If so, wrap it around to beginning again (but keep write wrapped flag as before) // If so, wrap it around to beginning again (but keep write wrapped flag as before)
playPos -= audioBufferSize; playPos -= audioBufferSize;
else else
{ {
// Otherwise, set write wrapped flag as will now appear as if write has wrapped but play position has not // Otherwise, set write wrapped flag as will now appear as if write has wrapped but play position has not
writeWrapped = true; writeWrapped = true;
writePos += audioBufferSize; writePos += audioBufferSize;
} }
} }
else 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 do
{ {
writePos += numBytes; writePos += numBytes;
} }
while (playEndPos > writePos); while (playEndPos > writePos);
} }
} }
// Check if write position has caught up with play region and now overlaps it (ie buffer over-run) // 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 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 // Move write position back to within buffer
if (writePos >= audioBufferSize) if (writePos >= audioBufferSize)
writePos -= audioBufferSize; writePos -= audioBufferSize;
// Handle buffer over-run // Handle buffer over-run
if (overRun) if (overRun)
{ {
overRuns++; overRuns++;
//printf("Audio buffer over-run #%u in OutputAudio(%u) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u, numBytes = %u]\n", //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); // overRuns, numSamples, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize, numBytes);
bufferFull = true; bufferFull = true;
// Discard current chunk of data // Discard current chunk of data
goto Finish; goto Finish;
} }
src = mixBuffer; src = mixBuffer;
INT8 *dst1; INT8* dst1;
INT8 *dst2; INT8* dst2;
UINT32 len1; UINT32 len1;
UINT32 len2; 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 (writePos + numBytes > audioBufferSize)
{ {
// If so, split write region into two // If so, split write region into two
dst1 = audioBuffer + writePos; dst1 = audioBuffer + writePos;
dst2 = audioBuffer; dst2 = audioBuffer;
len1 = audioBufferSize - writePos; len1 = audioBufferSize - writePos;
len2 = numBytes - len1; len2 = numBytes - len1;
} }
else else
{ {
// Otherwise, just copy whole region // Otherwise, just copy whole region
dst1 = audioBuffer + writePos; dst1 = audioBuffer + writePos;
dst2 = 0; dst2 = 0;
len1 = numBytes; len1 = numBytes;
len2 = 0; len2 = 0;
} }
// Copy chunk to write position in buffer // Copy chunk to write position in buffer
bytesRemaining = numBytes; bytesRemaining = numBytes;
bytesToCopy = (bytesRemaining > len1 ? len1 : bytesRemaining); bytesToCopy = (bytesRemaining > len1 ? len1 : bytesRemaining);
memcpy(dst1, src, bytesToCopy); memcpy(dst1, src, bytesToCopy);
// Adjust for number of bytes copied // Adjust for number of bytes copied
bytesRemaining -= bytesToCopy; bytesRemaining -= bytesToCopy;
src = (INT16*)((UINT8*)src + bytesToCopy); src = (INT16*)((UINT8*)src + bytesToCopy);
if (bytesRemaining) if (bytesRemaining)
{ {
// If write region was split into two, copy second half of chunk into buffer as well // If write region was split into two, copy second half of chunk into buffer as well
bytesToCopy = (bytesRemaining > len2 ? len2 : bytesRemaining); bytesToCopy = (bytesRemaining > len2 ? len2 : bytesRemaining);
memcpy(dst2, src, bytesToCopy); memcpy(dst2, src, bytesToCopy);
} }
// Move write position forward for next time // Move write position forward for next time
writePos += numBytes; 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 (writePos >= audioBufferSize)
{ {
// If so, wrap it around to beginning again and set write wrapped flag // If so, wrap it around to beginning again and set write wrapped flag
writePos -= audioBufferSize; writePos -= audioBufferSize;
writeWrapped = true; writeWrapped = true;
} }
Finish: Finish:
// Unlock SDL audio callback // Unlock SDL audio callback
SDL_UnlockAudio(); SDL_UnlockAudio();
// Return whether buffer is half full // Return whether buffer is half full
return bufferFull; return bufferFull;
} }
void CloseAudio() void CloseAudio()
{ {
// Close SDL audio output // Close SDL audio output
SDL_CloseAudio(); SDL_CloseAudio();
// Delete audio buffer // Delete audio buffer
if (audioBuffer != NULL) if (audioBuffer != NULL)
{ {
delete[] audioBuffer; delete[] audioBuffer;
audioBuffer = NULL; audioBuffer = NULL;
} }
} }

View file

@ -58,16 +58,17 @@
#include <algorithm> #include <algorithm>
#include <GL/glew.h> #include <GL/glew.h>
#ifdef SUPERMODEL_WIN32
#include "DirectInputSystem.h"
#include "WinOutputs.h"
#endif
#include "Supermodel.h" #include "Supermodel.h"
#include "Util/Format.h" #include "Util/Format.h"
#include "Util/NewConfig.h" #include "Util/NewConfig.h"
#include "Util/ConfigBuilders.h" #include "Util/ConfigBuilders.h"
#include "GameLoader.h" #include "GameLoader.h"
#include "SDLInputSystem.h" #include "SDLInputSystem.h"
#ifdef SUPERMODEL_WIN32
#include "DirectInputSystem.h"
#include "WinOutputs.h"
#endif
#include "SDLIncludes.h" #include "SDLIncludes.h"
#include "Debugger/SupermodelDebugger.h" #include "Debugger/SupermodelDebugger.h"
#include "Graphics/Legacy3D/Legacy3D.h" #include "Graphics/Legacy3D/Legacy3D.h"
@ -91,7 +92,7 @@ static Util::Config::Node s_runtime_config("Global");
Display Management 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. * Position and size of rectangular region within OpenGL display to render to.
@ -837,7 +838,7 @@ static void SuperSleep(UINT32 time)
******************************************************************************/ ******************************************************************************/
#ifdef SUPERMODEL_DEBUGGER #ifdef SUPERMODEL_DEBUGGER
int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *Inputs, COutputs *Outputs, std::shared_ptr<Debugger::CDebugger> Debugger) int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *Inputs, COutputs *Outputs, IScripting* scripting, std::shared_ptr<Debugger::CDebugger> Debugger)
{ {
std::shared_ptr<CLogger> oldLogger; std::shared_ptr<CLogger> oldLogger;
#else #else
@ -880,7 +881,8 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In
PrintGLInfo(false, true, false); PrintGLInfo(false, true, false);
// Initialize audio system // Initialize audio system
if (OKAY != OpenAudio()) SetAudioType(game.audio);
if (OKAY != OpenAudio(s_runtime_config))
return 1; return 1;
// Hide mouse if fullscreen, enable crosshairs for gun games // 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)) if (OKAY != Render3D->Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes))
goto QuitError; goto QuitError;
Model3->AttachRenderers(Render2D,Render3D); Model3->AttachRenderers(Render2D,Render3D);
// Reset emulator // Reset emulator
Model3->Reset(); Model3->Reset();
@ -1388,6 +1390,10 @@ static Util::Config::Node DefaultConfig()
// CSoundBoard // CSoundBoard
config.Set("EmulateSound", true); config.Set("EmulateSound", true);
config.Set("Balance", "0"); 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 // CDSB
config.Set("EmulateDSB", true); config.Set("EmulateDSB", true);
config.Set("SoundVolume", "100"); config.Set("SoundVolume", "100");
@ -1454,8 +1460,8 @@ static Util::Config::Node DefaultConfig()
static void Title(void) static void Title(void)
{ {
puts("Supermodel: A Sega Model 3 Arcade Emulator (Version " SUPERMODEL_VERSION ")"); puts("Supermodel: A Sega Model 3 Arcade Emulator (Version " SUPERMODEL_VERSION ")");
puts("Copyright 2011-2021 by Bart Trzynadlowski, Nik Henson, Ian Curtis,"); puts("Copyright 2011-2022 by Bart Trzynadlowski, Nik Henson, Ian Curtis, Harry Tuttle,");
puts(" Harry Tuttle, and Spindizzi\n"); puts(" Spindizzi, gm_mathew and njz3\n");
} }
static void Help(void) static void Help(void)
@ -1510,6 +1516,7 @@ static void Help(void)
puts(" when Digital Sound Board is present [Default: 100]"); puts(" when Digital Sound Board is present [Default: 100]");
puts(" -music-volume=<vol> Digital Sound Board volume in % [Default: 100]"); puts(" -music-volume=<vol> Digital Sound Board volume in % [Default: 100]");
puts(" -balance=<bal> Relative front/rear balance in % [Default: 0]"); puts(" -balance=<bal> Relative front/rear balance in % [Default: 0]");
puts(" -channels=<c> Number of sound channels to use on host [Default: 4]");
puts(" -flip-stereo Swap left and right audio channels"); puts(" -flip-stereo Swap left and right audio channels");
puts(" -no-sound Disable sound board emulation (sound effects)"); puts(" -no-sound Disable sound board emulation (sound effects)");
puts(" -no-dsb Disable Digital Sound Board (MPEG music)"); puts(" -no-dsb Disable Digital Sound Board (MPEG music)");
@ -1585,6 +1592,8 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv)
{ "-sound-volume", "SoundVolume" }, { "-sound-volume", "SoundVolume" },
{ "-music-volume", "MusicVolume" }, { "-music-volume", "MusicVolume" },
{ "-balance", "Balance" }, { "-balance", "Balance" },
{ "-channels", "NbSoundChannels" },
{ "-soundfreq", "SoundFreq" },
{ "-input-system", "InputSystem" }, { "-input-system", "InputSystem" },
{ "-outputs", "Outputs" }, { "-outputs", "Outputs" },
{ "-log-output", "LogOutput" }, { "-log-output", "LogOutput" },

View file

@ -57,6 +57,7 @@ CWinOutputs::~CWinOutputs()
// Broadcast a shutdown message // Broadcast a shutdown message
if (m_hwnd) if (m_hwnd)
PostMessage(HWND_BROADCAST, m_onStop, (WPARAM)m_hwnd, 0); PostMessage(HWND_BROADCAST, m_onStop, (WPARAM)m_hwnd, 0);
DeleteWindowClass();
} }
bool CWinOutputs::Initialize() bool CWinOutputs::Initialize()
@ -143,7 +144,19 @@ bool CWinOutputs::CreateWindowClass()
return false; 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 &regId, LPCSTR str) bool CWinOutputs::AllocateMessageId(UINT &regId, LPCSTR str)
{ {
regId = RegisterWindowMessage(str); regId = RegisterWindowMessage(str);

View file

@ -25,17 +25,17 @@
* Implementation of COutputs that sends MAMEHooker compatible messages via Windows messages. * Implementation of COutputs that sends MAMEHooker compatible messages via Windows messages.
*/ */
#ifndef INCLUDED_WINOUTPUTS_H #ifndef INCLUDED_WINOUTPUTS_H
#define INCLUDED_WINOUTPUTS_H #define INCLUDED_WINOUTPUTS_H
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#include "OSD/Outputs.h" #include "OSD/Outputs.h"
#include <vector> #include <vector>
using namespace std; using namespace std;
// Struct that represents a client (eg MAMEHooker) currently registered with the emulator // Struct that represents a client (eg MAMEHooker) currently registered with the emulator
struct RegisteredClient struct RegisteredClient
@ -50,57 +50,58 @@ struct CopyDataIdString
char string[1]; // String containing data char string[1]; // String containing data
}; };
class CWinOutputs : public COutputs class CWinOutputs : public COutputs
{ {
public: public:
/* /*
* CWinOutputs(): * CWinOutputs():
* ~CWinOutputs(): * ~CWinOutputs():
* *
* Constructor and destructor. * Constructor and destructor.
*/ */
CWinOutputs(); CWinOutputs();
virtual ~CWinOutputs(); virtual ~CWinOutputs();
/* /*
* Initialize(): * Initialize():
* *
* Initializes this class. * Initializes this class.
*/ */
bool Initialize(); bool Initialize();
/* /*
* Attached(): * Attached():
* *
* Lets the class know that it has been attached to the emulator. * Lets the class know that it has been attached to the emulator.
*/ */
void Attached(); void Attached();
protected: protected:
/* /*
* SendOutput(): * SendOutput():
* *
* Sends the appropriate output message to all registered clients. * Sends the appropriate output message to all registered clients.
*/ */
void SendOutput(EOutputs output, UINT8 prevValue, UINT8 value); void SendOutput(EOutputs output, UINT8 prevValue, UINT8 value);
private: private:
static bool s_createdClass; static bool s_createdClass;
/* /*
* CreateWindowClass(): * CreateWindowClass():
* *
* Registers the window class and sets up OutputWindowProcCallback to process all messages sent to the emulator window. * Registers the window class and sets up OutputWindowProcCallback to process all messages sent to the emulator window.
*/ */
static bool CreateWindowClass(); static bool CreateWindowClass();
static bool DeleteWindowClass();
/* /*
* OutputWindowProcCallback(hwnd, msg, wParam, lParam): * 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 * Receives all messages sent to the emulator window and passes them on to the CWinOutputs object (whose pointer is passed
* via GWLP_USERDATA). * via GWLP_USERDATA).
*/ */
static LRESULT CALLBACK OutputWindowProcCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK OutputWindowProcCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HWND m_hwnd; HWND m_hwnd;
@ -127,49 +128,49 @@ private:
* Processes the messages sent to the emulator window. * Processes the messages sent to the emulator window.
*/ */
LRESULT OutputWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT OutputWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
/* /*
* RegisterClient(hwnd, id): * RegisterClient(hwnd, id):
* *
* Registers a client (eg MAMEHooker) with the emulator. * Registers a client (eg MAMEHooker) with the emulator.
*/ */
LRESULT RegisterClient(HWND hwnd, LPARAM id); LRESULT RegisterClient(HWND hwnd, LPARAM id);
/* /*
* SendAllToClient(client): * SendAllToClient(client):
* *
* Sends the current state of all the outputs to the given registered client. * Sends the current state of all the outputs to the given registered client.
* Called whenever a client is registered with the emulator. * Called whenever a client is registered with the emulator.
*/ */
void SendAllToClient(RegisteredClient &client); void SendAllToClient(RegisteredClient &client);
/* /*
* UnregisterClient(hwnd, id): * UnregisterClient(hwnd, id):
* *
* Unregisters a client from the emulator. * Unregisters a client from the emulator.
*/ */
LRESULT UnregisterClient(HWND hwnd, LPARAM id); LRESULT UnregisterClient(HWND hwnd, LPARAM id);
/* /*
* SendIdString(hwnd, 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. * 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); LRESULT SendIdString(HWND hwnd, LPARAM id);
/* /*
* MapIdToName(id): * MapIdToName(id):
* *
* Maps the given id to an output's name. * Maps the given id to an output's name.
*/ */
const char *MapIdToName(LPARAM id); const char *MapIdToName(LPARAM id);
/* /*
* MapNameToId(name): * MapNameToId(name):
* *
* Maps the given name to an output's id. * Maps the given name to an output's id.
*/ */
LPARAM MapNameToId(const char *name); LPARAM MapNameToId(const char *name);
}; };
#endif // INCLUDED_WINOUTPUTS_H #endif // INCLUDED_WINOUTPUTS_H

View file

@ -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 // 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. float SoundClock; // Originally titled SysFPS; seems to be for the sound CPU.
const float Freq = 76; const float Freq = 76;
signed short *bufferl; signed short* bufferfl;
signed short *bufferr; signed short* bufferfr;
signed short* bufferrl;
signed short* bufferrr;
int length; int length;
int cnts; 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; unsigned int srate=44100;
@ -312,11 +315,11 @@ struct _SCSP
_SLOT Slots[32]; _SLOT Slots[32];
signed short RINGBUF[64]; signed short RINGBUF[64];
unsigned char BUFPTR; unsigned char BUFPTR;
#if FM_DELAY #if FM_DELAY
signed short DELAYBUF[FM_DELAY]; signed short DELAYBUF[FM_DELAY];
BYTE DELAYPTR; BYTE DELAYPTR;
#endif #endif
unsigned char *SCSPRAM; unsigned char *SCSPRAM;
UINT32 SCSPRAM_LENGTH; UINT32 SCSPRAM_LENGTH;
char Master; char Master;
#ifdef USEDSP #ifdef USEDSP
@ -625,8 +628,8 @@ bool SCSP_Init(const Util::Config::Node &config, int n)
SCSPDSP_Init(&SCSP->DSP); SCSPDSP_Init(&SCSP->DSP);
#endif #endif
SCSP->Master=1; SCSP->Master=1;
SCSP->SCSPRAM_LENGTH = 512 * 1024; SCSP->SCSPRAM_LENGTH = 512 * 1024;
SCSP->DSP.SCSPRAM = (UINT16 *)SCSP->SCSPRAM; SCSP->DSP.SCSPRAM = (UINT16 *)SCSP->SCSPRAM;
SCSP->DSP.SCSPRAM_LENGTH = (512 * 1024) / 2; SCSP->DSP.SCSPRAM_LENGTH = (512 * 1024) / 2;
MidiR=MidiW=0; MidiR=MidiW=0;
MidiOutR=MidiOutW=0; MidiOutR=MidiOutW=0;
@ -759,19 +762,32 @@ bool SCSP_Init(const Util::Config::Node &config, int n)
#endif #endif
LFO_Init(); LFO_Init();
buffertmpl = NULL; buffertmpfl = NULL;
buffertmpr = NULL; buffertmpfr = NULL;
buffertmpl=(signed int*) malloc(44100*sizeof(signed int)); buffertmprl = NULL;
if (NULL == buffertmpl) buffertmprr = NULL;
buffertmpfl=(signed int*) malloc(44100*sizeof(signed int));
if (NULL == buffertmpfl)
return ErrorLog("Insufficient memory for internal SCSP buffers."); return ErrorLog("Insufficient memory for internal SCSP buffers.");
buffertmpr=(signed int*) malloc(44100*sizeof(signed int)); buffertmpfr=(signed int*) malloc(44100*sizeof(signed int));
if (NULL == buffertmpl) if (NULL == buffertmpfr)
{ {
free(buffertmpl); free(buffertmpfr);
return ErrorLog("Insufficient memory for internal SCSP buffers."); 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; SCSPs->data[0x20 / 2] = 0;
TimCnt[0] = 0xffff; TimCnt[0] = 0xffff;
TimCnt[1] = 0xffff; TimCnt[1] = 0xffff;
@ -781,8 +797,10 @@ bool SCSP_Init(const Util::Config::Node &config, int n)
MIDILock = CThread::CreateMutex(); MIDILock = CThread::CreateMutex();
if (NULL == MIDILock) if (NULL == MIDILock)
{ {
free(buffertmpl); free(buffertmpfl);
free(buffertmpr); free(buffertmpfr);
free(buffertmprl);
free(buffertmprr);
return ErrorLog("Unable to create MIDI mutex!"); return ErrorLog("Unable to create MIDI mutex!");
} }
@ -1111,7 +1129,7 @@ void SCSP_w16(unsigned int addr,unsigned short val)
SCSP_UpdateReg(addr & 0x3f); SCSP_UpdateReg(addr & 0x3f);
} }
} }
else if (addr < 0x700) else if (addr < 0x700)
SCSP->RINGBUF[(addr - 0x600) / 2] = val; SCSP->RINGBUF[(addr - 0x600) / 2] = val;
else else
{ {
@ -1544,21 +1562,25 @@ void SCSP_DoMasterSamples(int nsamples)
balance /= 100.0f; balance /= 100.0f;
float masterBalance = 1.0f + balance; float masterBalance = 1.0f + balance;
float slaveBalance = 1.0f - balance; float slaveBalance = 1.0f - balance;
signed short *bufl, *bufr; signed short* buffl, * buffr;
signed short* bufrl, * bufrr;
INT32 sl, s, i; INT32 sl, s, i;
bufl = bufferl; buffl = bufferfl;
bufr = bufferr; buffr = bufferfr;
bufrl = bufferrl;
bufrr = bufferrr;
/* /*
* Generate samples * Generate samples
*/ */
for (s = 0; s < nsamples; ++s) 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 #if FM_DELAY
RBUFDST = SCSPs[0].DELAYBUF + SCSPs[0].DELAYPTR; 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)); SCSPDSP_SetSample(&SCSPs[0].DSP, (sample*LPANTABLE[Enc]) >> (SHIFT - 2), ISEL(slot), IMXL(slot));
Enc = ((TL(slot)) << 0x0) | ((DIPAN(slot)) << 0x8) | ((DISDL(slot)) << 0xd); Enc = ((TL(slot)) << 0x0) | ((DIPAN(slot)) << 0x8) | ((DISDL(slot)) << 0xd);
#ifdef RB_VOLUME #ifdef RB_VOLUME
smpl += (sample * volume[TL(slot) + pan_left[DIPAN(slot)]]) >> 17; smpfl += (sample * volume[TL(slot) + pan_left[DIPAN(slot)]]) >> 17;
smpr += (sample * volume[TL(slot) + pan_right[DIPAN(slot)]]) >> 17; smpfr += (sample * volume[TL(slot) + pan_right[DIPAN(slot)]]) >> 17;
#else #else
{ {
smpl += (sample*LPANTABLE[Enc]) >> SHIFT; smpfl += (sample*LPANTABLE[Enc]) >> SHIFT;
smpr += (sample*RPANTABLE[Enc]) >> SHIFT; smpfr += (sample*RPANTABLE[Enc]) >> SHIFT;
} }
#endif #endif
} }
@ -1615,11 +1637,11 @@ void SCSP_DoMasterSamples(int nsamples)
Enc = ((TL(slot)) << 0x0) | ((DIPAN(slot)) << 0x8) | ((DISDL(slot)) << 0xd); Enc = ((TL(slot)) << 0x0) | ((DIPAN(slot)) << 0x8) | ((DISDL(slot)) << 0xd);
{ {
#ifdef RB_VOLUME #ifdef RB_VOLUME
smpl += (sample * volume[TL(slot) + pan_left[DIPAN(slot)]]) >> 17; smprl += (sample * volume[TL(slot) + pan_left[DIPAN(slot)]]) >> 17;
smpr += (sample * volume[TL(slot) + pan_right[DIPAN(slot)]]) >> 17; smprr += (sample * volume[TL(slot) + pan_right[DIPAN(slot)]]) >> 17;
#else #else
smpl += (sample*LPANTABLE[Enc]) >> SHIFT; smprl += (sample*LPANTABLE[Enc]) >> SHIFT;
smpr += (sample*RPANTABLE[Enc]) >> SHIFT; smprr += (sample*RPANTABLE[Enc]) >> SHIFT;
} }
#endif #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 // 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); UINT16 Enc = ((EFPAN(slot)) << 0x8) | ((EFSDL(slot)) << 0xe);
smpl += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT))); smpfl += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT)));
smpr += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT))); smpfr += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT)));
} }
if (HasSlaveSCSP) if (HasSlaveSCSP)
{ {
@ -1659,8 +1681,8 @@ void SCSP_DoMasterSamples(int nsamples)
if (EFSDL(slot)) if (EFSDL(slot))
{ {
UINT16 Enc = ((EFPAN(slot)) << 0x8) | ((EFSDL(slot)) << 0xe); UINT16 Enc = ((EFPAN(slot)) << 0x8) | ((EFSDL(slot)) << 0xe);
smpl += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT))); smprl += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT)));
smpr += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * RPANTABLE[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)) if (EFSDL(slot))
{ {
UINT16 Enc = ((EFPAN(slot)) << 0x8) | ((EFSDL(slot)) << 0xd); UINT16 Enc = ((EFPAN(slot)) << 0x8) | ((EFSDL(slot)) << 0xd);
smpl += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT))); smpfl += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT)));
smpr += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT))); smpfr += (int)(masterBalance*(float)(((SCSPs[0].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT)));
} }
if (HasSlaveSCSP) if (HasSlaveSCSP)
{ {
@ -1677,32 +1699,47 @@ void SCSP_DoMasterSamples(int nsamples)
if (EFSDL(slot)) if (EFSDL(slot))
{ {
UINT16 Enc = ((EFPAN(slot)) << 0x8) | ((EFSDL(slot)) << 0xd); UINT16 Enc = ((EFPAN(slot)) << 0x8) | ((EFSDL(slot)) << 0xd);
smpl += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT))); smprl += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * LPANTABLE[Enc]) >> SHIFT)));
smpr += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT))); smprr += (int)(slaveBalance*(float)(((SCSPs[1].DSP.EFREG[i] * RPANTABLE[Enc]) >> SHIFT)));
} }
} }
} }
} }
if (DAC18B(SCSP)) if (DAC18B((&SCSP[0])))
{ {
smpl = ICLIP18(smpl); smpfl = ICLIP18(smpfl);
smpr = ICLIP18(smpr); smpfr = ICLIP18(smpfr);
} }
else else
{ {
smpl = ICLIP16(smpl >> 2); smpfl = ICLIP16(smpfl >> 2);
smpr = ICLIP16(smpr >> 2); smpfr = ICLIP16(smpfr >> 2);
} }
*bufl++ = ICLIP16(smpl); *buffl++ = ICLIP16(smpfl);
*bufr++ = ICLIP16(smpr); *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); SCSP_TimersAddTicks(1);
CheckPendingIRQ(); CheckPendingIRQ();
lastdiff = Run68kCB(slice - lastdiff); lastdiff = Run68kCB(slice - lastdiff);
}
} }
}
void SCSP_Update() 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; SoundClock = 76;
bufferl = leftBufferPtr; bufferfl = leftBufferPtr;
bufferr = rightBufferPtr; bufferfr = rightBufferPtr;
bufferrl = leftRearBufferPtr;
bufferrr = rightRearBufferPtr;
length = bufferLength; length = bufferLength;
cnts = 0; // what is this for? seems unimportant but need to find out cnts = 0; // what is this for? seems unimportant but need to find out
} }
@ -2121,10 +2161,14 @@ void SCSP_Deinit(void)
#ifdef USEDSP #ifdef USEDSP
free(SCSP->MIXBuf); free(SCSP->MIXBuf);
#endif #endif
free(buffertmpl); free(buffertmpfl);
free(buffertmpr); free(buffertmpfr);
free(buffertmprl);
free(buffertmprr);
delete MIDILock; delete MIDILock;
buffertmpl = NULL; buffertmpfl = NULL;
buffertmpr = NULL; buffertmpfr = NULL;
buffertmprl = NULL;
buffertmprr = NULL;
MIDILock = NULL; MIDILock = NULL;
} }

View file

@ -83,7 +83,7 @@ UINT32 SCSP_Slave_r32(UINT32 addr);
// Supermodel interface functions // Supermodel interface functions
void SCSP_SaveState(CBlockFile *StateFile); void SCSP_SaveState(CBlockFile *StateFile);
void SCSP_LoadState(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); void SCSP_Deinit(void);

View file

@ -21,28 +21,29 @@
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<ProjectGuid>{1248CF7C-B122-461C-9624-196AEFAE5046}</ProjectGuid> <ProjectGuid>{1248CF7C-B122-461C-9624-196AEFAE5046}</ProjectGuid>
<RootNamespace>Musashi68K</RootNamespace> <RootNamespace>Musashi68K</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>MultiByte</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>MultiByte</CharacterSet>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

View file

@ -22,24 +22,24 @@
<ProjectName>SDL2</ProjectName> <ProjectName>SDL2</ProjectName>
<ProjectGuid>{81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}</ProjectGuid> <ProjectGuid>{81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}</ProjectGuid>
<RootNamespace>SDL</RootNamespace> <RootNamespace>SDL</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">

View file

@ -22,24 +22,24 @@
<ProjectName>SDL2main</ProjectName> <ProjectName>SDL2main</ProjectName>
<ProjectGuid>{DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}</ProjectGuid> <ProjectGuid>{DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}</ProjectGuid>
<RootNamespace>SDLmain</RootNamespace> <RootNamespace>SDLmain</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">

View file

@ -22,24 +22,24 @@
<ProjectName>SDL2_net</ProjectName> <ProjectName>SDL2_net</ProjectName>
<ProjectGuid>{8AB3504F-5E58-4910-AFE8-7A1E595AC3F4}</ProjectGuid> <ProjectGuid>{8AB3504F-5E58-4910-AFE8-7A1E595AC3F4}</ProjectGuid>
<RootNamespace>SDL_net</RootNamespace> <RootNamespace>SDL_net</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">

View file

@ -22,29 +22,29 @@
<ProjectGuid>{B114BBD9-8AEA-4DAE-B367-A66A804CB3DD}</ProjectGuid> <ProjectGuid>{B114BBD9-8AEA-4DAE-B367-A66A804CB3DD}</ProjectGuid>
<RootNamespace>Supermodel</RootNamespace> <RootNamespace>Supermodel</RootNamespace>
<Keyword>Win32Proj</Keyword> <Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

View file

@ -25,22 +25,22 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<UseOfMfc>false</UseOfMfc> <UseOfMfc>false</UseOfMfc>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<UseOfMfc>false</UseOfMfc> <UseOfMfc>false</UseOfMfc>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<UseOfMfc>false</UseOfMfc> <UseOfMfc>false</UseOfMfc>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<UseOfMfc>false</UseOfMfc> <UseOfMfc>false</UseOfMfc>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />