Document what the functions actually do instead of that they can fail
The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
This commit is contained in:
@@ -50,18 +50,18 @@
|
||||
* uses for the same shape, for a caller translating one to the other.
|
||||
*/
|
||||
typedef enum {
|
||||
AKGL_AUDIO_WAVE_TRIANGLE = 0, /** SOUND waveform 0. Soft, flute-like. */
|
||||
AKGL_AUDIO_WAVE_SAWTOOTH = 1, /** SOUND waveform 1. Bright, brassy. */
|
||||
AKGL_AUDIO_WAVE_SQUARE = 2, /** SOUND waveform 2. Hollow, reedy. The default. */
|
||||
AKGL_AUDIO_WAVE_NOISE = 3, /** SOUND waveform 3. Unpitched; percussion. */
|
||||
AKGL_AUDIO_WAVE_SINE = 4 /** No SOUND equivalent. A pure tone. */
|
||||
AKGL_AUDIO_WAVE_TRIANGLE = 0, /**< SOUND waveform 0. Soft, flute-like. */
|
||||
AKGL_AUDIO_WAVE_SAWTOOTH = 1, /**< SOUND waveform 1. Bright, brassy. */
|
||||
AKGL_AUDIO_WAVE_SQUARE = 2, /**< SOUND waveform 2. Hollow, reedy. The default. */
|
||||
AKGL_AUDIO_WAVE_NOISE = 3, /**< SOUND waveform 3. Unpitched; percussion. */
|
||||
AKGL_AUDIO_WAVE_SINE = 4 /**< No SOUND equivalent. A pure tone. */
|
||||
} akgl_AudioWaveform;
|
||||
|
||||
/** @brief Holds one voice's oscillator, envelope, and how far through it is. */
|
||||
typedef struct {
|
||||
bool active;
|
||||
akgl_AudioWaveform waveform;
|
||||
float32_t hz;
|
||||
bool active; /**< Whether this voice is sounding. Cleared by the mixer once gate and release are both spent, so it goes quiet without being told to. */
|
||||
akgl_AudioWaveform waveform; /**< Oscillator shape. Persists across notes; set once with akgl_audio_waveform(). */
|
||||
float32_t hz; /**< Frequency of the current note. 0.0 when the voice has never sounded. */
|
||||
/**
|
||||
* @brief Position through one cycle, 0.0 to 1.0.
|
||||
*
|
||||
@@ -74,8 +74,11 @@ typedef struct {
|
||||
uint32_t duration_frames;
|
||||
/** @brief Frames generated since the tone started, gate and release. */
|
||||
uint32_t elapsed_frames;
|
||||
/** @brief Frames to rise from silence to full level. 0 starts at full level. */
|
||||
uint32_t attack_frames;
|
||||
/** @brief Frames to fall from full level to `sustain`. 0 drops to it at once. */
|
||||
uint32_t decay_frames;
|
||||
/** @brief Frames to fall to silence after the gate closes. 0 cuts off at once. */
|
||||
uint32_t release_frames;
|
||||
/** @brief Level the envelope decays to and holds, 0.0 to 1.0. */
|
||||
float32_t sustain;
|
||||
@@ -92,17 +95,26 @@ extern akgl_AudioVoice akgl_audio_voices[AKGL_AUDIO_MAX_VOICES];
|
||||
* it more than once. The voice table is reset only on the first call, so this
|
||||
* does not silence a voice that is already sounding.
|
||||
*
|
||||
* The device is resumed immediately rather than left paused, which is SDL's
|
||||
* default -- a caller who set up a voice and heard nothing would have no error
|
||||
* to explain it.
|
||||
*
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKGL_ERR_SDL When the corresponding validation or operation fails.
|
||||
* @throws AKGL_ERR_SDL If no playback device can be opened -- none present, none
|
||||
* permitted, or SDL's audio subsystem never initialized -- or if the
|
||||
* device cannot be resumed. The message carries `SDL_GetError()`.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_init(void);
|
||||
|
||||
/**
|
||||
* @brief Close the audio device and silence every voice.
|
||||
*
|
||||
* Safe to call when no device is open.
|
||||
* Safe to call when no device is open, and safe to call twice. It also puts the
|
||||
* voice table back to its defaults, so a subsequent akgl_audio_init() starts
|
||||
* from a known state rather than from whatever was left sounding.
|
||||
*
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @return `NULL`. There is no failure path -- SDL's stream teardown reports
|
||||
* nothing.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_shutdown(void);
|
||||
|
||||
@@ -115,19 +127,30 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_shutdown(void);
|
||||
* slightly longer than @p ms. Sounding a voice that is already sounding
|
||||
* restarts it from the beginning of its envelope.
|
||||
*
|
||||
* @param voice Zero-based voice index.
|
||||
* @param hz Frequency in hertz.
|
||||
* @param ms Gate length in milliseconds.
|
||||
* @param voice Zero-based voice index, 0 to #AKGL_AUDIO_MAX_VOICES - 1.
|
||||
* @param hz Frequency in hertz. Must be greater than 0. There is no upper
|
||||
* bound check, so a frequency above half the sample rate aliases
|
||||
* rather than being refused.
|
||||
* @param ms Gate length in milliseconds. Must be non-zero -- a zero-length
|
||||
* tone is refused rather than treated as "stop", because
|
||||
* akgl_audio_stop() already means that. Rounded down to a whole
|
||||
* number of frames, so a duration under ~0.023 ms rounds to
|
||||
* nothing.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_OUTOFBOUNDS When the corresponding validation or operation fails.
|
||||
* @throws AKERR_OUTOFBOUNDS If @p voice is outside the table, if @p hz is not
|
||||
* positive, or if @p ms is 0. Each message says which.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_tone(int voice, float32_t hz, uint32_t ms);
|
||||
|
||||
/**
|
||||
* @brief Silence one voice immediately, skipping its release.
|
||||
* @param voice Zero-based voice index.
|
||||
*
|
||||
* A hard cut, not a note-off: the release stage is not run, so the sound stops
|
||||
* on the next sample. Stopping a voice that is already silent is a no-op.
|
||||
*
|
||||
* @param voice Zero-based voice index, 0 to #AKGL_AUDIO_MAX_VOICES - 1.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_OUTOFBOUNDS When the corresponding validation or operation fails.
|
||||
* @throws AKERR_OUTOFBOUNDS If @p voice is outside the table.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_stop(int voice);
|
||||
|
||||
@@ -137,10 +160,12 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_stop(int voice);
|
||||
* Takes effect on the next akgl_audio_tone(); it does not reshape a note that
|
||||
* is already sounding.
|
||||
*
|
||||
* @param voice Zero-based voice index.
|
||||
* @param waveform Oscillator shape to use.
|
||||
* @param voice Zero-based voice index, 0 to #AKGL_AUDIO_MAX_VOICES - 1.
|
||||
* @param waveform One of the ::akgl_AudioWaveform shapes. The setting persists
|
||||
* across notes until changed.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_OUTOFBOUNDS When the corresponding validation or operation fails.
|
||||
* @throws AKERR_OUTOFBOUNDS If @p voice is outside the table, or @p waveform is
|
||||
* not one of the five defined shapes.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_waveform(int voice, akgl_AudioWaveform waveform);
|
||||
|
||||
@@ -153,21 +178,34 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_waveform(int voice, akgl_AudioWave
|
||||
*
|
||||
* Takes effect on the next akgl_audio_tone().
|
||||
*
|
||||
* @param voice Zero-based voice index.
|
||||
* @param attack Milliseconds to rise from silence to full level.
|
||||
* @param decay Milliseconds to fall from full level to @p sustain.
|
||||
* @param sustain Held level while the gate is open, 0.0 to 1.0.
|
||||
* @param release Milliseconds to fall to silence once the gate closes.
|
||||
* @param voice Zero-based voice index, 0 to #AKGL_AUDIO_MAX_VOICES - 1.
|
||||
* @param attack Milliseconds to rise from silence to full level. 0 starts at
|
||||
* full level.
|
||||
* @param decay Milliseconds to fall from full level to @p sustain. 0 drops to
|
||||
* it at once.
|
||||
* @param sustain Held level while the gate is open, 0.0 to 1.0 inclusive. 0.0 is
|
||||
* legal and means the note is audible only through its attack and
|
||||
* decay -- a plucked sound.
|
||||
* @param release Milliseconds to fall to silence once the gate closes. 0 cuts
|
||||
* off at once. An attack plus decay longer than the note's gate
|
||||
* is not an error: the release simply starts partway up the ramp.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_OUTOFBOUNDS When the corresponding validation or operation fails.
|
||||
* @throws AKERR_OUTOFBOUNDS If @p voice is outside the table, or @p sustain is
|
||||
* outside 0.0 to 1.0. The three durations are unbounded.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_envelope(int voice, uint32_t attack, uint32_t decay, float32_t sustain, uint32_t release);
|
||||
|
||||
/**
|
||||
* @brief Set the level every voice is scaled by.
|
||||
* @param level Master level, 0.0 to 1.0.
|
||||
*
|
||||
* Applied after the voices are summed and before the mix is clamped, so it
|
||||
* takes effect on notes already sounding. akgl_audio_shutdown() puts it back
|
||||
* to 1.0.
|
||||
*
|
||||
* @param level Master level, 0.0 to 1.0 inclusive. 0.0 is silence.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_OUTOFBOUNDS When the corresponding validation or operation fails.
|
||||
* @throws AKERR_OUTOFBOUNDS If @p level is outside 0.0 to 1.0. The message
|
||||
* reports it.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_volume(float32_t level);
|
||||
|
||||
@@ -177,11 +215,16 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_volume(float32_t level);
|
||||
* A voice goes quiet on its own when its gate and release have both elapsed, so
|
||||
* this is how a caller waits out a note without keeping its own clock.
|
||||
*
|
||||
* @param voice Zero-based voice index.
|
||||
* @param active Output destination set to `true` while the voice is sounding.
|
||||
* @param voice Zero-based voice index, 0 to #AKGL_AUDIO_MAX_VOICES - 1.
|
||||
* @param active Receives `true` while the voice is sounding, including during
|
||||
* its release. Required -- the return value is the error context.
|
||||
* The flag is only cleared by the mixer, so a voice whose time is
|
||||
* up still reads active until samples are next generated; with no
|
||||
* device open and nobody calling akgl_audio_mix(), it never
|
||||
* changes.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_OUTOFBOUNDS When the corresponding validation or operation fails.
|
||||
* @throws AKERR_OUTOFBOUNDS If @p voice is outside the table.
|
||||
* @throws AKERR_NULLPOINTER If @p active is `NULL`.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_voice_active(int voice, bool *active);
|
||||
|
||||
@@ -193,11 +236,19 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_voice_active(int voice, bool *acti
|
||||
* open a device here at all. Samples are single-precision, one channel, in the
|
||||
* range -1.0 to 1.0, and every active voice is advanced by @p frames.
|
||||
*
|
||||
* @param dest Output destination populated with @p frames samples.
|
||||
* @param frames Number of samples to generate.
|
||||
* @param dest Receives @p frames samples. Required, and must have room for all
|
||||
* of them -- the count is trusted, not checked against anything.
|
||||
* Overwritten, not accumulated into.
|
||||
* @param frames How many samples to generate. 0 is a no-op, not an error.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_OUTOFBOUNDS When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER If @p dest is `NULL`.
|
||||
* @throws AKERR_OUTOFBOUNDS If @p frames is negative.
|
||||
*
|
||||
* @warning This mutates the voice table -- it advances each voice's frame
|
||||
* counter and clears `active` on voices that have finished. Calling it
|
||||
* while a device opened by akgl_audio_init() is running means two
|
||||
* threads advancing the same voices, and this path does not take the
|
||||
* stream lock. Use one or the other, not both.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_mix(float32_t *dest, int frames);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user