josuFramework - Audio

The audio system in josuFramework is designed to be flexible, thread-safe, and highly adjustable. Most audio components inherit from AdjustableAudioComponent, which provides built-in support for volume, balance, frequency, and tempo adjustments.

Audio Components

All audio objects in the framework are based on AudioComponent. This base class handles:

  • Audio Thread Deferring: Operations are enqueued to be executed on the dedicated audio thread.

  • Disposal: Proper cleanup of native audio resources.

  • Update Logic: Periodic updates for state management and child components.

Adjustable Audio Components

AdjustableAudioComponent adds the ability to adjust various audio properties. These properties are managed using BindableNumber<Double> objects, allowing for easy UI binding and automatic updates.

Adjustable Properties

The following properties can be adjusted:

  • Volume: The loudness of the component (0.0 to 1.0).

  • Balance: The stereo panning (-1.0 for left, 1.0 for right, 0.0 for center).

  • Frequency: The playback rate, which also affects the pitch.

  • Tempo: The playback rate, without affecting the pitch.

Aggregation

Each property is an aggregate of multiple sources. This means you can have a “master volume” and a “local volume” both affecting the same component.

  • Local Adjustments: Accessed via getVolume(), getBalance(), etc.

  • Global/Parent Adjustments: Linked using bindAdjustments(IAggregateAudioAdjustment).

  • Manual Adjustments: Added using addAdjustment(AdjustableProperty type, IBindable<Double> adjustBindable).

The final value used for playback is the result of multiplying all sources (for Volume, Frequency, and Tempo) or summing them (for Balance).

Tracks and Samples

The framework distinguishes between two main types of audio:

Tracks

Tracks (ITrack) are intended for long-running audio, like background music.

ITrack track = trackStore.get("music.mp3");
track.start();
track.setLooping(true);
track.getVolume().setValue(0.5);

Samples

Samples (ISample) are intended for short sound effects. Playing a sample returns a SampleChannel, which allows for independent adjustment of that specific playback instance.

ISample sample = sampleStore.get("hit.wav");
ISampleChannel channel = sample.play();
channel.getVolume().setValue(0.8);

Samples also have a playbackConcurrency setting to limit how many instances of the same sample can play simultaneously.

Audio Mixing

The AudioMixer (IAudioMixer) combines audio from multiple IAudioChannel instances into a single output stream.

Each channel provides PCM sample data via:

}

Adjustments suck as volume, balance, frequency and temp are propagated through the mixer hierarchy using AdjustableAudioComponent.

Mixer adjustments

Adjustments applied to a mixer affect all channels attached to it.

AudioMixer sfxMixer = new AudioMixer();
sfxMixer.getVolume().setValue(0.7);

ISampleChannel channel = sample.play();
sfxMixer.add(channel);

The final playback volume of the channel will include both:

  • the channel’s own volume

  • the mixer’s aggregate volume

Clipping

Because audio samples are summed together, mixed output may exceed the valid sample range.

Implementations should clamp output values:

sample = Math.max(-1.0f, Math.min(1.0f, sample));

Threading

Mixers are expected to operate on the dedicated audio thread. Channel modifications should avoid concurrent modification during rendering.

The current implementation uses a simplified update model and may evolve toward a lock-free or command-queue-based audio pipeline in future versions.