Files
dusk/src/dusk/audio/mixer/audiomixer.h
T
YourWishesandClaude Sonnet 5 9364768397 Add cutscene controls for the audio mixer, fix PSP pause latency
- Fixed PSP pause taking up to ~1.1s to actually go silent: its output
  thread ran independently of stream->state, draining its whole software
  ring regardless. audiostream_t.state is now volatile and the output
  thread checks AUDIO_STREAM_STATE_PLAYING before each hardware chunk,
  skipping output (without consuming the ring) while paused - pause now
  goes silent within about one chunk (~23ms) and resume has no gap.
- Added audioMixerPause/Resume/SetPan/SetLoop/FadeTo/IsFading to the
  mixer - immediate, channel-indexed primitives for cutscenes to drive.
  Fade transitions (fadeFrom/To/Duration/Time/Easing) live on
  audiomixerchannelstate_t and advance every frame in
  audioMixerChannelApplyVolume(), reusing the same easingApply()
  interpolation uifullbox_t already uses for screen fades.
- New src/dusk/rpg/cutscene/item/audio/ with 9 cutscene item types:
  AUDIO_PLAY (+ AUDIO_PLAY_SIMPLE/AUDIO_PLAY_LOOPED shorthands),
  AUDIO_STOP, AUDIO_PAUSE, AUDIO_RESUME, AUDIO_FADE (+ FADE_OUT/FADE_IN
  shorthands), AUDIO_FADE_WAIT, AUDIO_SET_PAN, AUDIO_SET_LOOP, and the
  combined AUDIO_SET - registered through the same enum/union/callback
  table/macro mechanism every other item type uses.
- Wired the same 9 types into the JSON-based (offline JSONC -> binary
  .cts) cutscene asset pipeline: tools/asset/cutscene/__main__.py's
  encoder and assetcutsceneloader.c's decoder. Verified round-trip by
  hand-encoding/decoding a test file covering all 9 types, and confirmed
  the two existing real cutscene files re-encode byte-identical.

Built and verified on Linux, PSP (Docker), GameCube (Docker) and Wii
(Docker).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-02 17:44:06 -05:00

205 lines
6.7 KiB
C

/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "audio/mixer/audiomixerchannel.h"
typedef struct {
audiomixerchannelstate_t channels[AUDIO_MIXER_CHANNEL_COUNT];
} audiomixer_t;
extern audiomixer_t AUDIO_MIXER;
/**
* Initializes the audio mixer.
*/
errorret_t audioMixerInit();
/**
* Updates the audio mixer, called before rendering (the start of the
* frame) - see audioMixerChannelUpdateEarly()'s own comment, run for every
* channel.
*/
errorret_t audioMixerUpdateEarly();
/**
* Updates the audio mixer, called at the end of each frame - see
* audioMixerChannelUpdateLate()'s own comment, run for every channel.
*/
errorret_t audioMixerUpdateLate();
/**
* Same as audioMixerPlayLooped() but with no looping.
*
* @param file File to be played.
* @param channel Channel you want to play this sound on.
* @param volume Volume of the sound, from 0.0 (silent) to 1.0 (loudest).
* @param pan Stereo panning of the sound, from AUDIO_STREAM_LEFT to
* AUDIO_STREAM_RIGHT.
*/
void audioMixerPlay(
const char_t *file,
const audiomixerchannel_t channel,
const float_t volume,
const float_t pan
);
/**
* Queues a sound to be played on the next audio mixer update, which occurs at
* the end of each frame.
*
* Mixer channels can only hold one voice (audio stream) at a time. If you try
* to queue multiple sounds on the same channel only the last one will be played
* and will stop any actively playing on that channel currently.
*
* Volume is also mixed based on the channel, so supplying volume 1.0 is then
* multiplied by the channel's volume, e.g. 0.5*1.0 = 0.5.
*
* @param file File to be played.
* @param channel Channel you want to play this sound on.
* @param volume Volume of the sound, from 0.0 (silent) to 1.0 (loudest).
* @param pan Stereo panning of the sound, from AUDIO_STREAM_LEFT to
* AUDIO_STREAM_RIGHT.
* @param loopCount How many times to loop the sound, 0 for infinite looping.
* @param loopStart Where the loop segment ends, in seconds, or -1 to loop
* the whole stream (the default).
* @param loopTo Where the loop segment starts, in seconds.
*/
void audioMixerPlayLooped(
const char_t *file,
const audiomixerchannel_t channel,
const float_t volume,
const float_t pan,
const uint8_t loopCount,
const float_t loopStart,
const float_t loopTo
);
/**
* Shared implementation behind audioMixerPlay()/audioMixerPlayLooped() -
* queues an AUDIO_MIXER_COMMAND_PLAY for the given channel, to be loaded at
* the end of this frame and started at the start of the next (see
* audioMixerUpdateLate()/audioMixerUpdateEarly()).
*
* @param file File to be played.
* @param channel Channel you want to play this sound on.
* @param volume Volume of the sound, from 0.0 (silent) to 1.0 (loudest).
* @param pan Stereo panning of the sound, from AUDIO_STREAM_LEFT to
* AUDIO_STREAM_RIGHT.
* @param looping Whether the stream should loop once it finishes.
* @param loopCount How many times to loop the sound, 0 for infinite looping.
* Ignored unless looping is true.
* @param loopStart Where the loop segment ends, in seconds, or -1 to loop
* the whole stream. Ignored unless looping is true.
* @param loopTo Where the loop segment starts, in seconds. Ignored unless
* looping is true.
*/
void audioMixerQueuePlay(
const char_t *file,
const audiomixerchannel_t channel,
const float_t volume,
const float_t pan,
const bool_t looping,
const uint8_t loopCount,
const float_t loopStart,
const float_t loopTo
);
/**
* Stops playing sound on the given channel.
*
* @param channel Channel to stop.
*/
void audioMixerStop(const audiomixerchannel_t channel);
/**
* Pauses the given channel's currently playing stream, if any - a safe
* no-op if the channel isn't playing anything. Playback position is
* retained, so audioMixerResume() continues from the same point.
*
* @param channel Channel to pause.
*/
void audioMixerPause(const audiomixerchannel_t channel);
/**
* Resumes the given channel's currently paused stream, if any - a safe
* no-op if the channel isn't playing anything.
*
* @param channel Channel to resume.
*/
void audioMixerResume(const audiomixerchannel_t channel);
/**
* Sets the stereo panning of the given channel's currently playing stream,
* if any - a safe no-op if the channel isn't playing anything. Only
* affects what's playing right now; a later audioMixerPlay()/
* audioMixerPlayLooped() call on this channel uses its own `pan` argument
* instead.
*
* @param channel Channel to update.
* @param pan Stereo panning, from AUDIO_STREAM_LEFT to AUDIO_STREAM_RIGHT.
*/
void audioMixerSetPan(const audiomixerchannel_t channel, const float_t pan);
/**
* Sets the looping behaviour of the given channel's currently playing
* stream, if any - a safe no-op if the channel isn't playing anything.
* Only affects what's playing right now; a later audioMixerPlay()/
* audioMixerPlayLooped() call on this channel uses its own loop arguments
* instead.
*
* @param channel Channel to update.
* @param looping Whether the stream should loop.
* @param loopCount How many times to loop, 0 for infinite. Ignored unless
* looping is true.
* @param loopStart Where the loop segment ends, in seconds, or -1 to loop
* the whole stream. Ignored unless looping is true.
* @param loopTo Where the loop segment starts, in seconds. Ignored unless
* looping is true.
*/
void audioMixerSetLoop(
const audiomixerchannel_t channel,
const bool_t looping,
const uint8_t loopCount,
const float_t loopStart,
const float_t loopTo
);
/**
* Starts fading the given channel from `from` to `to` over `duration`
* seconds, eased by `easing` - see audioMixerChannelFadeTo()'s own
* comment. Continues running (advanced every audioMixerUpdateEarly()) even
* if the channel isn't currently playing anything.
*
* @param channel Channel to fade.
* @param from Starting fade value, from 0.0 (silent) to 1.0 (loudest).
* @param to Ending fade value, from 0.0 (silent) to 1.0 (loudest).
* @param duration How long the fade takes, in seconds.
* @param easing Easing curve to apply to the fade's progress.
*/
void audioMixerFadeTo(
const audiomixerchannel_t channel,
const float_t from,
const float_t to,
const float_t duration,
const easingtype_t easing
);
/**
* Whether the given channel currently has a fade in progress (started via
* audioMixerFadeTo() and not yet finished).
*
* @param channel Channel to check.
* @return true if a fade is still in progress.
*/
bool_t audioMixerIsFading(const audiomixerchannel_t channel);
/**
* Disposes the audio mixer.
*/
errorret_t audioMixerDispose();