feat: OplChip bare-chip wrapper over nuked-opl3#3
Merged
Conversation
8330ecd to
2330083
Compare
Owner
|
@abedegno looks like you might need to resolve the conflict from the new binary on the other PR before I can merge this one |
Exposes OPL3_Reset / OPL3_WriteReg / OPL3_GenerateStream directly so callers can synthesize their own register streams (e.g. Ultima Underworld TVFX sound effects) without going through the MIDI/bank layer. Multi-targets net8.0;net10.0 for the test project so CI continues to pass on machines with only one framework installed.
The previous revision P/Invoked OPL3_Reset / OPL3_WriteReg / OPL3_GenerateStream directly out of libadlmidi's internal nuked-opl3 source. Those symbols happened to leak out of the macOS dylib (where the existing build linked without visibility filtering) but are not part of libadlmidi's public ABI and are absent from the Windows DLL and Linux .so export tables — so a reviewer on Windows got EntryPointNotFoundException. Rework against the new adl_barechip_* ABI (https://github.com/abedegno/libADLMIDI feat/barechip-wrapper, based on Wohlstand/libADLMIDI 89284b0, v1.6.2). The new API owns chip allocation (returns an opaque handle), which also lets us drop the 64 KB AllocHGlobal trick we used to carve space for the opl3_chip struct from managed code. Rebuilt binaries for osx-arm64 (native clang), win-x64, win-x86 (mingw-w64 via Docker), and linux-x64 (gcc via Docker). All four now export adl_barechip_new / _free / _write_reg / _generate / _reset, verified with nm (macOS / Linux) and objdump -p (Windows). The full ADLMIDI_DECLSPEC-decorated adl_* ABI is still exported intact on Windows (213 exports total in libADLMIDI.dll).
Reworked per upstream feedback on libADLMIDI PR #310: drop the
parallel adl_barechip_* ABI and instead sit on two additions to
libadlmidi's existing public API:
* adl_rt_rawOplCommand(device, chipId, reg, val) — raw register
write reusing the same internal path used by IMF/KLM playback.
* adl_reserveChipChannels(device, chipId, mask) — mark chip
channels as off-limits to the MIDI voice allocator so raw writes
and MIDI playback can share a chip.
OplChip now:
- Wraps an ADL_MIDIPlayer* rather than a bare chip handle.
- Create(sr) allocates its own device, sets 1 chip, reserves all
23 per-chip channels so MIDI never touches it.
- FromPlayer(device, chipId, mask) lets callers reuse an
already-initialised MidiPlayer device for coexisting MIDI + raw
SFX on the same chip.
- Reset() calls adl_reset() (owning instances only) and re-asserts
the channel reservation.
Test updates:
- OplChipTests.Note_on sets OPL3 panning bits (0xC0 |= 0x30)
because libadlmidi initialises chips in OPL3-extended mode where
those bits gate L/R output.
- DllImportFixture guards SetDllImportResolver with a static flag:
with a second test class (OplChipTests) sharing the fixture,
duplicate registration threw "A resolver is already set".
Native binaries refreshed for all 4 RIDs from
abedegno/libADLMIDI feat/barechip-wrapper at commit caa5db4.
All 5 ADLMidi.NET tests pass; all 47 UnderworldGodot Sfx tests pass.
Follows the API rename in libADLMIDI (review feedback on PR #310): adl_rt_rawOplCommand -> adl_rt_rawOPL3. Refresh native libs from the updated libADLMIDI build across all 4 RIDs.
14b0ac1 to
49d2568
Compare
Contributor
Author
|
@csinkers - Rebased on top of the macOS PR merge, CI is green. |
Owner
|
Nice one, I've pushed out a NuGet package with your changes (version 1.2.0) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a thin managed wrapper,
ADLMidi.NET.OplChip, exposing libadlmidi's embedded nuked-opl3 chip at the register-write level (OPL3_Reset,OPL3_WriteReg,OPL3_GenerateStream). Lets callers synthesize their own OPL register streams — bypassing the MIDI / bank / sequencer layers — for use cases like rendering proprietary effect formats (my specific driver: Ultima Underworld 1 TVFX sound effects, which encode each SFX as a state-machine over raw OPL2 parameters, not as MIDI patches).The three native symbols are already exported from the shipped
libadlmidi.dylib/.so/.dll, so no native-side changes are needed — this PR is pure C# + one test CSProj tweak.What changed
src/ADLMidi.NET/OplChip.cs— new public class. Allocates theopl3_chipstruct viaMarshal.AllocHGlobal(hardcoded 64 KB — nuked-opl3's struct is ~54 KB, dominated bywritebuf[2048]; 10 KB headroom).src/ADLMidi.NET.Tests/OplChipTests.cs— 3 tests (silent chip → zero samples; OPL2 note-on → audible output; reset → silence).src/ADLMidi.NET.Tests/ADLMidi.NET.Tests.csproj— multi-targetnet8.0;net10.0so tests build on machines with either framework installed.Stacking
Stacks on #1 (macOS ARM64 test-fixture support) and #2 (adl_rt). This PR's actual changes are independent of both — only the branch base is stacked so the test infra resolves the dylib on arm64 Macs during development. GitHub will auto-rebase onto master as #1 and #2 land.
Tradeoffs
opl3_chippast 64 KB, the allocation bound will need to bump. Happy to expose asizeofhelper from native side if you prefer not to carry the assumption.OplChip.Reset()re-initialises at 44100 since the sample-rate is stashed inside the opaque struct. Callers needing a different rate dispose + re-create. Documented on the method.Test plan
dotnet test --filter OplChipTests— 3/3 pass (net10.0 on macOS arm64)