buildEvents method
Renders the sequence into a list of FaustEventData ready for the native bridge.
autoDecayMs is the micro-gap between staccato notes to ensure clean re-strikes.
Implementation
List<FaustEventData> buildEvents({double autoDecayMs = 10.0}) {
final List<FaustEventData> events = <FaustEventData>[];
final double samplesPerBeat = (60.0 / tempoBpm) * sampleRate;
final int autoDecaySamples = (autoDecayMs * sampleRate / 1000.0).toInt();
// Group notes by instrument to handle overlaps/slurs
final Map<int, List<FaustNote>> instrumentTracks = {};
for (var note in _notes) {
instrumentTracks.putIfAbsent(note.instrumentId, () => []).add(note);
}
for (var entry in instrumentTracks.entries) {
final int instId = entry.key;
final List<FaustNote> trackNotes = entry.value;
trackNotes.sort((a, b) => a.beat.compareTo(b.beat));
for (int i = 0; i < trackNotes.length; i++) {
final note = trackNotes[i];
final int startSample = (note.beat * samplesPerBeat).toInt();
final int durationSamples = (note.duration * samplesPerBeat).toInt();
final int endSample = startSample + durationSamples;
// 1. Set Frequency
events.add(FaustEventData(
sampleOffset: startSample,
instrumentId: instId,
eventType: 1, // SetFreq
value: note.pitch,
));
// 2. Start Note (Strike/Breath)
events.add(FaustEventData(
sampleOffset: startSample,
instrumentId: instId,
eventType: 0, // Strike
value: note.velocity,
paramId: note.paramId,
));
// 3. Handle Note Off or Slur
bool isSlurred = false;
if (i < trackNotes.length - 1) {
final nextNote = trackNotes[i + 1];
final int nextStart = (nextNote.beat * samplesPerBeat).toInt();
// If the next note starts exactly at or before our end, and it's legato
if (nextStart <= endSample && (note.legato || nextNote.legato)) {
isSlurred = true;
}
}
if (!isSlurred) {
// Normal note: Insert a micro-decay before the end to ensure separation
final int stopSample = max(startSample + 100, endSample - autoDecaySamples);
events.add(FaustEventData(
sampleOffset: stopSample,
instrumentId: instId,
eventType: 0, // Stop Breath/Strike
value: 0.0,
));
}
}
}
events.sort((FaustEventData a, FaustEventData b) => a.sampleOffset.compareTo(b.sampleOffset));
return events;
}