Releases: RealBitdancer/opal
Releases · RealBitdancer/opal
Release list
opal v2.0.0
This release breaks the public API deliberately. Every name now follows one consistent
convention, and the index-based layout removes the structure copy problem that plagued
the original public-domain source and every previous opal release, where an initialized
chip could never be copied or moved.
Added
- Public constants for the two state fields that previously spoke in magic numbers:
OPAL_ENVELOPE_STAGE_*(OFF, ATTACK, DECAY, SUSTAIN, RELEASE) for
OpalOperator::envelopeStageandOPAL_CHANNEL_TYPE_*(2OP, 4OP, 4OP2, DRUM) for
OpalChannel::chanType. Visualizers no longer hardcode the values.
Changed
- The whole library follows one naming convention: functions are
opalplus PascalCase
(opalWriteReg), types areOpalplus PascalCase with the struct tag matching the
typedef (OpalChannel, no_tsuffix), struct members are camelCase (egOut,
opSlot), and macros stayOPAL_SCREAMING_CASE. The old mixed style kept the
public-domain source readable against upstream. That concern is retired, and the
internal helpers insrc/opal.cfollow the same convention now. - Three functions are renamed for accuracy as well as style:
Opal_Portis now
opalWriteReg(the two-port address/data dance was never emulated, a register is
written directly),Opal_PortBufferedis nowopalWriteRegBuffered, andOpal_Read
is nowopalReadStatus(the status register is the only readable thing). The other
five change style only:opalInit,opalSetSampleRate,opalFlushWriteBuf,
opalPan, andopalSample. Signatures and behavior are unchanged. - The
Opalstruct is position independent. Every internal cross reference is an index
instead of a pointer:OpalChannel::Op[]becameopSlot[](operator indices),
ChannelPairbecamepairIndex,OutPtr[]becameoutSource[](operator indices
whose outputs sum into the channel), andOpalOperator::ModbecamemodSource
(an operator index,OPAL_MOD_OWN_FB, orOPAL_OP_NONE). Copying, assigning, or
memcpying an instance yields a fully working chip, so a plain copy is a save state.
Rendered output is bit-identical to 1.0.2. - This is a breaking layout change for consumers that read
Opalinternals, such as
visualizers. The new index fields make the active FM routing readable as data, which
pointers never were. - The boolean flags (
noteSel,tremoloEnable, and friends) are C11boolinstead of
theopal_boolint typedef. - The constants moved from an enum to defines:
OpalOPL3SampleRateis now
OPAL_OPL3_SAMPLE_RATE,OpalNumChannelsisOPAL_CHANNEL_COUNT, and
OpalNumOperatorsisOPAL_OPERATOR_COUNT. The header guard is
OPAL_OPAL_H_INCLUDED.
Removed
- The
MasterandChanback pointers on operators, theMasterand pointer-based
pair and routing fields on channels, and theZeroModfield on the chip. The no-copy
rule and every warning about it are gone with them. opal_bool,OPAL_TRUE, andOPAL_FALSE.
Documentation
- README and CONTRIBUTING describe the single naming convention. The section explaining
why the core kept its upstream names is gone, since the reason went with the names. - BINDINGS.md lists binding versions as the bound opal release plus a packaging
revision (2.0.0-1is the first opal-zig release binding opal2.0.0).
opal v1.0.2
Added
- The web player accepts a
?play=1query parameter and starts the first track on load, or
on the first tap where the browser demands a gesture. The README's live demo links use it. - BINDINGS.md lists third-party language bindings, starting with
opal-zig.
Fixed
- The example player's DRO decoder assumed the 24-byte v1 header. The earliest DOSBox builds
wrote a single-byte hardware type (21-byte header), so those files lost their first commands
or desynced. Nonzero bytes in the padding now identify them (AdPlug's heuristic). - The web player's nine channel meters no longer run a cosmetic CSS animation. They track the
live OPL envelopes (EgOuton each channel's modulator and carrier) via a newwebLevels
export, so bars rise and fall with the music being played.
Documentation
opal.hdocuments the channel-to-operator slot layout (modulatorOp[slot], carrier
Op[slot + 3], with thech_slottable inlined) so state snapshot consumers such as
visualizers stop guessing a sequential pair layout that is wrong for every channel but 0.opal.hdocuments the operator fields most useful to visualizers:EnvelopeStagevalues,
EgOutas total attenuation in 0.1875 dB steps, andKeyas a key-on source bitmask.opal.hwarns thatOpal_Initwires internal cross pointers, so an instance must be
initialized in place and never copied or relocated afterwards.- README and CONTRIBUTING cover DRO early headers,
Opalpointer stability, buffered ports,
CI on main only, and core versus player naming.
opal v1.0.1
Fixed
- Two operator channels ignored the connection bit (C0 bit 0) and always played serial FM.
Additive instruments are additive again. - Enabling four operator mode via register 0x104, or writing C0 on the primary channel of a
pair, never installed the operator routing. Only a C0 write on the secondary channel did. - Four operator connection modes 2 and 3 used wrong operator topologies. All four modes now
match the YMF262 algorithms. - Disabling four operator mode left both channels of the pair wired as halves of the old
four operator channel. - Note select (register 08 bit 6) picked the wrong F-Number bit for the key scale number,
which skewed KSR and KSL rates. The value is also latched on frequency writes now, so
changing NTS no longer rescales notes already sounding. - An A0 write to a four operator primary recomputed the secondary key scale number from the
secondary octave instead of inheriting the primary value. - OPL3 waveforms 4 to 7 were folded back to 0 to 3 the moment OPL3 mode switched off. The
hardware keeps playing the stored waveform, so opal does too.
Changed
- The status register now uses the hardware layout. Bit 7 is IRQ, bit 6 timer 1 overflow,
bit 5 timer 2 overflow, and flags persist until a reset write to register 4 instead of
clearing on read. - The
Opal_Pancomment described a channel number multiplied by 256. The function wants a
plain channel index 0 to 17 and the comment now says so. - The README no longer lists OPL2 waveform select as a feature. The enable bit is stored
and ignored, since the YMF262 ignores it too.
opal v1.0.0
Changelog
All notable changes to this project are documented here.
[1.0.0] - 2026-07-07
Added
- C11 static library with an eight-function public API (
Opal_Init,Opal_SetSampleRate,
Opal_Port,Opal_PortBuffered,Opal_FlushWriteBuf,Opal_Pan,Opal_Sample,Opal_Read) - Full OPL2 and OPL3 stereo synthesis with two and four operator FM, eight waveforms, tremolo
and vibrato LFOs, rhythm mode, timers, and the status register - Linear resampling from the native 49716 Hz to any output rate
- Example
playerthat streams DRO, HSC, and IMF or WLF files to the audio device via miniaudio - Seven public-domain HSC demo tracks under
music/from HSCDEMO3.EXE (1992) by Hannes Seifert,
limited to the tracks the demo itself marksindependentorpublic domain - WebAssembly player (
examples/web) that compiles the core and format decoders with Emscripten
behind a tracker-styled browser front end, built by thewebCMake preset - Audio unlock on iOS so the ring switch no longer mutes Web Audio, done by starting the device
inside the tap and playing a silent looping media element - CMake install rules and a
find_package(opal)package config - CMake presets for Windows (MSVC, Clang, and MinGW in 32 and 64 bit), Linux (GCC), and macOS
(Clang), plus library-only cross presets for Android (NDK), iOS, and WebAssembly - GitHub Actions workflows that build every preset on Windows, Linux, macOS, Android, iOS, and
the web on each push to main, deploy the web player to GitHub Pages, and drive the
per-platform status badges in the README
Notes
- Stereo output matches established YMF262 reference emulators at chip rate
- DRO playback handles version 1 only, meaning
DBRAWOPLwith a version word of zero - The CSW register bit is decoded but left inert, as on real YMF262 hardware