Skip to content

[.NET] TinySoundFont ChannelNoteOn/ChannelNoteOff throw NullReferenceException during synthesis (1.8.4, 1.9.0-alpha.1891) #2857

Description

@Gungr

Summary

In the C# port (AlphaTab NuGet 1.8.4, also present in 1.9.0-alpha.1891), TinySoundFont.ChannelNoteOn / ChannelNoteOff throw System.NullReferenceException when a MIDI file is actually synthesized — both via the audio exporter (AlphaSynth.ExportAudioIAlphaSynthAudioExporter.Render) and via the realtime AlphaSynth.Play() path. The TypeScript/JS engine is unaffected.

Repro (minimal, .NET 8, Windows)

using AlphaTab;
using AlphaTab.Core.EcmaScript;
using AlphaTab.Midi;
using AlphaTab.Synth;

var sf2 = System.IO.File.ReadAllBytes("timgm6mb.sf2"); // any valid SoundFont
var synth = new AlphaSynth(new NAudioSynthOutput(), 50);
var ready = new System.Threading.ManualResetEventSlim();
synth.Ready.On(() => ready.Set());
synth.LoadSoundFont(new Uint8Array(sf2), false);
ready.Wait(TimeSpan.FromSeconds(20));

var file = new MidiFile { Format = MidiFileFormat.MultiTrack, Division = 960 };
var track = new MidiTrack();
track.AddEvent(new ProgramChangeEvent(0, 0, 0, 24));
track.AddEvent(new TempoChangeEvent(0, 500000));
track.AddEvent(new NoteOnEvent(0, 0, 0, 60, 100));
track.AddEvent(new NoteOffEvent(0, 960, 0, 60, 64));
file.Tracks.Add(track);

var options = new AudioExportOptions { SampleRate = 44100, MasterVolume = 1.0 };
var exporter = (AlphaSynthAudioExporter)synth.ExportAudio(options, file, null, null);
var chunk = exporter.Render(250); // -> NullReferenceException

Observed:

System.NullReferenceException: Object reference not set to an instance of an object.
   at AlphaTab.Synth.Synthesis.TinySoundFont.ChannelNoteOn(Double channel, Double key, Double vel)

Narrowing (decompiled IL, AlphaTab.dll 1.8.4)

  1. TinySoundFont's constructor initializes _midiEventQueue, _mutedChannels, _soloChannels, _transpositionPitches, _liveTranspositionPitches, _metronomeChannel, and _voices = new List<Voice>() — but not _channels (matches the TS, where _channels is lazily created).
  2. LoadSoundFont(Uint8Array) calls Reset() first, and Reset() sets _channels = null (TS equivalent keeps _channels and only stops voices — please verify the translation).
  3. _channelInit(channel) recreates _channels lazily and its grow-loop matches the TS (i <= channel, inclusive), ending in return _channels.ChannelList[channel];.
  4. Calling the font directly (via reflection) after LoadSoundFont:
    • ChannelSetPresetIndex(0, 24) → OK
    • ChannelNoteOn(0, 60, 100)NRE (note: direct preset-level NoteOn(24, 60, 100) succeeds, which suggests the crash is in code inlined into ChannelNoteOn — e.g. inside NoteOn or the channel lookup)
    • ChannelNoteOff(0, 60)NRE (this method iterates _voices; in the observed instance _channels held 17 fully-constructed Channel entries, so the null deref is elsewhere)
    • ChannelGetPresetIndex(0) → OK

Suggested first look: the state of _channels/_voices across the LoadSoundFontReset()LoadPresets() sequence, and the NoteOn body that channelNoteOn inlines (e.g. preset lookup / voices.Add), since the TS originals of all these are null-safe and work in the JS engine.

Impact

Any C# consumer trying to synthesize audio (realtime playback or audio export) of a program-change + note-on MIDI file crashes. The audio features of the C# port are currently unusable with a loaded SoundFont.

Environment

  • AlphaTab 1.8.4 (also reproduced on 1.9.0-alpha.1891), AlphaTab.Windows 1.8.4, .NET 8, Windows 10/11
  • SoundFont: TimGM6mb (public-domain-ish GM set, 136 presets) — issue is not font-specific (reproduced with other GM fonts as well)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    Priority

    None yet

    Area

    None yet

    Platform

    None yet

    Work State

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions