- 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]>
205 lines
6.7 KiB
C
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();
|