Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Catalog · How the catalog works
Anything · Lazarus or Delphi
Random projectRandom project

SedaiAudio4Delphi

A thin, flat-C DLL bridge that lets you use the **Sedai Audio Foundation** (a Free Pascal / Object Pascal audio synthesis library) from **Delphi 11.3** (or any Delphi version that supports `external 'sedaiaudio.dll'`).

View on GitHub

Member rating

No ratings yet.

Sign in to rate this project.

README

Sedai Audio 4 Delphi v0.1

Unbenannt

SedaiAudio https://github.com/camauri/SedaiAudio

A thin, flat-C DLL bridge that lets you use the Sedai Audio Foundation (a Free Pascal / Object Pascal audio synthesis library) from Delphi 11.3 (or any Delphi version that supports external 'sedaiaudio.dll').

The Sedai Audio Foundation is a complete synth engine: classic subtractive, FM, additive, wavetable, Karplus-Strong, sample playback, MIDI voice management, modulation matrix, multi-effect filters, and more. It is written in Free Pascal and ships as a Delphi-incompatible Object Pascal unit. This project re-packages it as a native Win64 DLL with a flat cdecl C-API, and ships a Delphi import unit so you can call every function directly from your Delphi engine.


Features

  • Flat C-API, cdecl — every public function of SedaiAudioFoundation is exported under its original name (e.g. InitAudio, PlaySine, PlayWavetable, MIDIPlayNote, …). 128 original exports + one helper for array returns = 129 DLL exports.
  • Static Delphi imports — no LoadLibrary / GetProcAddress boiler- plate. Add SedaiAudio4Delphi.pas to your uses clause and call.
  • Five synthesis engines in one DLL:
    • Classic subtractive (sine / square / saw / triangle / pulse / noise, plus lead / bass / pad presets)
    • FM synthesis (e-piano / brass / bell / organ / lead / bass)
    • Additive synthesis (up to 64 harmonics, organ / bell / strings)
    • Karplus-Strong plucked string (guitar / bass)
    • Wavetable synthesis (basic / supersaw / PWM / Serum / Wasp / PPG)
  • Sample playback — register in-memory buffers or load WAV files, repitched relative to a configurable root note, loop mode supported.
  • Wavetable loading — load single files or scan whole directories, on-disk cache, list/count/query loaded wavetables, custom in-memory wavetables.
  • Advanced voice control — per-voice frequency / amplitude / pan / pulse-width / ADSR / retrigger, per-voice filter (type, cutoff, Q, slope 12/24/36/48 dB).
  • Instrument modulation matrix — unlimited LFOs per instrument, fixed sources (envelope / velocity / key-track / pitch-bend) routed to pitch / cutoff / amplitude, one-call helpers for vibrato / tremolo / filter-LFO.
  • Instrument oscillators — three-oscillator mix, ring-mod, hard-sync, sub-oscillator.
  • MIDI utilities — note↔frequency conversion, velocity↔amplitude, pan conversion, note name + octave lookup.
  • MIDI voice pool — 256-voice pool with allocate / set / on / off / release, frequency / amplitude / wavetable / pan per voice.
  • MIDI integration callback — register a cdecl callback to be invoked from the audio thread for external sequencer updates.
  • Chord and scale helpers — one call for a triad or a major scale across any synth type.
  • Win64 native — built with Free Pascal (-Twin64), links against the Microsoft x64 ABI, so cdecl on both sides is ABI-identical.
  • Console-safe — the DLL redirects FPC's stdout / stderr to NUL on load, so WriteLn calls inside the engine do not raise EInOutError in GUI hosts (no more External Exception E0465043).
  • Exception-safe Init / Shutdown — both are wrapped in try-except so a backend failure returns False instead of crashing the Delphi host.

Project layout

File Purpose
sedaiaudio.lpr Free Pascal DLL project. Compile this to produce sedaiaudio.dll.
SedaiAudio4Delphi.pas Delphi import unit. Add to your uses clause.
SedaiAudioSample.pas Delphi VCL sample form. 32 buttons, one per sound, no DFM changes.
SedaiAudioFoundation.pas The original FPC engine unit (drop-in, not modified by this project).

Building the DLL yourself

You need:

  • Free Pascal 3.2.2 or newer (fpc on PATH)
  • The full Sedai Audio Foundation source tree (all Sedai*.pas units), in the same directory as sedaiaudio.lpr or on the FPC unit search path
  • Audio backend dependencies: SDL2.dll and SDL3.dll must be available to the compiled DLL at runtime (place them next to your .exe). The Sedai backend currently pulls in both, so both are needed.

From the directory that contains sedaiaudio.lpr and the Sedai units:

bash
fpc -Twin64 -O2 -XSs -Xt sedaiaudio.lpr

Flags:

  • -Twin64 target Windows 64-bit
  • -O2 optimizer level 2
  • -XSs smart-linkable (smaller DLL)
  • -Xt strip symbols

The output is sedaiaudio.dll. Copy it (together with SDL2.dll and SDL3.dll) next to your Delphi .exe or onto the system PATH.

The DLL's begin…end. block redirects FPC's Output and ErrOutput text files to NUL. This is required because the Sedai engine uses WriteLn for status messages, and in a Delphi GUI host there is no console attached to stdout — without the redirect, FPC raises EInOutError, which Delphi cannot decode and surfaces as External Exception E0465043. The DLL also wraps InitAudio and ShutdownAudio in try-except, so any other FPC exception during init/shutdown returns False / is swallowed instead of crashing the host.


Using the DLL from Delphi

  1. Copy sedaiaudio.dll, SDL2.dll, and SDL3.dll into your Delphi project's output directory (next to the .exe).
  2. Add SedaiAudio4Delphi.pas to your project.
  3. Add SedaiAudio4Delphi to the uses clause where you want to call the engine.
  4. Initialize, play, shut down:
pascal
uses
  SedaiAudio4Delphi;

if not InitAudio(64) then
  raise Exception.Create('Sedai Audio backend failed to initialize');
try
  PlaySine(440.0, 0.5);
  PlayEPiano(440.0);
  PlayWavetable(440.0);              // nil -> DLL uses 'basic'
  PlayWavetable(440.0, PAnsiChar(AnsiString('supersaw')));

  // Advanced: get a voice handle and shape it
  var V := PlayClassicAdv(440.0, PAnsiChar(AnsiString('lead')));
  if V >= 0 then
  begin
    SetVoicePan(V, -0.5);
    SetVoiceADSR(V, 0.01, 0.2, 0.6, 0.4);
    SetVoiceFilter(V, True, FT_LOWPASS, 2000.0, 0.7, FS_24DB);
    NoteOff(V);                      // releases the envelopes
  end;

  // MIDI note pool
  var MV := MIDIPlayNote(60, 100, PAnsiChar(AnsiString('basic')));
  // ... later
  MIDIReleaseAllVoices;
finally
  ShutdownAudio;
end;

A full, runnable demo form is in SedaiAudioSample.pas — every button calls one engine function, and a TTimer shows live sample rate / active voice count / master volume in the form's status label.


Calling-convention & ABI notes

  • cdecl throughout. On Win64 the FPC register and cdecl conventions are ABI-identical (both use the Microsoft x64 calling convention), so callback registration is transparent.

  • String parameters are PAnsiChar (null-terminated ANSI strings). Delphi's default string is UnicodeString — convert explicitly:

    pascal
    PlayClassic(440.0, PAnsiChar(AnsiString('sine')));

    For routines that had a default preset in the original API (PlayClassic, PlayFM, PlayWavetable, PlayKarplus, PlayAdditive, MIDIPlayNote, …), pass nil to use the default. The DLL substitutes the original default value ('sine', 'epiano', 'basic', …). Note: PAnsiChar(AnsiString('')) in Delphi returns nil, so passing an empty Delphi string is the same as passing nil.

  • String return values (GetLoadedWavetableName, GetWavetableCacheInfo, MIDINoteToName, MIDIGetVoiceWavetable, GetWavetableFormatByIndex) return PAnsiChar pointing into a thread-local static buffer inside the DLL. The buffer is overwritten by the next string-returning call on the same thread. Copy the result to a Delphi string immediately:

    pascal
    var NoteName: string := string(MIDINoteToName(60));
  • Array returns (GetLoadedWavetables, GetWavetableFormats): the original routines returned TStringArray, which cannot cross the ABI boundary. They are exported as Integer (the element count). Use the indexed getters to retrieve individual items:

    pascal
    var Count := GetLoadedWavetables;
    for var I := 0 to Count - 1 do
      Memo.Lines.Add(string(GetLoadedWavetableName(I)));
  • Enum parameters are passed as Integer (32-bit) to keep the ABI independent of FPC enum packing. The constants SAF_*, WT_*, FT_*, FS_*, MS_*, MDT_*, VOM_* are declared in SedaiAudio4Delphi.pas. SAF_* (synth type) and MDT_* (modulation destination) are confirmed against the FPC source; the others (WT_*, FT_*, FS_*, MS_*, VOM_*) are inferred from usage — verify against the FPC source if you need the full set: SedaiAudioTypes, SedaiOscillator, SedaiFilter, SedaiModulationMatrix, SedaiVoice.

  • Opaque pointer parameters (RegisterSample.ABuffer, PlayCustomWavetable.ACustomWavetable, PlayCustomWavetableAdv.ACustomWavetable) take FPC-internal types that have no Delphi counterpart in this unit:

    • TSedaiAudioBuffer is a class → pass the object reference as Pointer
    • TWavetable is a record → pass the address of the record as Pointer (the DLL dereferences it)

    The memory layout must match the FPC type. Most Delphi callers will use LoadSampleFile + PlaySample / PlayLoadedWavetable instead, which cover the typical workflows without exposing those types.

  • Callbacks (RegisterMidiUpdateCallback) expect a cdecl procedure of procedure(ADeltaTimeSeconds: Single); cdecl. The DLL stores it and invokes it from the audio thread — keep the callback body short and non-blocking.


Requirements

Component Required for
Free Pascal 3.2.2+ Building sedaiaudio.dll
Sedai Audio Foundation source Building sedaiaudio.dll
SDL2.dll Runtime, next to your .exe
SDL3.dll Runtime, next to your .exe
Delphi 11.3 (or compatible) Compiling the sample + your app

Windows 64-bit only. Other targets would require recompiling the DLL and updating the external 'sedaiaudio.dll' references in the wrapper unit.

Sample project, exe and dlls included


License

The Sedai Audio Foundation is © 2025 Artiforge, licensed under GPL-3.0. The bridge files in this repository (sedaiaudio.lpr, SedaiAudio4Delphi.pas, SedaiAudioSample.pas) are provided under the same license to keep the combined work distributable.