# Generate Background Music from JSON in .NET

*What if application data could drive sound without your application owning an entire audio engine?*

Modern applications already use JSON to describe behaviour.

- Configuration
- User preferences
- Application state
- Notifications
- Workflow status
- Environment settings
- Feature flags

That data typically controls things like:

```text
JSON
  ↓
UI

JSON
  ↓
Feature Behaviour

JSON
  ↓
API Configuration

JSON
  ↓
Workflow Logic
```

But JSON can also drive audio.

With **SoundScript**, a .NET application can transform ordinary structured data into musical intent and render deterministic **WAV** or **MIDI** output.

The key idea is that **JSON is not SoundScript's language**.

Your application owns the JSON schema.

Your application decides what the data means.

SoundScript simply performs the rendering.

```text
JSON
  ↓
Application Mapping
  ↓
SoundScript Source
  ↓
Compile
  ↓
WAV / MIDI
```

That separation turns out to be surprisingly useful.

Business data remains business data.

Audio remains programmable.

Let's build it.

---

## Install SoundScript

SoundScript 13 targets .NET 10.

```bash
dotnet add package SoundScript --version 13.0.0
```

We'll also use the JSON functionality built into .NET.

---

## Start with a Simple Configuration

Imagine an application has a configuration file like this:

```json
{
  "name": "checkout-success",
  "tempo": 120,
  "dynamic": "mf",
  "notes": "C4 e E4 e G4 e C5 q"
}
```

Create a matching model:

```csharp
record AudioCue(
    string Name,
    int Tempo,
    string Dynamic,
    string Notes);
```

Load the JSON:

```csharp
using System.Text.Json;

var json =
    File.ReadAllText("cue.json");

var cue =
    JsonSerializer.Deserialize<AudioCue>(
        json)
    ?? throw new InvalidOperationException(
        "Invalid audio configuration.");
```

At this stage there's nothing audio-specific happening.

The application simply has structured state.

---

## Map JSON into SoundScript

Now transform that state into musical intent.

```csharp
var source = $"""
    tempo {cue.Tempo}

    track cue {{
        instrument piano

        {cue.Dynamic}

        {cue.Notes}
    }}
    """;
```

Compile and render:

```csharp
using SoundScript;

var compilation =
    SoundScriptEngine.Compile(source);

var wav =
    compilation.RenderWave();

var midi =
    compilation.RenderMidi();

File.WriteAllBytes(
    $"{cue.Name}.wav",
    wav);

File.WriteAllBytes(
    $"{cue.Name}.mid",
    midi);
```

That's the entire pipeline.

```text
cue.json
    ↓
Deserialize
    ↓
Application Model
    ↓
Map to SoundScript
    ↓
Compile
    ↓
checkout-success.wav
checkout-success.mid
```

---

## Why Not Just Store a WAV File?

A reasonable question.

For a single fixed cue:

```text
checkout-success.wav
```

is often the best solution.

Static assets are:

- Easy to preview
- Professionally designed
- Simple to deploy
- Simple to maintain

You shouldn't replace static audio just because dynamic generation is possible.

The value appears when application state influences the sound.

---

## Example: Severity-Driven Audio

Suppose the application receives:

```json
{
  "state": "warning",
  "severity": 3
}
```

Instead of exposing raw audio controls, define a simple business model:

```csharp
record StatusCue(
    string State,
    int Severity);
```

Map severity into known dynamics:

```csharp
var dynamic =
    cue.Severity switch
    {
        1 => "mp",
        2 => "mf",
        3 => "f",
        _ => "ff"
    };
```

Map state into known musical motifs:

```csharp
var motif =
    cue.State switch
    {
        "success" =>
            "C4 e E4 e G4 e C5 q",

        "warning" =>
            "A3 e A3 e E4 q A3 e",

        "failure" =>
            "C3 e C3 e G2 q C3 e",

        _ =>
            "C4 q"
    };
```

Create SoundScript:

```csharp
var source = $"""
    tempo 110

    track status {{
        instrument piano

        {dynamic}
        {motif}
    }}
    """;
```

And render:

```csharp
var wav =
    SoundScriptEngine
        .Compile(source)
        .RenderWave();
```

Notice the separation of responsibilities:

```text
State + Severity
         ↓
Application Rules
         ↓
Musical Intent
         ↓
SoundScript
         ↓
Audio
```

---

## Keep Business Logic Outside the Audio Engine

This is an important architectural boundary.

You probably don't want SoundScript deciding:

```text
Severity 4 = Emergency
```

That's an application concern.

Likewise, you probably don't want your business configuration describing:

```text
MIDI Channel 1
Velocity 112
Tick Position 480
```

That's audio implementation detail.

A cleaner architecture is:

```text
Business State
      ↓
Application Rules
      ↓
Musical Intent
      ↓
SoundScript
      ↓
Audio
```

Each layer has a single responsibility.

---

## Avoid Passing Arbitrary Source from JSON

A tempting approach is:

```json
{
  "source": "tempo 120 track x { ... }"
}
```

followed by:

```csharp
SoundScriptEngine.Compile(
    config.Source);
```

For trusted developer-authored configuration, that's perfectly valid.

For user data or external input, a better pattern is:

```json
{
  "state": "warning",
  "intensity": 2
}
```

Then map it explicitly:

```text
Known Input
      ↓
Validation
      ↓
Application Rules
      ↓
Known Musical Structures
```

Rather than:

```text
Arbitrary String
        ↓
Compiler
```

The same principle applies to many configurable systems.

---

## A More Realistic Example

Imagine a deployment monitoring system producing:

```json
{
  "event": "deployment",
  "result": "failed",
  "severity": 4,
  "environment": "production"
}
```

Create a model:

```csharp
record DeploymentEvent(
    string Event,
    string Result,
    int Severity,
    string Environment);
```

Now define rules.

### Tempo

```csharp
var tempo =
    data.Environment switch
    {
        "production" => 116,
        "staging" => 104,
        _ => 96
    };
```

### Dynamic

```csharp
var dynamic =
    data.Severity switch
    {
        <= 1 => "mp",
        2 => "mf",
        3 => "f",
        _ => "ff"
    };
```

### Motif

```csharp
var motif =
    data.Result switch
    {
        "success" =>
            "C4 e E4 e G4 e C5 q",

        "failed" =>
            "C3 e C3 e G2 q C3 e",

        _ =>
            "C4 q"
    };
```

Assemble the cue:

```csharp
var source = $"""
    tempo {tempo}

    track deployment {{
        instrument piano

        {dynamic}
        {motif}
    }}
    """;
```

The same deployment state always produces the same intended musical behaviour.

That's one of the biggest advantages of deterministic audio.

---

## JSON Is Control Data, Not Music

Notice the difference between these two representations.

Application state:

```json
{
  "result": "failed",
  "severity": 4
}
```

Musical intent:

```text
tempo 116

track deployment {
    instrument piano
    ff

    C3 e
    C3 e
    G2 q
    C3 e
}
```

They serve different purposes.

Trying to make them identical often creates unnecessary coupling.

Keeping them separate tends to produce cleaner designs.

---

## Why Not Use an AI Music API?

Let's compare the goals.

Imagine sending:

```json
{
  "state": "warning",
  "severity": 3
}
```

to a generative music system using a prompt like:

> Create a short urgent notification sound for severity level 3.

That can work very well if you want creative variation.

But application audio often requires different guarantees.

You may want:

```text
Severity 3
      ↓
Same Rules
      ↓
Same Output
```

You may want to:

- Test it
- Cache it
- Hash it
- Version it
- Reproduce it

Deterministic generation is often a better fit for those requirements.

---

## Comparing Audio Architectures

### Static Assets

```text
State
  ↓
Lookup
  ↓
Existing WAV
```

**Advantages**

- Simple
- Fast
- Professional audio quality

**Trade-off**

Every variation usually requires another asset.

---

### Generative Music Service

```text
State
  ↓
Prompt
  ↓
Model
  ↓
Generated Audio
```

**Advantages**

- Creative
- Rich output
- Large stylistic range

**Trade-off**

- Probabilistic behaviour
- External dependencies
- Different reproducibility model

---

### DSP / Custom Synthesis

```text
State
  ↓
DSP Code
  ↓
Oscillators
Envelopes
Mixers
  ↓
PCM Audio
```

**Advantages**

- Maximum control

**Trade-off**

- Significant audio-engine complexity

---

### SoundScript

```text
State
  ↓
Application Rules
  ↓
Musical Intent
  ↓
SoundScript
  ↓
WAV / MIDI
```

This occupies a useful middle ground.

- More dynamic than static assets
- Higher level than DSP
- More deterministic than prompt generation

---

## User-Configurable Notification Profiles

Imagine users can select:

```json
{
  "notificationStyle": "gentle",
  "speed": "normal"
}
```

Your application maps:

```text
gentle
   ↓
piano
mp
```

and:

```text
normal
   ↓
tempo 100
```

Another profile might be:

```json
{
  "notificationStyle": "urgent",
  "speed": "fast"
}
```

Mapping to:

```text
urgent
  ↓
stronger dynamic
different motif

fast
  ↓
tempo 132
```

Notice that you're exposing meaningful product concepts, not audio-engine internals.

That's generally easier for both developers and users.

---

## Game State as Audio

Imagine:

```json
{
  "health": 24,
  "danger": true,
  "level": 7
}
```

Application rules derive:

```text
danger = true
      ↓
warning motif

health < 30
      ↓
stronger dynamic

level = 7
      ↓
tempo adjustment
```

Result:

```text
Game State
     ↓
Application Rules
     ↓
Musical State
     ↓
Audio
```

This pattern is particularly useful during prototyping when sound design is still evolving.

---

## Cache Repeated States

Not every cue needs to be rendered repeatedly.

Generate a cache key:

```csharp
var key =
    $"{state}:{severity}:{tempo}";
```

Then:

```csharp
if (cache.TryGetValue(
        key,
        out var existing))
{
    return existing;
}
```

Otherwise:

```csharp
var wav =
    SoundScriptEngine
        .Compile(source)
        .RenderWave();

cache[key] = wav;

return wav;
```

The architecture becomes:

```text
Application State
         ↓
      Key
    ↙     ↘
 Hit     Miss
  ↓        ↓
Reuse   Render
           ↓
        Cache
```

A simple optimisation with substantial benefits.

---

## Generate Assets at Build Time

Dynamic audio doesn't have to mean runtime rendering.

Suppose you support:

```text
3 States
× 4 Severity Levels
```

That's only:

```text
12 Outputs
```

A build pipeline could generate:

```text
success-1.wav
success-2.wav
success-3.wav
success-4.wav

warning-1.wav
warning-2.wav
warning-3.wav
warning-4.wav

failure-1.wav
failure-2.wav
failure-3.wav
failure-4.wav
```

At runtime, the application simply loads files.

You keep deterministic generation while avoiding runtime rendering entirely.

This hybrid model is often very practical.

---

## Audio Mappings Can Be Tested

Because generation is deterministic, it can participate in automated testing.

```csharp
var first =
    BuildCue(config);

var second =
    BuildCue(config);

Assert.True(
    first.AsSpan()
         .SequenceEqual(second));
```

Or use hashes:

```csharp
using System.Security.Cryptography;

var hash =
    Convert.ToHexString(
        SHA256.HashData(first));

Console.WriteLine(hash);
```

Your configuration-to-sound rules become testable software.

---

## A Simple Helper

You can encapsulate the entire workflow:

```csharp
using SoundScript;

static byte[] RenderCue(
    string state,
    int severity)
{
    var dynamic =
        severity switch
        {
            1 => "mp",
            2 => "mf",
            3 => "f",
            _ => "ff"
        };

    var motif =
        state switch
        {
            "success" =>
                "C4 e E4 e G4 e C5 q",

            "warning" =>
                "A3 e A3 e E4 q A3 e",

            "failure" =>
                "C3 e C3 e G2 q C3 e",

            _ =>
                "C4 q"
        };

    var source = $"""
        tempo 110

        track cue {{
            instrument piano

            {dynamic}
            {motif}
        }}
        """;

    return SoundScriptEngine
        .Compile(source)
        .RenderWave();
}
```

Usage:

```csharp
var wav =
    RenderCue(
        "warning",
        3);

File.WriteAllBytes(
    "warning.wav",
    wav);
```

Your application stays focused on:

```text
State
Severity
```

rather than audio implementation details.

---

## Where This Approach Fits

### 🔔 Application Notifications

Generate controlled cues from application events.

### 🎮 Games

Derive audio behaviour from game state.

### 🚦 Monitoring

Map system status into repeatable cues.

### ♿ Accessibility Experiments

Create structured non-visual feedback.

### 🏭 Industrial Applications

Represent machine and process states.

### 🎓 Education

Demonstrate how structured data can drive music.

### 🔄 CI/CD

Turn build and deployment status into deterministic audio.

### 🧪 Automated Testing

Generate reproducible audio fixtures.

---

## When Not to Use It

Don't adopt this architecture simply because your application already uses JSON.

If the requirement is:

> Play one professionally designed notification sound.

Then:

```text
notification.wav
```

may be the ideal solution.

Dynamic generation becomes valuable when audio meaningfully depends on application state.

That's the real decision point.

---

## The Bigger Idea

Modern software already transforms data into many outputs.

```text
JSON
  ↓
UI

JSON
  ↓
HTML

JSON
  ↓
API Behaviour

JSON
  ↓
Workflow Execution
```

Programmable audio adds another possibility:

```text
JSON
  ↓
Application Rules
  ↓
Musical Intent
  ↓
Audio
```

The important part isn't the JSON.

The important part is that sound becomes another output of application logic.

---

## Try It Yourself

Create:

```json
{
  "name": "success",
  "tempo": 120,
  "dynamic": "mf",
  "notes": "C4 e E4 e G4 e C5 q"
}
```

Map it to SoundScript:

```csharp
var source = $"""
    tempo {cue.Tempo}

    track cue {{
        instrument piano

        {cue.Dynamic}
        {cue.Notes}
    }}
    """;
```

Render it:

```csharp
var wav =
    SoundScriptEngine
        .Compile(source)
        .RenderWave();

File.WriteAllBytes(
    $"{cue.Name}.wav",
    wav);
```

Then experiment.

Change:

```json
"tempo": 120
```

to:

```json
"tempo": 80
```

Or:

```json
"dynamic": "mf"
```

to:

```json
"dynamic": "ff"
```

Render again and compare the result.

The workflow is straightforward:

```text
Configure
     ↓
Map
     ↓
Compile
     ↓
Render
     ↓
Listen
```

---

## Try SoundScript

```bash
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

### Previous

**Text-to-Music with SoundScript: Deterministic Composition Instead of Prompting**

### Coming Next

**Procedural Game and Application Audio in .NET**

We'll move from configuration-driven cues to event-driven procedural audio and explore when generated sound makes more sense than maintaining large collections of static assets.

---

**SoundScript**  
*Write audio and media like code.*

📧 info@dharangutti.in  
🌐 https://www.dharangutti.in/
