Skip to content

Add Yamaha MA FM synthesis for SMAF, plus HandyPhone score support - #286

Merged
psi29a merged 4 commits into
masterfrom
smaflib
Jul 26, 2026
Merged

Add Yamaha MA FM synthesis for SMAF, plus HandyPhone score support#286
psi29a merged 4 commits into
masterfrom
smaflib

Conversation

@psi29a

@psi29a psi29a commented Jul 23, 2026

Copy link
Copy Markdown
Member

Decode the HandyPhone Standard score format (format_type 0x00, MA-1/MA-2) in smaf2mid.c, merging its multi-track streams to MIDI.

Add an optional FM synthesis engine (WANT_MAFM) so SMAF files play with their real Yamaha voices instead of the GM approximation: port the FM core, voice-exclusive decoder, and ADPCM codec to C from the (Apache-2.0) akustikrausch/yamaha-smaf-player, wrapped in src/mafm.c and wired into the render loop like the SF2 path (per-mdi mafm_synth). Embedded ATR/Awa ADPCM samples play as scheduled drums alongside the FM voices.

Also document the MFi (.mld) format and update SmafFileFormat.txt.
SMAF_handyphone_test files.zip

Screenshot From 2026-07-23 10-23-22

SolidSoda.mp3 (older version)

SolidSoda.mp3 (latest version)

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b503160b-3654-4dde-b454-1c624e2712db

📥 Commits

Reviewing files that changed from the base of the PR and between 9a99188 and 1077a7f.

📒 Files selected for processing (3)
  • docs/man/man3/WildMidi_Open.3
  • docs/man/man3/WildMidi_OpenBuffer.3
  • src/smaf2mid.c
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/man/man3/WildMidi_Open.3
  • src/smaf2mid.c

📝 Walkthrough

Walkthrough

Adds SMAF/MMF parsing and MIDI conversion, Yamaha MAFM FM and ADPCM rendering, custom voice handling, runtime playback routing, error reporting, documentation, and MAFM-enabled builds across supported platforms.

Changes

SMAF and MAFM support

Layer / File(s) Summary
SMAF contracts and format definitions
include/*, src/mafm/*.h, docs/SMAF_FM.md, docs/formats/SmafFileFormat.txt, NOTICE
Adds public SMAF/MAFM APIs, FM and ADPCM data structures, error definitions, format documentation, design status, third-party attribution, and user-facing format descriptions.
SMAF-to-MIDI conversion
src/smaf2mid.c, src/f_smaf.c, src/root.zig, src/wm_error.c
Converts HandyPhone and Mobile Standard SMAF score tracks to format-0 MIDI and parses MMMD input through the new SMAF frontend.
MAFM voice and PCM engine
src/mafm.c, src/mafm/*
Adds Yamaha voice-exclusive decoding, FM synthesis, GM/drum approximations, Yamaha ADPCM decoding, ATR wave scheduling, event handling, voice management, and stereo rendering.
Runtime playback integration
src/wildmidi_lib.c, src/internal_midi.c, src/player/playlist.c
Detects SMAF input, routes custom-voice files to MAFM rendering, synchronizes seek state, frees MAFM instances, and adds mix-buffer overflow checks.
Cross-platform build integration
CMakeLists.txt, src/CMakeLists.txt, build.zig, */Makefile*, */config.h
Adds MAFM feature controls and includes new SMAF/MAFM sources and objects in platform builds.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant wildmidi_lib
  participant f_smaf
  participant smaf2mid
  participant MAFM
  participant OutputBuffer
  Client->>wildmidi_lib: Load MMMD data
  wildmidi_lib->>f_smaf: _WM_ParseNewSmaf
  f_smaf->>smaf2mid: _WM_smaf2midi
  f_smaf->>MAFM: Detect custom voices and create synth
  wildmidi_lib->>MAFM: Feed events and render PCM
  MAFM->>OutputBuffer: Accumulate stereo samples
  OutputBuffer-->>Client: Return audio output
Loading

Suggested reviewers: sezero

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main changes: SMAF HandyPhone score support and optional Yamaha MA FM synthesis.
Description check ✅ Passed The description is clearly related to the changeset and matches the SMAF/FM synthesis and documentation updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@psi29a

psi29a commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

Still WIP but wanted the coderabbit output :)

@psi29a
psi29a marked this pull request as ready for review July 23, 2026 08:36

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (1)
build.zig (1)

72-72: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider exposing MAFM as an opt-out build option.

WILDMIDI_MAFM is hardcoded to 1 for every zig-package consumer with no way to disable the (still WIP) FM engine. A b.option(bool, "mafm", "Enable Yamaha MA FM synthesis", true) would let consumers opt out while keeping the current default.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@build.zig` at line 72, Replace the hardcoded WILDMIDI_MAFM value in the build
configuration with a boolean b.option named "mafm", described as enabling Yamaha
MA FM synthesis and defaulting to true. Use that option’s value for
WILDMIDI_MAFM so package consumers can disable the WIP FM engine while
preserving the current default.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/formats/SmafFileFormat.txt`:
- Around line 393-399: Update decode_all_handyphone and decode_handyphone so
Rhythm-typed channels are not added to perc_mask or rerouted to MIDI channel 9;
preserve their original channel and distinct programs, matching the documented
sample-corpus behavior.
- Line 21: Correct the spelling of “decyphered” to “deciphered” in the
descriptive sentence.

In `@src/mafm.c`:
- Around line 340-346: Update _WM_MAFM_HasCustomVoices to allocate the temporary
mafm_synth probe on the heap instead of placing it on the stack, preserving the
existing input validation and bank-count check. Handle allocation failure by
returning no custom voices, pass the allocated instance to mafm_build_bank, read
bank_count, free the instance before returning, and ensure every return path
releases allocated memory.
- Line 206: Update the PCM allocation in the surrounding decode flow before
_WM_MAFM_AdpcmDecodeAll to use calloc for 2*alen int16_t samples, allowing the
allocation to validate the total product and prevent overflow-based
under-allocation. Preserve the existing w->pcm ownership and failure handling.
- Around line 350-365: Thread the drum note through patch selection instead of
using the channel program: update mafm_select_patch to accept a note parameter
and pass that note to _WM_MAFM_DrumApprox when is_drum is true, while retaining
program-based selection for non-drum patches. Update the mafm_note_on call site
to provide the current note and adjust any affected declarations or callers.

In `@src/root.zig`:
- Around line 163-165: Replace the removed std.process.getEnvVarOwned call in
the FREEPATS_PATH setup with a Zig 0.16-compatible environment lookup, such as
std.c.getenv followed by an allocator-owned copy, while preserving the existing
error.SkipZigTest behavior when the variable is unavailable. Ensure the
resulting path remains allocator-managed for subsequent use.

In `@src/smaf2mid.c`:
- Around line 696-712: Remove the Rhythm-channel percussion reroute introduced
through perc_mask in decode_all_handyphone and decode_handyphone, preserving
each channel’s normal MIDI channel, program, pan, and volume handling. Keep the
documented behavior that Rhythm-typed channels may contain melodic voices; do
not route them to MIDI channel 9 or reinterpret program values as drum pitches.
- Around line 178-200: Guard the allocation-size multiplication in both
schedule_off and hp_push before calling realloc: validate that the computed
element count fits in size_t when multiplied by sizeof(struct pending_off) or
the corresponding HandyPhone event type, returning the existing allocation
failure path on overflow. Apply the same checked-growth behavior to ctx->offs
and e->ev, including their doubling logic, while preserving normal growth and
insertion/push behavior.

In `@src/wildmidi_lib.c`:
- Around line 2398-2409: Update the mix-buffer growth logic around
mdi->mix_buffer_size to validate new_size before multiplying by sizeof(int32_t);
reject values that would overflow the allocation size, report WM_ERR_MEM, unlock
mdi->lock, and return -1 before calling realloc. Preserve the existing growth
calculation and successful reallocation behavior for safe sizes.

---

Nitpick comments:
In `@build.zig`:
- Line 72: Replace the hardcoded WILDMIDI_MAFM value in the build configuration
with a boolean b.option named "mafm", described as enabling Yamaha MA FM
synthesis and defaulting to true. Use that option’s value for WILDMIDI_MAFM so
package consumers can disable the WIP FM engine while preserving the current
default.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b8423380-5f73-47f6-bd12-c40b5758faf2

📥 Commits

Reviewing files that changed from the base of the PR and between 8c2b3d5 and 3521b08.

📒 Files selected for processing (33)
  • CMakeLists.txt
  • NOTICE
  • amiga/Makefile
  • amiga/Makefile.vbcc
  • android/jni/Android.mk
  • build.zig
  • djgpp/Makefile
  • docs/formats/SmafFileFormat.txt
  • include/config.h.cmake
  • include/f_smaf.h
  • include/internal_midi.h
  • include/mafm.h
  • include/smaf2mid.h
  • include/wm_error.h
  • macosx/Makefile
  • mingw/Makefile
  • msvc/common.mak
  • os2/makefile.emx
  • os2/makefile.wat
  • src/CMakeLists.txt
  • src/f_smaf.c
  • src/internal_midi.c
  • src/mafm.c
  • src/mafm/ma_fm_core.c
  • src/mafm/ma_fm_core.h
  • src/mafm/smaf_voice.c
  • src/mafm/smaf_voice.h
  • src/mafm/yamaha_adpcm.c
  • src/mafm/yamaha_adpcm.h
  • src/root.zig
  • src/smaf2mid.c
  • src/wildmidi_lib.c
  • src/wm_error.c

Comment thread docs/formats/SmafFileFormat.txt Outdated
Comment thread docs/formats/SmafFileFormat.txt Outdated
Comment thread src/mafm.c Outdated
Comment thread src/mafm.c
Comment thread src/mafm.c Outdated
Comment thread src/root.zig Outdated
Comment thread src/smaf2mid.c
Comment thread src/smaf2mid.c
Comment thread src/wildmidi_lib.c

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/SMAF_FM.md`:
- Line 3: Update the document status declaration in SMAF_FM.md to reflect that
implementation work exists, removing the “no implementation yet” wording while
preserving the documented M1–M4 completion state.

In `@win32wat/makefile`:
- Line 43: Complete the Win32 Watcom MAFM integration by adding ma_fm_core.obj,
smaf_voice.obj, and yamaha_adpcm.obj to the OBJ list alongside mafm.obj, and
define WILDMIDI_MAFM in CFLAGS_LIB so the guarded runtime paths in f_smaf.c and
wildmidi_lib.c are enabled.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 76da31c0-00c6-48e6-b541-b59b5db72b84

📥 Commits

Reviewing files that changed from the base of the PR and between 3521b08 and 6b684b2.

📒 Files selected for processing (5)
  • android/jni/Android.mk
  • docs/SMAF_FM.md
  • os2/makefile.wat
  • src/root.zig
  • win32wat/makefile
🚧 Files skipped from review as they are similar to previous changes (1)
  • os2/makefile.wat

Comment thread docs/SMAF_FM.md Outdated
Comment thread win32wat/makefile Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/smaf2mid.c (1)

183-186: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Check capacity before doubling it.

alloc * 2 can wrap before the new product checks run, producing a smaller allocation followed by an out-of-bounds write. Guard the existing capacity before doubling, and compare using size_t without narrowing SIZE_MAX / sizeof(...) to uint32_t.

Proposed fix
-        uint32_t na = ctx->offs_alloc ? ctx->offs_alloc * 2 : 64;
+        uint32_t na;
         struct pending_off *n;
-        if (na > (uint32_t)(SIZE_MAX / sizeof(struct pending_off))) return -1;
+        if (ctx->offs_alloc > UINT32_MAX / 2u) return -1;
+        na = ctx->offs_alloc ? ctx->offs_alloc * 2 : 64;
+        if ((size_t)na > SIZE_MAX / sizeof(*n)) return -1;

Apply the equivalent change in hp_push.

Also applies to: 468-471

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/smaf2mid.c` around lines 183 - 186, Update the capacity-growth logic in
the pending-offset allocation path and the equivalent hp_push path: validate the
existing capacity before doubling it, reject growth that would overflow, and
perform the SIZE_MAX comparison in size_t without narrowing it to uint32_t.
Preserve the realloc flow only after the doubled capacity is proven safe.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/smaf2mid.c`:
- Around line 183-186: Update the capacity-growth logic in the pending-offset
allocation path and the equivalent hp_push path: validate the existing capacity
before doubling it, reject growth that would overflow, and perform the SIZE_MAX
comparison in size_t without narrowing it to uint32_t. Preserve the realloc flow
only after the doubled capacity is proven safe.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1e07af82-9f18-4922-ba8a-d6e37d0a0129

📥 Commits

Reviewing files that changed from the base of the PR and between 6b684b2 and 8001a4a.

📒 Files selected for processing (6)
  • docs/SMAF_FM.md
  • docs/formats/SmafFileFormat.txt
  • src/mafm.c
  • src/smaf2mid.c
  • src/wildmidi_lib.c
  • win32wat/makefile
🚧 Files skipped from review as they are similar to previous changes (4)
  • win32wat/makefile
  • docs/formats/SmafFileFormat.txt
  • docs/SMAF_FM.md
  • src/mafm.c

Comment thread android/jni/Android.mk Outdated
Comment thread os2/makefile.wat Outdated
Comment thread os2/makefile.wat Outdated
Comment thread src/mafm/ma_fm_core.c Outdated
Comment thread src/mafm/smaf_voice.c Outdated
Comment thread src/mafm/smaf_voice.h Outdated
Comment thread src/mafm/yamaha_adpcm.c Outdated
Comment thread src/mafm.c
Comment thread win32wat/makefile Outdated
Comment thread win32wat/makefile Outdated
Comment thread CMakeLists.txt Outdated
@psi29a
psi29a requested a review from sezero July 25, 2026 11:18
@psi29a

psi29a commented Jul 25, 2026

Copy link
Copy Markdown
Member Author

@sezero I updated with your review, thanks :)

@psi29a

psi29a commented Jul 25, 2026

Copy link
Copy Markdown
Member Author

SolidSoda.mp3

I was very amazed by the quality.

@psi29a psi29a mentioned this pull request Jul 25, 2026
@psi29a

psi29a commented Jul 25, 2026

Copy link
Copy Markdown
Member Author

Is ready for review and eval :)

I treated it like SF2 so that if people don't need it, then they compile without support and keep the binary small.

@sezero

sezero commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Can I push to your branch?

@sezero

sezero commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Can I push to your branch?

OK here is a clean-up patch: smaflib.patch (Not pushing, just in case.) (Also inlined below.)

Details
build clean-ups. guard mafm/*.c with config. fix build in c++ mode.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4e6d794..6788b67 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -211,7 +211,7 @@ check_symbol_exists(sqrtf math.h HAVE_SQRTF)
 cmake_pop_check_state()
 
 # ######### General setup ##########
-INCLUDE_DIRECTORIES(BEFORE "${PROJECT_SOURCE_DIR}/include" "${PROJECT_BINARY_DIR}/include" "${PROJECT_SOURCE_DIR}/src/mafm")
+INCLUDE_DIRECTORIES(BEFORE "${PROJECT_SOURCE_DIR}/include" "${PROJECT_BINARY_DIR}/include")
 IF (NOT HAVE_STDINT_H) # AND NOT HAVE_INTTYPES_H
     INCLUDE_DIRECTORIES(BEFORE "${PROJECT_SOURCE_DIR}/include/stdint")
 ENDIF()
diff --git a/amiga/Makefile b/amiga/Makefile
index e65f59f..380924a 100644
--- a/amiga/Makefile
+++ b/amiga/Makefile
@@ -21,7 +21,7 @@ AR=$(CROSS)-ar
 RANLIB=$(CROSS)-ranlib
 endif
 
-INCLUDE = -I. -I../include -I../src/mafm
+INCLUDE = -I. -I../include
 CFLAGS = $(INCLUDE) -Wall -W -fno-common
 ARFLAGS = cr
 LD = $(CC)
diff --git a/amiga/Makefile.vbcc b/amiga/Makefile.vbcc
index ec5869d..912cf68 100644
--- a/amiga/Makefile.vbcc
+++ b/amiga/Makefile.vbcc
@@ -10,7 +10,7 @@ CC=vc
 #MKLIB=join $(LIB_OBJ) as WildMidi.lib
 MKLIB=cat $(LIB_OBJ) > WildMidi.lib
 
-INCLUDE = -I. -I../include -I../src/mafm
+INCLUDE = -I. -I../include
 CFLAGS  = -O1 -speed -c99
 CFLAGS += $(INCLUDE)
 LDFLAGS = -L. -lWildMidi
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index b78e31a..9784045 100644
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -2,7 +2,7 @@ LOCAL_PATH := $(call my-dir)/../..
 include $(CLEAR_VARS)
 
 LOCAL_MODULE     := WildMidi
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/android/jni $(LOCAL_PATH)/include $(LOCAL_PATH)/src/mafm
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/android/jni $(LOCAL_PATH)/include
 LOCAL_ARM_MODE   := arm
 LOCAL_CFLAGS     += -DWILDMIDI_BUILD
 LOCAL_CFLAGS     += -fvisibility=hidden -DSYM_VISIBILITY
diff --git a/build.zig b/build.zig
index a58c796..28d29e1 100644
--- a/build.zig
+++ b/build.zig
@@ -11,7 +11,6 @@ pub fn build(b: *std.Build) void {
     });
 
     lib_mod.addIncludePath(b.path("include"));
-    lib_mod.addIncludePath(b.path("src/mafm"));
 
     const lib = b.addLibrary(.{
         .name = "wildmidi",
diff --git a/djgpp/Makefile b/djgpp/Makefile
index 09237ca..4bf7324 100644
--- a/djgpp/Makefile
+++ b/djgpp/Makefile
@@ -25,7 +25,7 @@ RANLIB=$(CROSS)-ranlib
 endif
 DXE3GEN=dxe3gen
 
-INCLUDE = -I. -I../include/stdint -I../include -I../src/mafm
+INCLUDE = -I. -I../include/stdint -I../include
 CFLAGS = $(INCLUDE) -Wall -W -fno-common
 ARFLAGS = cr
 
diff --git a/macosx/Makefile b/macosx/Makefile
index 6707f88..7a342c3 100644
--- a/macosx/Makefile
+++ b/macosx/Makefile
@@ -32,7 +32,7 @@ LIB_INSTALLNAME=$(LIBSHARED_MAJ)
 PLAYER=wildmidi
 PLAYER_STATIC=wildmidi-static
 
-INCLUDE=-I. -I../include -I../src/mafm
+INCLUDE=-I. -I../include
 ARFLAGS=cr
 CFLAGS=$(INCLUDE) -Wall -W -fno-common
 LDFLAGS=
diff --git a/mingw/Makefile b/mingw/Makefile
index e38a38c..b6b1669 100644
--- a/mingw/Makefile
+++ b/mingw/Makefile
@@ -25,7 +25,7 @@ IMPLIB=lib$(LIBNAME).dll.a
 PLAYER=wildmidi.exe
 PLAYER_STATIC=wildmidi-static.exe
 
-INCLUDE=-I. -I../include -I../src/mafm
+INCLUDE=-I. -I../include
 ARFLAGS=cr
 CFLAGS=$(INCLUDE) -Wall -W -fno-common
 LDFLAGS=-static-libgcc
diff --git a/msvc/Makefile.vs b/msvc/Makefile.vs
index f0d9ef5..751bc79 100644
--- a/msvc/Makefile.vs
+++ b/msvc/Makefile.vs
@@ -1,7 +1,7 @@
 # Makefile for Win32 using Visual Studio 2010 / newer:
 #	nmake -f Makefile.vs
 
-INCLUDES  = -I. -I..\include -I..\src\mafm
+INCLUDES  = -I. -I..\include
 CPPFLAGS  = -DNDEBUG -D_CRT_SECURE_NO_WARNINGS
 
 CFLAGS    = -nologo /O2 /MD /W3
diff --git a/msvc/Makefile.vs6 b/msvc/Makefile.vs6
index 42f7ed3..478b321 100644
--- a/msvc/Makefile.vs6
+++ b/msvc/Makefile.vs6
@@ -1,7 +1,7 @@
 # Makefile for Win32 using Visual Studio 6:
 #	nmake -f Makefile.vs6
 
-INCLUDES  = -I. -I..\include -I..\include\stdint -I..\src\mafm
+INCLUDES  = -I. -I..\include -I..\include\stdint
 CPPFLAGS  = -DNDEBUG
 
 CFLAGS    = -nologo /O2 /MD /W3 /G6
diff --git a/msvc/Makefile.vs8 b/msvc/Makefile.vs8
index bd40d36..d3f5869 100644
--- a/msvc/Makefile.vs8
+++ b/msvc/Makefile.vs8
@@ -1,7 +1,7 @@
 # Makefile for Win32 using Visual Studio 2005/2008:
 #	nmake -f Makefile.vs8
 
-INCLUDES  = -I. -I..\include -I..\include\stdint -I..\src\mafm
+INCLUDES  = -I. -I..\include -I..\include\stdint
 CPPFLAGS  = -DNDEBUG -D_CRT_SECURE_NO_WARNINGS
 
 CFLAGS    = -nologo /O2 /MD /W3
diff --git a/os2/makefile.emx b/os2/makefile.emx
index 4888caa..e6462ea 100644
--- a/os2/makefile.emx
+++ b/os2/makefile.emx
@@ -17,7 +17,7 @@ EMXOMF=emxomf
 endif
 LD=$(CC)
 
-INCLUDES=-I. -I../include -I../src/mafm
+INCLUDES=-I. -I../include
 CFLAGS = $(INCLUDES) -Wall -W -Zmt
 LDFLAGS = -Zomf -Zmt
 LDFLAGS_DLL = -Zdll -Zomf -Zmt
diff --git a/os2/makefile.wat b/os2/makefile.wat
index 5c8ef87..64dbcd1 100644
--- a/os2/makefile.wat
+++ b/os2/makefile.wat
@@ -36,7 +36,7 @@ CFLAGS_LIB+= $(DLLFLAGS)
 BLD_TARGET=$(DLLNAME) $(PLAYER)
 !endif
 INCPATH=-I"$(%WATCOM)/h/os2" -I"$(%WATCOM)/h"
-INCLUDES=$(INCPATH) -I. -I"../include" -I"../src/mafm"
+INCLUDES=$(INCPATH) -I. -I"../include"
 
 OBJ=wm_error.obj file_io.obj lock.obj wildmidi_lib.obj reverb.obj gus_pat.obj f_xmidi.obj f_mus.obj f_hmp.obj f_midi.obj f_hmi.obj f_smaf.obj mus2mid.obj xmi2mid.obj hmp2mid.obj hmi2mid.obj smaf2mid.obj internal_midi.obj patches.obj sample.obj sf2.obj mafm.obj ma_fm_core.obj smaf_voice.obj yamaha_adpcm.obj synth.obj opl3.obj
 PLAYER_OBJ=wm_tty.obj msleep.obj getopt_long.obj out_none.obj out_wave.obj out_dart.obj wildmidi.obj
diff --git a/src/mafm.c b/src/mafm.c
index 2ba2972..cfd35f9 100644
--- a/src/mafm.c
+++ b/src/mafm.c
@@ -43,9 +43,9 @@ typedef char mafm_char20[20]; /* no empty source. */
 #include "wildmidi_lib.h"
 #include "internal_midi.h"
 #include "mafm.h"
-#include "ma_fm_core.h"
-#include "smaf_voice.h"
-#include "yamaha_adpcm.h"
+#include "mafm/ma_fm_core.h"
+#include "mafm/smaf_voice.h"
+#include "mafm/yamaha_adpcm.h"
 
 /* big-endian 32-bit read */
 #define MAFM_BE32(p) (((uint32_t)(p)[0] << 24) | ((uint32_t)(p)[1] << 16) | \
diff --git a/src/mafm/ma_fm_core.c b/src/mafm/ma_fm_core.c
index d55eb95..789447a 100644
--- a/src/mafm/ma_fm_core.c
+++ b/src/mafm/ma_fm_core.c
@@ -15,6 +15,14 @@
  * curve, not in the mantissa.
  */
 
+#include "config.h"
+
+#ifndef WILDMIDI_MAFM
+
+typedef char mafm_core20[20]; /* no empty src. */
+
+#else
+
 #include "ma_fm_core.h"
 
 #include <math.h>
@@ -424,3 +432,4 @@ float _WM_MAFM_VoiceTick(struct mafm_voice *v) {
 
     return (float)(out * v->velocity * v->volume * 0.7);
 }
+#endif /* WILDMIDI_MAFM */
diff --git a/src/mafm/smaf_voice.c b/src/mafm/smaf_voice.c
index aeb5622..636f8e5 100644
--- a/src/mafm/smaf_voice.c
+++ b/src/mafm/smaf_voice.c
@@ -15,6 +15,14 @@
  * into the same struct.
  */
 
+#include "config.h"
+
+#ifndef WILDMIDI_MAFM
+
+typedef char smaf_voice20[20]; /* no empty src. */
+
+#else
+
 #include "smaf_voice.h"
 
 #include <string.h>
@@ -255,3 +263,4 @@ void _WM_MAFM_ParseVoiceExclusive(const uint8_t *p, uint32_t n,
 
     /* unknown Yamaha sub-form: leave out->valid = 0 */
 }
+#endif /* WILDMIDI_MAFM */
diff --git a/src/mafm/yamaha_adpcm.c b/src/mafm/yamaha_adpcm.c
index ffead6d..2589b85 100644
--- a/src/mafm/yamaha_adpcm.c
+++ b/src/mafm/yamaha_adpcm.c
@@ -15,6 +15,14 @@
  * (high-nibble-first is the Yamaha-standard default).
  */
 
+#include "config.h"
+
+#ifndef WILDMIDI_MAFM
+
+typedef char yamaha_adpcm20[20]; /* no empty src. */
+
+#else
+
 #include "yamaha_adpcm.h"
 
 static int adpcm_clamp16(int v) {
@@ -69,3 +77,5 @@ uint32_t _WM_MAFM_AdpcmDecodeAll(const uint8_t *data, uint32_t n,
     }
     return w;
 }
+
+#endif /* WILDMIDI_MAFM */
diff --git a/src/smaf2mid.c b/src/smaf2mid.c
index 644f23b..7d90280 100644
--- a/src/smaf2mid.c
+++ b/src/smaf2mid.c
@@ -28,6 +28,7 @@
  * See docs/formats/SmafFileFormat.txt for the format description.
  */
 
+#define __STDC_LIMIT_MACROS
 #include "config.h"
 
 #include <stddef.h>
diff --git a/src/wildmidi_lib.c b/src/wildmidi_lib.c
index faebcd4..cb6204a 100644
--- a/src/wildmidi_lib.c
+++ b/src/wildmidi_lib.c
@@ -22,6 +22,7 @@
  */
 
 #define _WILDMIDI_LIB_C
+#define __STDC_LIMIT_MACROS
 
 #include "config.h"
 
diff --git a/win32wat/makefile b/win32wat/makefile
index a892ee1..2a33c63 100644
--- a/win32wat/makefile
+++ b/win32wat/makefile
@@ -38,7 +38,7 @@ CFLAGS_LIB+= $(DLLFLAGS)
 BLD_TARGET=$(DLLNAME) $(PLAYER)
 !endif
 INCPATH=-I"$(%WATCOM)/h/nt" -I"$(%WATCOM)/h"
-INCLUDES=$(INCPATH) -I. -I"../include" -I"../src/mafm"
+INCLUDES=$(INCPATH) -I. -I"../include"
 
 OBJ=wm_error.obj file_io.obj lock.obj wildmidi_lib.obj reverb.obj gus_pat.obj f_xmidi.obj f_mus.obj f_hmp.obj f_midi.obj f_hmi.obj f_smaf.obj mus2mid.obj xmi2mid.obj hmp2mid.obj hmi2mid.obj smaf2mid.obj internal_midi.obj patches.obj sample.obj sf2.obj mafm.obj ma_fm_core.obj smaf_voice.obj yamaha_adpcm.obj synth.obj opl3.obj
 PLAYER_OBJ=wm_tty.obj msleep.obj getopt_long.obj out_none.obj out_wave.obj out_openal.obj out_win32mm.obj wildmidi.obj

@sezero

sezero commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

@psi29a : If you allow me, I'll do some butchering: (i) Will push the -L patch directly to master with reference to issue/215, and (ii) will apply my patch and rebase to master and squash all commits into a sing one for one final review.

@psi29a

psi29a commented Jul 25, 2026

Copy link
Copy Markdown
Member Author

@psi29a : If you allow me, I'll do some butchering: (i) Will push the -L patch directly to master with reference to issue/215, and (ii) will apply my patch and rebase to master and squash all commits into a sing one for one final review.

Yeah, I'm "done" for now. :)

@sezero

sezero commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Done: all squashed and the -L patch pushed to master separately.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/smaf2mid.c (1)

836-848: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

ntracks == 0 produces a valid-but-empty MIDI reported as success.

If no HandyPhone Mtsq is found (e.g. every MTR* was malformed), the output is just tempo + EOT, so playback is silent with no diagnostic. Declining with WM_ERR_NOT_SMAF "(no score track)" would match find_sequence's behaviour for the Mobile path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/smaf2mid.c` around lines 836 - 848, The HandyPhone conversion path around
decode_all_handyphone must reject ntracks == 0 instead of emitting an empty MIDI
and succeeding. Before hp_emit, report WM_ERR_NOT_SMAF with the “no score track”
diagnostic and jump to _end, while preserving the existing memory-error handling
for negative results and normal emission for positive track counts.
docs/SMAF_FM.md (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Title still says “(Design)” though the status is now IMPLEMENTED. Consider dropping the suffix for consistency with line 3.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/SMAF_FM.md` at line 1, Update the document title heading to remove the
“(Design)” suffix so it matches the IMPLEMENTED status shown on line 3.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@build.zig`:
- Around line 39-42: Update the Zig build configuration to add a MAFM
enable/disable option, conditionally include the MAFM source files and set
WILDMIDI_MAFM only when enabled, and preserve builds without MAFM for
conversion-only use. Use the existing build-option/configuration setup in
build.zig rather than unconditionally compiling these sources or defining the
macro.

In `@docs/SMAF_FM.md`:
- Around line 142-147: Update the byte-layout literal “[formatByte][fmt2]” in
the M1 ADPCM codec documentation to be enclosed in backticks, and apply the same
Markdown escaping to the matching pattern near the later referenced section.
Preserve the surrounding wording and formatting.

In `@src/smaf2mid.c`:
- Around line 88-92: Add a sticky out-of-memory flag to struct smaf_ctx, set it
whenever resize_dst() fails in write1 and the other write* helpers, and make
_WM_smaf2mid return WM_ERR_MEM before transferring the output buffer when the
flag is set. Ensure subsequent writes remain harmless while the conversion
reports failure instead of success.

---

Nitpick comments:
In `@docs/SMAF_FM.md`:
- Line 1: Update the document title heading to remove the “(Design)” suffix so
it matches the IMPLEMENTED status shown on line 3.

In `@src/smaf2mid.c`:
- Around line 836-848: The HandyPhone conversion path around
decode_all_handyphone must reject ntracks == 0 instead of emitting an empty MIDI
and succeeding. Before hp_emit, report WM_ERR_NOT_SMAF with the “no score track”
diagnostic and jump to _end, while preserving the existing memory-error handling
for negative results and normal emission for positive track counts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 36a60c9e-9cba-4a76-bb66-b1b59ff17fbb

📥 Commits

Reviewing files that changed from the base of the PR and between 0cb2d86 and db9b21c.

📒 Files selected for processing (43)
  • CMakeLists.txt
  • NOTICE
  • amiga/Makefile
  • amiga/Makefile.vbcc
  • amiga/config.h
  • android/jni/Android.mk
  • android/jni/config.h
  • build.zig
  • djgpp/Makefile
  • djgpp/config.h
  • docs/SMAF_FM.md
  • docs/formats/SmafFileFormat.txt
  • include/config.h.cmake
  • include/f_smaf.h
  • include/internal_midi.h
  • include/mafm.h
  • include/smaf2mid.h
  • include/wm_error.h
  • macosx/Makefile
  • macosx/config.h
  • mingw/Makefile
  • mingw/config.h
  • msvc/common.mak
  • msvc/config.h
  • os2/config.h
  • os2/makefile.emx
  • os2/makefile.wat
  • src/CMakeLists.txt
  • src/f_smaf.c
  • src/internal_midi.c
  • src/mafm.c
  • src/mafm/ma_fm_core.c
  • src/mafm/ma_fm_core.h
  • src/mafm/smaf_voice.c
  • src/mafm/smaf_voice.h
  • src/mafm/yamaha_adpcm.c
  • src/mafm/yamaha_adpcm.h
  • src/root.zig
  • src/smaf2mid.c
  • src/wildmidi_lib.c
  • src/wm_error.c
  • win32wat/config.h
  • win32wat/makefile
🚧 Files skipped from review as they are similar to previous changes (31)
  • include/f_smaf.h
  • mingw/config.h
  • android/jni/config.h
  • include/smaf2mid.h
  • macosx/config.h
  • djgpp/config.h
  • NOTICE
  • os2/makefile.wat
  • os2/config.h
  • amiga/config.h
  • win32wat/config.h
  • include/wm_error.h
  • src/root.zig
  • msvc/config.h
  • include/mafm.h
  • src/mafm/smaf_voice.h
  • include/config.h.cmake
  • android/jni/Android.mk
  • src/f_smaf.c
  • msvc/common.mak
  • src/internal_midi.c
  • src/CMakeLists.txt
  • src/mafm/yamaha_adpcm.h
  • src/wm_error.c
  • include/internal_midi.h
  • src/mafm/ma_fm_core.h
  • src/mafm/yamaha_adpcm.c
  • docs/formats/SmafFileFormat.txt
  • src/mafm/smaf_voice.c
  • src/mafm.c
  • src/mafm/ma_fm_core.c

Comment thread build.zig
Comment thread docs/SMAF_FM.md
Comment thread src/smaf2mid.c
@sezero

sezero commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Among the test files you provided abıove, three are rejected: Errors as printed by player:
SILENT.MMF -> no score track
AB00221GM7.MMF, AB03957GM7.MMF -> unsupported SMAF track format

@sezero

sezero commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

The coderabbit reviews are annoying, even though some are legit.

And, no, I haven't read or evaluated the latest ones here...

@sezero

sezero commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

P.S.: Is the smaf branch still relevant?

@psi29a

psi29a commented Jul 25, 2026

Copy link
Copy Markdown
Member Author

P.S.: Is the smaf branch still relevant?

Not really, it was my attempt to write a parser from scratch that purely did midi conversion. But the MMF/SMAF have samples in them like trackers do. Sooooo.... I mean, if we just want to dump to midi it was nice, but this new one also does that.

@psi29a

psi29a commented Jul 25, 2026

Copy link
Copy Markdown
Member Author

Among the test files you provided abıove, three are rejected: Errors as printed by player: SILENT.MMF -> no score track AB00221GM7.MMF, AB03957GM7.MMF -> unsupported SMAF track format

Yes, those are not supported because they are MA-7 files. Compressed / proprietary yamaha formart. I would really need to reverse engineer it. So I'll leave it for another PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/smaf2mid.c (1)

22-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Header comment is stale. HandyPhone (0x00) is now decoded too, and Mtsu/ATR data is consumed by the MAFM path rather than universally ignored.

📝 Suggested wording
- * Converts Yamaha SMAF ("MMF") Mobile Standard score tracks into a Standard
- * MIDI File.  Only the score track SEQUENCE (Mtsq) is converted; embedded
- * PCM/ADPCM audio (Mtsp/ATR) and custom FM voice banks (Mtsu) are ignored, so
- * playback falls back to the General MIDI patch set.
+ * Converts Yamaha SMAF ("MMF") HandyPhone Standard (format 0x00) and Mobile
+ * Standard (0x01/0x02) score tracks into a Standard MIDI File.  Only the score
+ * track SEQUENCE (Mtsq) is converted here; embedded PCM/ADPCM audio (Mtsp/ATR)
+ * and custom FM voice banks (Mtsu) are handled separately by the MAFM path
+ * (src/mafm.c) and otherwise fall back to the General MIDI patch set.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/smaf2mid.c` around lines 22 - 29, Update the file header comment to
reflect current behavior: mention that HandyPhone (0x00) is decoded, and remove
the claim that Mtsu/ATR data is universally ignored, noting its consumption by
the MAFM path instead. Keep the existing description of score-track conversion
and the format documentation reference.
src/wildmidi_lib.c (1)

2405-2534: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

WM_GetOutput_MAFM is structurally sound but is now the 4th near-duplicate of the mix-buffer growth/overflow-guard logic in this file.

The event loop, tail-ring-out (_WM_MAFM_ActiveVoices), render, clamp, and pack stages all correctly mirror WM_GetOutput_SF2 — no functional issues found. However, the buffer-growth block (lines 2425-2442) is now byte-for-byte identical to the same block in WM_GetOutput_Linear (963-969), WM_GetOutput_Gauss (1303-1309), and WM_GetOutput_SF2 (2286-2292). Extracting a small grow_mix_buffer(struct _mdi *mdi, uint32_t needed) helper would collapse all 4 copies into one, so any future fix to this logic (like the overflow guard itself) only needs to land once.

♻️ Suggested helper
static int grow_mix_buffer(struct _mdi *mdi, uint32_t size) {
    if ((size / 2) > mdi->mix_buffer_size) {
        uint32_t new_size = ((size / 2) <= (mdi->mix_buffer_size * 2))
            ? mdi->mix_buffer_size + MEM_CHUNK : size / 2;
        int32_t *new_buf;
        if (new_size > (UINT32_MAX / sizeof(int32_t))) {
            _WM_GLOBAL_ERROR(WM_ERR_MEM, NULL, 0);
            return -1;
        }
        new_buf = (int32_t *) realloc(mdi->mix_buffer, new_size * sizeof(int32_t));
        if (new_buf == NULL) {
            _WM_GLOBAL_ERROR(WM_ERR_MEM, NULL, errno);
            return -1;
        }
        mdi->mix_buffer = new_buf;
        mdi->mix_buffer_size = new_size;
    }
    return 0;
}

Each call site then becomes if (grow_mix_buffer(mdi, size) < 0) { _WM_Unlock(&mdi->lock); return -1; }.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/wildmidi_lib.c` around lines 2405 - 2534, Extract the duplicated
mix-buffer growth and overflow-check block into a shared grow_mix_buffer helper
near the output routines. Update WM_GetOutput_Linear, WM_GetOutput_Gauss,
WM_GetOutput_SF2, and WM_GetOutput_MAFM to call it, preserving their existing
lock-unlock and error-return behavior when growth fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/smaf2mid.c`:
- Around line 174-182: Update the shared write_event helper to prevent unsigned
underflow when at_ms precedes ctx->last_event_ms: clamp the event time or delta
so backward timestamps produce no large delay. Preserve normal delta calculation
and last_event_ms updates for non-wrapped timestamps, covering both decoder
paths that call write_event.
- Around line 661-682: Update the MIDI channel mapping for melodic channels in
the note handling branch and the long-control path near line 613 so it skips
reserved MIDI channel 9, while keeping percussion explicitly routed to channel
9. Reuse one consistent mapping approach for both paths, preserving the existing
base-channel behavior and ensuring melodic channels never collide with the
percussion destination.

---

Nitpick comments:
In `@src/smaf2mid.c`:
- Around line 22-29: Update the file header comment to reflect current behavior:
mention that HandyPhone (0x00) is decoded, and remove the claim that Mtsu/ATR
data is universally ignored, noting its consumption by the MAFM path instead.
Keep the existing description of score-track conversion and the format
documentation reference.

In `@src/wildmidi_lib.c`:
- Around line 2405-2534: Extract the duplicated mix-buffer growth and
overflow-check block into a shared grow_mix_buffer helper near the output
routines. Update WM_GetOutput_Linear, WM_GetOutput_Gauss, WM_GetOutput_SF2, and
WM_GetOutput_MAFM to call it, preserving their existing lock-unlock and
error-return behavior when growth fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6bfa80aa-e33c-4c41-ab06-a2a86cf80c2a

📥 Commits

Reviewing files that changed from the base of the PR and between db9b21c and a860fbe.

📒 Files selected for processing (43)
  • CMakeLists.txt
  • NOTICE
  • amiga/Makefile
  • amiga/Makefile.vbcc
  • amiga/config.h
  • android/jni/Android.mk
  • android/jni/config.h
  • build.zig
  • djgpp/Makefile
  • djgpp/config.h
  • docs/SMAF_FM.md
  • docs/formats/SmafFileFormat.txt
  • include/config.h.cmake
  • include/f_smaf.h
  • include/internal_midi.h
  • include/mafm.h
  • include/smaf2mid.h
  • include/wm_error.h
  • macosx/Makefile
  • macosx/config.h
  • mingw/Makefile
  • mingw/config.h
  • msvc/common.mak
  • msvc/config.h
  • os2/config.h
  • os2/makefile.emx
  • os2/makefile.wat
  • src/CMakeLists.txt
  • src/f_smaf.c
  • src/internal_midi.c
  • src/mafm.c
  • src/mafm/ma_fm_core.c
  • src/mafm/ma_fm_core.h
  • src/mafm/smaf_voice.c
  • src/mafm/smaf_voice.h
  • src/mafm/yamaha_adpcm.c
  • src/mafm/yamaha_adpcm.h
  • src/root.zig
  • src/smaf2mid.c
  • src/wildmidi_lib.c
  • src/wm_error.c
  • win32wat/config.h
  • win32wat/makefile
🚧 Files skipped from review as they are similar to previous changes (35)
  • djgpp/config.h
  • macosx/config.h
  • msvc/config.h
  • include/f_smaf.h
  • amiga/config.h
  • os2/config.h
  • android/jni/config.h
  • win32wat/config.h
  • mingw/config.h
  • src/mafm/smaf_voice.h
  • src/mafm/yamaha_adpcm.h
  • os2/makefile.emx
  • os2/makefile.wat
  • NOTICE
  • include/smaf2mid.h
  • CMakeLists.txt
  • src/root.zig
  • src/CMakeLists.txt
  • build.zig
  • include/config.h.cmake
  • msvc/common.mak
  • include/mafm.h
  • android/jni/Android.mk
  • src/f_smaf.c
  • src/wm_error.c
  • amiga/Makefile.vbcc
  • src/mafm/yamaha_adpcm.c
  • src/internal_midi.c
  • win32wat/makefile
  • docs/SMAF_FM.md
  • src/mafm/ma_fm_core.h
  • src/mafm.c
  • docs/formats/SmafFileFormat.txt
  • src/mafm/smaf_voice.c
  • src/mafm/ma_fm_core.c

Comment thread src/smaf2mid.c
Comment thread src/smaf2mid.c

@sezero sezero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be good. Should we squash?

@psi29a

psi29a commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

This should be good. Should we squash?

I want the shuffle bits to land first. Then we can merge this if there are no conflicts.

@psi29a

psi29a commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

I'm going to rebase this on master, fix conflicts, tidy up changelog

psi29a added 3 commits July 26, 2026 15:10
Decode the HandyPhone Standard score format (format_type 0x00, MA-1/MA-2)
in smaf2mid.c, merging its multi-track streams to MIDI.

Add an optional FM synthesis engine (WANT_MAFM) so SMAF files play with
their real Yamaha voices instead of the GM approximation: port the FM core,
voice-exclusive decoder, and ADPCM codec to C from the (Apache-2.0)
akustikrausch/yamaha-smaf-player, wrapped in src/mafm.c and wired into the
render loop like the SF2 path (per-mdi mafm_synth). Embedded ATR/Awa ADPCM
samples play as scheduled drums alongside the FM voices.

Also document the MFi (.mld) format and update SmafFileFormat.txt.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/smaf2mid.c (1)

133-138: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

seekdst() sets dst_ptr before guaranteeing capacity.

ctx->dst_ptr = ctx->dst + pos is computed against the old buffer, then resize_dst() reallocs (moving ctx->dst) and recomputes pos from the stale pointer difference — it happens to work today only because every call site passes a pos already within dstsize. Reordering (grow first, then set the pointer) makes it robust.

♻️ Suggested reorder
 static void seekdst(struct smaf_ctx *ctx, uint32_t pos) {
-    ctx->dst_ptr = ctx->dst + pos;
     while (ctx->dstsize < pos)
         if (resize_dst(ctx)) { ctx->oom = 1; return; }
+    ctx->dst_ptr = ctx->dst + pos;
     ctx->dstrem = ctx->dstsize - pos;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/smaf2mid.c` around lines 133 - 138, Update seekdst() to ensure the
destination buffer has capacity for pos before assigning ctx->dst_ptr. Perform
the resize loop first, preserve the OOM return behavior, then compute
ctx->dst_ptr from the potentially reallocated ctx->dst and set ctx->dstrem.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/man/man3/WildMidi_OpenBuffer.3`:
- Line 17: Add MUS to the documented input-format list for WildMidi_OpenBuffer
in docs/man/man3/WildMidi_OpenBuffer.3 at lines 17-17, and make the same
documentation update for WildMidi_Open in docs/man/man3/WildMidi_Open.3 at lines
13-13.

---

Nitpick comments:
In `@src/smaf2mid.c`:
- Around line 133-138: Update seekdst() to ensure the destination buffer has
capacity for pos before assigning ctx->dst_ptr. Perform the resize loop first,
preserve the OOM return behavior, then compute ctx->dst_ptr from the potentially
reallocated ctx->dst and set ctx->dstrem.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 807da926-41fe-44a7-8638-d4323869b0f9

📥 Commits

Reviewing files that changed from the base of the PR and between 57b1370 and 9a99188.

📒 Files selected for processing (49)
  • CMakeLists.txt
  • NOTICE
  • README.md
  • amiga/Makefile
  • amiga/Makefile.vbcc
  • amiga/config.h
  • android/jni/Android.mk
  • android/jni/config.h
  • build.zig
  • djgpp/Makefile
  • djgpp/config.h
  • docs/SMAF_FM.md
  • docs/formats/SmafFileFormat.txt
  • docs/man/man1/wildmidi.1
  • docs/man/man3/WildMidi_ConvertToMidi.3
  • docs/man/man3/WildMidi_Open.3
  • docs/man/man3/WildMidi_OpenBuffer.3
  • include/config.h.cmake
  • include/f_smaf.h
  • include/internal_midi.h
  • include/mafm.h
  • include/smaf2mid.h
  • include/wm_error.h
  • macosx/Makefile
  • macosx/config.h
  • mingw/Makefile
  • mingw/config.h
  • msvc/common.mak
  • msvc/config.h
  • os2/config.h
  • os2/makefile.emx
  • os2/makefile.wat
  • src/CMakeLists.txt
  • src/f_smaf.c
  • src/internal_midi.c
  • src/mafm.c
  • src/mafm/ma_fm_core.c
  • src/mafm/ma_fm_core.h
  • src/mafm/smaf_voice.c
  • src/mafm/smaf_voice.h
  • src/mafm/yamaha_adpcm.c
  • src/mafm/yamaha_adpcm.h
  • src/player/playlist.c
  • src/root.zig
  • src/smaf2mid.c
  • src/wildmidi_lib.c
  • src/wm_error.c
  • win32wat/config.h
  • win32wat/makefile
🚧 Files skipped from review as they are similar to previous changes (34)
  • android/jni/config.h
  • win32wat/config.h
  • macosx/config.h
  • src/root.zig
  • os2/makefile.emx
  • os2/config.h
  • include/config.h.cmake
  • msvc/config.h
  • include/f_smaf.h
  • include/smaf2mid.h
  • djgpp/config.h
  • CMakeLists.txt
  • amiga/config.h
  • os2/makefile.wat
  • src/mafm/yamaha_adpcm.h
  • NOTICE
  • include/internal_midi.h
  • src/mafm/smaf_voice.h
  • mingw/config.h
  • amiga/Makefile.vbcc
  • src/wm_error.c
  • build.zig
  • msvc/common.mak
  • src/CMakeLists.txt
  • docs/formats/SmafFileFormat.txt
  • src/mafm/ma_fm_core.h
  • win32wat/makefile
  • android/jni/Android.mk
  • include/mafm.h
  • src/mafm/smaf_voice.c
  • docs/SMAF_FM.md
  • src/mafm/ma_fm_core.c
  • src/mafm.c
  • src/mafm/yamaha_adpcm.c

Comment thread docs/man/man3/WildMidi_OpenBuffer.3 Outdated
@psi29a
psi29a merged commit 50cd02f into master Jul 26, 2026
23 checks passed
@psi29a psi29a mentioned this pull request Jul 26, 2026
@sezero
sezero deleted the smaflib branch July 26, 2026 14:34
@sezero

sezero commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Do we need the old smaf branch or can we delete it?

@sezero

sezero commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

P.S.: Is the smaf branch still relevant?

Not really, it was my attempt to write a parser from scratch that purely did midi conversion. But the MMF/SMAF have samples in them like trackers do. Sooooo.... I mean, if we just want to dump to midi it was nice, but this new one also does that.

Diffed smaf2mid between old smaf branch and master, they are practically the same but latter being improved. Deleted the old smaf branch.

@psi29a

psi29a commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

Looking into ma-7 now... going to be awhile, very little documentation online. The only player that supports it is Yamaha's official app.

This was referenced Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants