Less audio plumbing. More programmable sound.
Generating a MIDI file in C# isn't particularly difficult.
Generating MIDI, rendering audio, keeping everything reproducible, and making the musical source easy to review in Git is where complexity starts to accumulate.
SoundScript takes a different approach.
Instead of manually creating MIDI events or building audio pipelines, you describe musical intent and let SoundScript render the outputs.
Let's build something.
Install SoundScript
SoundScript 13 targets .NET 10.
dotnet add package SoundScript --version 13.0.0
Create a small C# program:
using SoundScript;
var cue = SoundScriptEngine.Compile(
"tempo 120 track cue { instrument piano mf C4 e E4 e G4 q }");
File.WriteAllBytes("success.mid", cue.RenderMidi());
File.WriteAllBytes("success.wav", cue.RenderWave());
That's it.
One musical definition produces two outputs:
- ๐น MIDI
- ๐ WAV
No DAW project.
No MIDI event plumbing.
No duplicated definitions.
The Musical Source
Here's the SoundScript source in a more readable format:
tempo 120
track cue {
instrument piano
mf
C4 e
E4 e
G4 q
}
Where:
| Symbol | Meaning |
|---|---|
| C4, E4, G4 | Notes |
| e | Eighth note |
| q | Quarter note |
| mf | Mezzo-forte dynamic |
Instead of manually creating:
- note-on events
- note-off events
- timestamps
- velocities
you describe the musical idea and let SoundScript generate the implementation details.
Why Use a Language Instead of a MIDI API?
Libraries such as DryWetMIDI are excellent when you need direct control over MIDI data.
They expose concepts like:
- Notes
- Timed events
- Tempo maps
- Patterns
- MIDI devices
- Playback
- File manipulation
That's exactly what a MIDI library should do.
SoundScript operates at a higher level.
Traditional MIDI Workflow
Application
โ
Create musical objects
โ
Manage timing
โ
Build MIDI structure
โ
Serialize MIDI
SoundScript Workflow
Application
โ
Describe music
โ
Compile
โ
Render MIDI / WAV
SoundScript isn't trying to replace MIDI libraries.
It provides a different abstraction when your application cares about musical intent rather than individual MIDI events.
If you're building a MIDI editor, DAW, or hardware integration tool, a MIDI library is likely the right choice.
If your application simply needs to say:
Play this deterministic musical cue.
then a higher-level representation can dramatically reduce application code.
Change the Music Without Changing the Application
Suppose a designer wants a different sound.
Only the musical definition changes:
var cue = SoundScriptEngine.Compile("""
tempo 96
track notification {
instrument violin
mp
C4 q
E4 q
G4 q
C5 h
}
""");
The rendering code remains identical:
File.WriteAllBytes(
"notification.mid",
cue.RenderMidi());
File.WriteAllBytes(
"notification.wav",
cue.RenderWave());
Separating musical behaviour from application logic becomes increasingly useful as audio requirements evolve.
One Source, Multiple Outputs
MIDI and WAV serve different purposes.
MIDI
MIDI contains symbolic musical information.
Useful for:
- Sequencing
- Editing
- DAW workflows
- MIDI processing
- Compact storage
WAV
WAV contains rendered audio.
Useful for:
- Direct playback
- HTTP delivery
- Asset generation
- Application packaging
- Audio processing pipelines
Using separate systems for MIDI and audio often means maintaining multiple representations of the same cue.
SoundScript generates both from a single source:
โโโโ MIDI
SoundScript โโโโโค
โโโโ WAV
One definition.
Two outputs.
No duplication.
Move Music Out of C
Embedding short cues in code is convenient.
For larger projects, store music in .ss files.
scores/
โโโ notification.ss
Load and compile:
using SoundScript;
var compilation =
SoundScriptEngine.CompileFile(
"scores/notification.ss");
File.WriteAllBytes(
"notification.wav",
compilation.RenderWave());
Now musical content can evolve independently of application code.
Audio Becomes Versionable
One of the most interesting side effects of representing music as text is that audio becomes part of normal software workflows.
You can:
- โ Commit audio definitions to Git
- โ Review them in pull requests
- โ Compare changes with diffs
- โ Regenerate assets during builds
- โ Automate fixture generation
- โ Track exactly what changed
Instead of comparing:
notification-old.wav
notification-new.wav
you review:
-tempo 100
+tempo 120
-track alert {
- C4 q E4 q
+track alert {
+ C4 e
+ E4 e
+ G4 q
}
For developers, that's a much more useful workflow.
Deterministic Rendering
SoundScript is designed around reproducible generation.
Given the same:
- SoundScript source
- Renderer version
- Render options
- Referenced assets
the output is designed to be repeatable.
That enables workflows such as:
Source
โ
Build
โ
WAV / MIDI
โ
Hash
โ
Regression Test
This becomes particularly valuable when audio is part of:
- Automated testing
- CI/CD pipelines
- Generated assets
- Research workflows
- Reproducible demonstrations
We'll explore this in a future article.
Interesting Use Cases
๐ Application Notifications
Generate cues for:
- Success
- Warning
- Failure
- New messages
- Deployment completion
๐ฎ Game Prototypes
Create state-driven audio behaviours without manually managing countless variations.
๐งช Automated Testing
Generate WAV and MIDI fixtures as part of test setup.
๐ฆ Monitoring Systems
Map operational state into repeatable audio signals.
โฟ Accessibility Experiments
Explore alternative non-visual feedback mechanisms.
๐ Education
Programmatically demonstrate:
- Notes
- Intervals
- Tempo
- Dynamics
- Musical structure
๐ญ Industrial Applications
Represent machine or process state through structured audio.
When Not to Use SoundScript
SoundScript is not a replacement for a DAW.
A traditional DAW remains the right tool for:
- Recording
- Mixing
- Mastering
- Complex arrangements
- Plugin-heavy workflows
- Live performance
SoundScript addresses a different question:
What if audio is part of your application's programmable behaviour?
Try Modifying the Example
Start with:
C4 e
E4 e
G4 q
Then experiment.
Change the tempo:
tempo 80
Change the instrument:
instrument violin
Change the dynamics:
p
Change the phrase:
C4 q
G4 q
C5 h
Recompile.
Render again.
Listen to the WAV.
Inspect the MIDI.
Commit the source.
That's the entire SoundScript workflow:
Write
โ
Compile
โ
Render
โ
Version
Get Started
Install SoundScript:
dotnet add package SoundScript --version 13.0.0
Resources
- ๐ Website: https://soundscript.net/
- ๐ฆ NuGet: https://www.nuget.org/packages/SoundScript
- ๐ป GitHub: https://github.com/dharangutti/sound-script
- ๐ Documentation: https://soundscript.net/doc.html?p=documentation.md
- ๐ V13 Release: https://github.com/dharangutti/sound-script/releases/tag/v13.0.0
SoundScript Developer Series
This article is part of a series exploring programmable audio with .NET.
Coming next:
Deterministic Audio for Automated Testing in .NET
We'll generate WAV and MIDI fixtures from source, render them repeatedly, and verify the output programmatically.
SoundScript
Write audio and media like code.
Top comments (0)