Skip to content

Commit

Permalink
Added Windy Fairy's Konami System 573 digital audio offset plugin.
Browse files Browse the repository at this point in the history
  • Loading branch information
cuavas committed Mar 22, 2023
1 parent 4fb4453 commit 7bec664
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
* text=auto !eol

*.json svneol=native#application/json
*.lua svneol=native#text/plain
*.md svneol=native#text/plain
26 changes: 26 additions & 0 deletions .github/workflows/plugins.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Validate plugins

on:
push:
paths:
- '.github/workflows/**'
- 'plugins/**'
pull_request:
paths:
- '.github/workflows/**'
- 'plugins/**'

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y python3-jsonschema
- name: Validate plugin properties
run: for x in plugins/*/plugin.json ; do jsonschema -i "$x" plugins/plugin.schema ; done
35 changes: 35 additions & 0 deletions plugins/ksys573_da_offset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Konami System 573 digital audio offset plugin

This plugin works by intercepting audio timer register reads in Konami System 573 digital hardware games and may not work perfectly in every game depending on how it uses the timer register.

## Configuration

Here is an example **settings.json** commented to demonstrate how to add game-specific offsets and a default offset to be used for all games on System 573 digital hardware without a specific offset specified.

A default offset of 28 ms is what I personally found to be most useful when running MAME on Windows, with the PortAudio sound output module using the WASAPI back-end for lowest audio latency. I suggest you experiment with the default offset to figure out what works best for your setup.

```json
{
"default": "28ms", // Specify a default offset for all System 573 digital audio games unless overridden
// Setting this to 0 is the equivalent of disabling the default override
// Can be specified as a number of samples (e.g. 1234) or a delay in milliseconds (e.g. 1234ms)
"overrides": {
"ddrmax": 1234, // Offset for the game "ddrmax" specified as a number of samples
"ddr5m": "50ms", // Offset for the gaem "ddr5m" specified as a delay in milliseconds (automatically converted to a number of samples)
}
}
```

## License

Copyright © 2022-2023 windyfairy

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
168 changes: 168 additions & 0 deletions plugins/ksys573_da_offset/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
-- license:BSD-3-Clause
-- copyright-holders:windyfairy
local exports = {
name = 'ksys573_da_offset',
version = '0.0.1',
description = 'Konami System 573 digital audio offset plugin',
license = 'BSD-3-Clause',
author = { name = 'windyfairy' }
}


local plugindir


local function load_settings()
local function calculate_offset_from_milliseconds(milliseconds)
return math.floor((emu.attotime.from_msec(milliseconds):as_double() * 44100) + 0.5)
end

local function get_processed_offset_value(val)
local match = string.match(val, "([%d]+[%.]?[%d]*)%s*[mM][sS]")
if match ~= nil then
return calculate_offset_from_milliseconds(tonumber(match))
end

return math.floor(val + 0.5)
end

local json = require('json')
local filename = plugindir .. '/settings.json'
local file = io.open(filename, 'r')
local default_offset = calculate_offset_from_milliseconds(28)

local loaded_settings
if file then
loaded_settings = json.parse(file:read('a'))
file:close()
end
if not loaded_settings then
emu.print_error(string.format('Error loading System 573 audio offset settings: error opening or parsing %s as JSON', filename))
loaded_settings = {}
end

if loaded_settings['default'] == nil then
loaded_settings['default'] = default_offset
end
loaded_settings['default'] = get_processed_offset_value(loaded_settings['default'])

if loaded_settings['overrides'] == nil then
loaded_settings['overrides'] = {}
end

for k, v in pairs(loaded_settings["overrides"]) do
loaded_settings['overrides'][k] = get_processed_offset_value(v)
end

return loaded_settings
end


local ksys573_da_offset = exports

function ksys573_da_offset.set_folder(path)
plugindir = path
end

function ksys573_da_offset.startplugin()
local counter_offset
local passthrough_counter_high, passthrough_counter_low

local function install_counter_passthrough(memory)
local max = math.max
local memory_read_i16 = memory.read_i16
local counter_current = 0
local is_callback_read = false

local function get_offset_counter()
return max(0, counter_current - counter_offset)
end

local function counter_high_callback(offset, data, mask)
if mask == 0xffff0000 then
-- hack because reading memory directly also calls the callback
if is_callback_read == true then
return data
end

return get_offset_counter() & mask
end

return data
end

local function counter_low_callback(offset, data, mask)
if mask == 0x0000ffff then
is_callback_read = true
local counter_upper = memory_read_i16(memory, 0x1f6400ca)
is_callback_read = false
counter_current = (data & mask) | (counter_upper << 16)
return get_offset_counter() & mask
end

return data
end

passthrough_counter_high = memory:install_read_tap(0x1f6400c8, 0x1f6400cb, "counter_high", counter_high_callback)
passthrough_counter_low = memory:install_read_tap(0x1f6400cc, 0x1f6400cf, "counter_low", counter_low_callback)
end

local function menu_callback(index, event)
return false
end

local function menu_populate()
local menu = {}
table.insert(menu, { 'System 573 Digital Audio Delay', '', 'off' })
if not counter_offset then
table.insert(menu, { 'Not applicable for this system', '', 'off' })
elseif counter_offset == 0 then
table.insert(menu, { 'No offset specified for this system', '', 'off' })
else
table.insert(menu, { string.format('%d samples', counter_offset), '', 'off' })
end
return menu
end

emu.register_start(
function ()
if not manager.machine.devices[":k573dio"] then
counter_offset = nil
return
end

local settings = load_settings()
counter_offset = settings['default']

local override_offset = settings['overrides'][manager.machine.system.name]
if override_offset ~= nil then
emu.print_verbose(string.format('System 573 audio offset override for %s found: %s -> %s', manager.machine.system.name, counter_offset, override_offset))
counter_offset = override_offset
end

-- don't hook the code if no offset is specified
if counter_offset == 0 then
return
end

install_counter_passthrough(manager.machine.devices[":maincpu"].spaces["program"], counter_offset)
emu.print_verbose(string.format('System 573 audio counter is now being offset by %s samples.', counter_offset))
end)

emu.register_stop(
function ()
if passthrough_counter_high then
passthrough_counter_high:remove()
passthrough_counter_high = nil
end
if passthrough_counter_low then
passthrough_counter_low:remove()
passthrough_counter_low = nil
end
counter_offset = nil
end)

emu.register_menu(menu_callback, menu_populate, 'System 573 Digital Audio Offset')
end

return exports
10 changes: 10 additions & 0 deletions plugins/ksys573_da_offset/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"plugin": {
"name": "ksys573_da_offset",
"description": "Konami System 573 digital audio offset plugin",
"version": "0.0.1",
"author": "windyfairy",
"type": "plugin",
"start": "true"
}
}
4 changes: 4 additions & 0 deletions plugins/ksys573_da_offset/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"default": "0ms",
"overrides": {}
}
35 changes: 35 additions & 0 deletions plugins/plugin.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"type": "object",
"properties": {
"plugin": {
"type": "object",
"properties": {
"name": {
"type": "string",
"pattern": "^[A-Za-z][0-9A-Za-z_]*$"
},
"description": {
"type": "string"
},
"version": {
"type": "string"
},
"author": {
"type": "string"
},
"type": {
"type": "string",
"pattern": "^(plugin|library)$"
},
"start": {
"type": "string",
"pattern": "^(true|false)$"
}
},
"additionalProperties": false,
"required": [ "name", "description", "version", "author", "type" ]
}
},
"additionalProperties": false,
"required": [ "plugin" ]
}

0 comments on commit 7bec664

Please sign in to comment.