# Build an audio generator

> Run a small Node audio generator with a Maru account token or a configured inference service.

Canonical: https://maruaudio.com/docs/audio-generator
Documentation index: https://maruaudio.com/llms.txt

Build a command-line application that turns a prompt into seeded WAV files.
The generation SDK handles request limits, model selection, progress,
cancellation, event-stream reconnection and WAV decoding. Your application
supplies credentials and decides where to save the result.

This is an experimental source-workspace example. It requires a Maru checkout
with its dependencies installed. Hosted generation also requires an available
inference service and Studio admission for the account. The public npm package's
proposed hosted operation API is a separate integration; these examples use the
existing generation gateway.

## Start without an account

From the repository root:

```sh
pnpm --filter @maruaudio/website exec node --import tsx scripts/examples/generate-audio.ts \
  --mode mock --prompt 'a dry glass bell' --seeds 1042,1043 \
  --seconds 2 --out /tmp/maru-bells
```

The mock produces deterministic test tones, without a model download or a
provider call. Use a new output directory for each run. The example refuses an
existing directory before submitting generation.

Try a loop with the same application:

```sh
pnpm --filter @maruaudio/website exec node --import tsx scripts/examples/generate-audio.ts \
  --mode mock --audio-type loop --prompt 'an evolving instrumental pad' \
  --seconds 8 --seeds 1050 --out /tmp/maru-loop
```

## Connect your Maru account

Create a personal access token on [Access tokens](https://maruaudio.com/account/tokens) with the
`inference:run` scope. Set `MARU_TOKEN` in your shell or secret manager.
Keep it out of source control, browser code and module state. The token belongs
to your account, and generated usage is attributed to that account.

```sh
pnpm --filter @maruaudio/website exec node --import tsx scripts/examples/generate-audio.ts \
  --mode account --prompt 'a dry glass bell' --seconds 2 --seeds 1042 \
  --out /tmp/maru-cloud-bell
```

The default account service is the Maru platform. Set `MARU_API_URL` to use a
development website instead. Tokens are sent only to that origin, over HTTPS
or a local loopback connection. A token needs both the inference scope and an
account admitted to Studio. Owning a token alone does not grant admission.

Account mode submits and cancels through the typed `generation` tRPC service.
The SDK reads the authenticated event stream and audio bytes. Your program
never receives the website's provider secret or an inference-runner token.

## Connect another inference service

Set `AUDIO_INFERENCE_URL` to a service that implements Maru's audio inference
v2 contract, and `AUDIO_INFERENCE_KEY` to the key issued by its operator:

```sh
pnpm --filter @maruaudio/website exec node --import tsx scripts/examples/generate-audio.ts \
  --mode service --audio-type loop --prompt 'a warm ambient instrumental loop' \
  --seconds 8 --seeds 1050 --out /tmp/maru-service-loop
```

This service credential differs from a Stability AI provider API key. The
connector handles the Stable Audio model protocol supported by that service;
it does not call arbitrary provider APIs. Direct service requests do not pass
through the Maru account wallet. Their operator owns usage accounting.

## Build on the example

The reusable host is `apps/website/scripts/examples/audio-generator.ts`.
Import `connectExampleGenerator` and `generateToDirectory` from that file in a
workspace application. The command-line adapter beside it demonstrates the
minimal configuration:

```ts
const connector = await connectExampleGenerator({
  mode: "account",
  credential: process.env.MARU_TOKEN,
})
const result = await generateToDirectory({
  connector,
  out: "/tmp/my-new-take",
  request: {
    audioType: "one-shot",
    prompt: "a soft wooden tap",
    seconds: 2,
    model: "auto",
    seeds: [1042],
    steps: 8,
  },
  signal: myAbortController.signal,
})
```

The host composes `createAudioGenerationService`, `collectGenerationJob`, the
SDK's hosted or mock connector and the shared WAV encoder. It adds no model
implementation or realtime audio path. For a module face, retain this host
boundary: the host owns credentials and inference, while the face receives
display-safe progress and prepared audio resources.

Each successful item becomes a float32 WAV. `receipt.json` records its seed,
model provenance, license, SHA-256 and the job's terminal outcome. Check
`terminal.type` and `itemErrors` before treating the batch as complete. Partial
results stay available when an item fails or a job is cancelled. Ctrl-C cancels
the job; the CLI also cancels after ten minutes. Service discovery has its own
SDK timeout. These outputs are candidates for audition and quality checks,
not automatically published modules or sound banks.

## Credits and model usage

[Usage](https://maruaudio.com/usage) shows account balances, audio usage and model input/output
tokens. The initial token policy reuses Okslop's 0.5 credits per 1,000 input
tokens and 1.5 per 1,000 output tokens, rounded up to a tenth per provider call.
Model multipliers have a central override map; unlisted models use 1. Audio
credits use the existing model, duration, variation and step weights.

The server owns pricing. It stores each event's policy version and quoted
amount, so changing rates does not rewrite older events. Local generation is
unbilled. Account credit enforcement is an operator-controlled rollout:
metering mode records consumption without debiting a wallet. When enabled,
generation reserves credits before submission and a refused submission
releases that reservation. Accepted work retains its admission quote.

Hosted agent calls debit their observed token usage. A completed call can
exceed its starting balance; the next call is then blocked. Missing provider
counts remain marked incomplete. Cached input and reasoning output are already
included in the provider's respective totals and are not added twice.

These starter credit ratios do not establish a currency price, subscription,
automatic free allowance or purchase flow.
