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.ExportAudio → IAlphaSynthAudioExporter.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)
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).
LoadSoundFont(Uint8Array) calls Reset() first, and Reset() sets _channels = null (TS equivalent keeps _channels and only stops voices — please verify the translation).
_channelInit(channel) recreates _channels lazily and its grow-loop matches the TS (i <= channel, inclusive), ending in return _channels.ChannelList[channel];.
- 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 LoadSoundFont → Reset() → 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)
Summary
In the C# port (
AlphaTabNuGet 1.8.4, also present in1.9.0-alpha.1891),TinySoundFont.ChannelNoteOn/ChannelNoteOffthrowSystem.NullReferenceExceptionwhen a MIDI file is actually synthesized — both via the audio exporter (AlphaSynth.ExportAudio→IAlphaSynthAudioExporter.Render) and via the realtimeAlphaSynth.Play()path. The TypeScript/JS engine is unaffected.Repro (minimal, .NET 8, Windows)
Observed:
Narrowing (decompiled IL,
AlphaTab.dll1.8.4)TinySoundFont's constructor initializes_midiEventQueue,_mutedChannels,_soloChannels,_transpositionPitches,_liveTranspositionPitches,_metronomeChannel, and_voices = new List<Voice>()— but not_channels(matches the TS, where_channelsis lazily created).LoadSoundFont(Uint8Array)callsReset()first, andReset()sets_channels = null(TS equivalent keeps_channelsand only stops voices — please verify the translation)._channelInit(channel)recreates_channelslazily and its grow-loop matches the TS (i <= channel, inclusive), ending inreturn _channels.ChannelList[channel];.LoadSoundFont:ChannelSetPresetIndex(0, 24)→ OKChannelNoteOn(0, 60, 100)→ NRE (note: direct preset-levelNoteOn(24, 60, 100)succeeds, which suggests the crash is in code inlined intoChannelNoteOn— e.g. insideNoteOnor the channel lookup)ChannelNoteOff(0, 60)→ NRE (this method iterates_voices; in the observed instance_channelsheld 17 fully-constructedChannelentries, so the null deref is elsewhere)ChannelGetPresetIndex(0)→ OKSuggested first look: the state of
_channels/_voicesacross theLoadSoundFont→Reset()→LoadPresets()sequence, and theNoteOnbody thatchannelNoteOninlines (e.g.presetlookup /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
AlphaTab1.8.4 (also reproduced on1.9.0-alpha.1891),AlphaTab.Windows1.8.4, .NET 8, Windows 10/11