kobodl/sound/a_agw.h
Ville Lindholm dbc223eb84
Initial commit
Import existing source tree; original VCS history is no longer available.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-28 16:35:31 +03:00

360 lines
11 KiB
C

/*(LGPL)
---------------------------------------------------------------------------
a_agw.h - "Algorithmically Generated Waveform" file support
---------------------------------------------------------------------------
* Copyright (C) 2002, 2003, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* The AGW v0.1 file format:
*
* This extension to the EEL language makes it possible
* to use EEL scripts to construct waveforms using the
* "Waveform Construction API" of the engine.
*
* Why!?
* Well, pages of hard-coded calls to the Waveform
* Construction API and recompiling for every turn
* while creating sounds got boring, so I decided to
* hack up something simple to get cleaner code and
* faster sound editing.
*
* Over a few weeks, the file format turned into an
* interpreted language, which I eventually cleaned up
* and separated from the engine. This language is
* called "EEL" - Extensible Embeddable Language, and
* is not tied to the audio engine.
*
* Built-in commands:
* //
* C++ style comment.
*
*/ /* ... */
/* C style comment.
*
* w_reset;
* Resets the AGW engine state to the default.
* This is done automatically before a script
* is executed after being loaded with w_load().
* See below for default settings for w_env etc.
*
* w_format <wid>, <sformat>, <samplerate>;
* Set format of <wid> to <sformat>;
* <samplerate> samples per second.
*
* Supported sample formats:
* MONO8 STEREO8
* MONO16 STEREO16
*
* w_blank <wid>, <samples>, <loop>;
* Create <samples> samples of silent data
* for waveform <wid>. <loop> can be either 0
* or 1, and if it's 1, the waveform will be
* set up for looping playback.
*
* w_env <mod_target>[, <time>, <level>[, <time>, <level>...]];
* w_env <mod_target>, <level>;
* Program the envelope generator modulating
* <mod_target> with the specified points.
* Times are in seconds, and levels are in Hz
* for frequency envelopes, while for other
* envelopes, 1.0 corresponds to 0 dB, "no
* change" etc.
*
* Calling w_env with two arguments sets up
* modulator 'mod_target' to the fixed level of
* the second argument - which obviously is *not*
* a duration in this case.
*
* Calling w_env with no other arguments than
* 'mod_target' resets the envelope generator
* for that destination to the default.
*
* Available modulation targets:
* (Defaults are constant levels.)
*
* Name Controls Default
* ---------------------------------------------
* AMPLITUDE Amplitude 1.0
* BALANCE Mixing control 0.0
* FREQUENCY f0/cut-off 100.0
* LIMIT Max overtone freq. 100000.0
* MOD1 PW ratio/FM depth/... 0.0
* MOD2 Various stuff... 0.0
* MOD3 Various stuff... 0.0
*
TODO:
* w_env_taper <mod_target>, <x_coeff>[, <x2_coeff>[, <x3_coeff>]];
* Set transformation of the envelope for target
* 'mod_target' to the function:
*
* out = in * x_coeff +
* in*in * x2_coeff +
* in*in*in * x3_coeff;
*
* Examples:
* w_env_taper FREQUENCY, 1;
* w_env_taper AMPLITUDE, .5, .25, .25;
*
* w_mod <destination>, <frequency>, <amplitude>, <depth>;
* If f(t) is the envelope as defined with
* audio_wave_env(), and w(t) is the modulator phase,
* the final result will be
*
* sin(w(t)) * amplitude + f(t) * (1.0 + sin(w(t)) * depth)
*
* That is, the modulation will add a sine with
* 'amplitude' to the envelope, and it will also add
* a sine with an amplitude of 'depth', scaled by
* the envelope. (Or if you will, the second component
* offset by +1.0 will modulate the envelope.)
*
* w_osc <wid>, <waveform>, <mode>;
* Apply a sound using <mode> to waveform <wid>,
* generated using a <waveform> oscillator.
* Various parameters of the oscillator may
* be modulated.
*
* Available modes:
* WCA_ADD Add oscillator output to buffer
* WCA_MUL AM or ring modulate output with buffer
* ==> BALANCE: 0 ==> no effect
* 0.5 ==> AM
* 1.0 ==> Ring modulation
*
* WCA_FM FM oscillator with buffer, then overwrite
* the buffer with the result. Note that
* it's *not* the buffer that is modulated!
* ==> BALANCE controls FM depth.
*
* WCA_FM_ADD Like AMM_FM, but adds the result to the
* buffer instead of overwriting it.
* ==> BALANCE controls FM depth.
*
* WCA_SYNC Reset oscillator phase when a + -> -
* zero crossing is detected in the
* buffer. Overwrites the buffer with the
* result.
*
* WCA_SYNC_ADD Like AMM_SYNC, but adds the result to
* the buffer instead of overwriting it.
*
* Available waveforms:
* DC Add DC offset
*
* SINE
* ==> MOD1 sets 1.0f FM depth
*
* HALFSINE Half wave rectified sine
* ==> MOD1 sets zero clip level
*
* RECTSINE Full wave rectified sine
* ==> MOD1 sets zero limit level
*
* PULSE Oversampled PWM pulse oscillator.
* ==> MOD1 sets pulse width; 0.5 <==> square
*
* TRIANGLE
* ==> MOD1 ==> ramp up...tri...ramp down
*
* SINEMORPH Sine/square/sawtooth morph, using
* recursive FM. If the sum of MOD1 and
* MOD2 exceeds 1.0, they are balanced
* and scaled to restrict the total to
* 1.0 while preserving the saw/square
* ratio. MOD1 and MOD2 are also scaled
* according to the FREQUENCY/LIMIT
* ratio, to approximate bandlimiting.
* ==> MOD1 sets saw "amount"
* ==> MOD2 sets square "amount"
*
* BLMORPH Bandlimited sine/saw/square/triangle
* oscillator, based on additive synthesis
* using a bank of sine oscillators.
* The harmonics for the respective
* waveforms are scaled by MOD1, MOD2 and
* MOD3, and then are summed. The output
* is bandlimited to the frequency set by
* LIMIT, with a roll-off giving a
* harmonic at LIMIT a 12 dB attenuation.
* ==> MOD1 modulates sawtooth harmonics
* ==> MOD2 modulates square harmonics
* ==> MOD3 modulates triangle harmonics
*
* BLCROSS Bandlimited sine/saw/square/triangle
* oscillator, based on additive synthesis
* using a bank of sine oscillators.
* The harmonics for the respective
* waveforms are bandlimited by the
* respective MODn mapped from [0, 1] to
* [FREQUENCY, LIMIT], with a roll-off
* towards the high end, giving the
* highest harmonic of each waveform a
* 12 dB attenuation.
* ==> MOD1 controls sawtooth cut-off
* ==> MOD2 controls square cut-off
* ==> MOD3 controls triangle cut-off
*
* NOISE "White" noise (6581/SID style :-)
* The noise is generated by a pseudo
* random number generator that is polled
* for a new value at a rate of 2 * f, to
* achieve a minimum period corresponding
* to FREQUENCY. The pseudo random number
* generator is reset at the start of the
* waveform, to guarantee consistent
* results. (Obviously, this means that
* applying noise more than once is rather
* pointless, as it would just increase
* the amplitude!)
* ==> MOD1 sets *non-bandlimited* LP cutoff
*
* SPECTRUM Multiplying frequency spectrum.
* Overtone frequencies are calculated by
* recursively multiplying with MOD1.
* ==> f[n] = f[n-1] * MOD1
* ==> a[n] = a[n-1] * MOD2
*
* ASPECTRUM Adding frequency spectrum. MOD1 is
* the distance in Hz between adjacent
* overtones. As you might expect, an
* ASPECTRUM usually *will* hit the
* maximum # of overtones limit, unless
* you track it with LIMIT properly!
* ==> f[n] = f[n-1] + MOD1
* ==> a[n] = a[n-1] * MOD2
*
* HSPECTRUM Harmonic frequency spectrum with
* separate modulation of even and odd
* harmonics.
* ==> f[n] = f[1] * n
* ==> (Even n) a[n] = a[n-1] * MOD1
* ==> (Odd n) a[n] = a[n-1] * MOD2
*
* PHSPECTRUM Pseudo-Harmonic frequency spectrum.
* Basically a HSPECTRUM, where you
* redefine "harmonic" using MOD1. :-)
* MOD2 and MOD3 control even and odd
* harmonics respectively.
* ==> f[n] = f[1] * n * MOD1
* ==> (Even n) a[n] = a[n-1] * MOD2
* ==> (Odd n) a[n] = a[n-1] * MOD3
*
* w_filter <wid>, <type>;
* Apply filter of <type> to waveform <wid>.
*
* Supported filter types:
* ALLPASS (Currently a NOP)
* LOWPASS_6
* HIGHPASS_6
* LOWPASS_12
* HIGHPASS_12
* BANDPASS_12
* NOTCH_12
* PEAK_12
*
* w_gain <wid>;
* Scale the amplitude of waveform <wid> using the
* AMPLITUDE envelope.
*
*
* w_load <wid>, <filename>, <looped>;
*
* WARNING: This command is recursive!
*
* Load waveform, or load and execute AGW file
* <filename>, and put the result in waveform
* <wid>. It's recommended to pass either
* 'target', something calculated from 'target'
* (when loading "banks" of sounds), or 'tempN'
* for <wid>, although it's possible (but usually
* very nasty!) to specify your own waveform IDs.
FIXME:
The 'looped' argument is a remainder of the
old engine code, and should be replaced with
something sensible.
*
* Note that in the current implementation, there
* is no notion of "scope" - "loading" an AGW
* script from within another AGW script treats
* all variables as globals in all respects.
*
* w_save <wid>, <filename>;
* Save the rendered waveform 'wid' as 'filename'.
*
* w_convert <from_wid>, <to_wid>, <format>[, <rate>[, <resamp>]];
* Convert 'from_wid' into 'format' and 'rate'
* using resampling method 'resamp' and render the
* result into 'to_wid'. Leaving out the rate, or
* setting it to 0 keeps the original rate. Leaving
* out 'inter' uses the generally best interpolation
* method available.
*
* Supported resampling methods:
* NEAREST Nearest sample ("useless")
* NEAREST4X Nearest with 4x oversampling
* LINEAR Linear Interpolation
* LINEAR2X LI w/ 2x oversampling
* LINEAR4X LI w/ 4x oversampling
* LINEAR8X LI w/ 8x oversampling
* LINEAR16X LI w/ 16x oversampling
* CUBIC Cubic interpolation
*
* WORST Worst available method
* MEDIUM Good performance/quality
* BEST Best available method
*
* w_enhance <wid>, <from_f>, <level>;
*
* w_gate <wid>, <cutoff>, <min>, <threshold>, <attenuation>;
*
* (See eel/eel.h for commands that are part of EEL.)
*
* w_mix <from_wid>, <to_wid>;
* Mix waveform <from_wid> into waveform <to_wid>.
* The amplitude envelope controls the mixing
* level, and the frequency envelope controls the
* playback frequency of the source waveform.
*
*/
#ifndef _A_AGW_H_
#define _A_AGW_H_
#ifdef __cplusplus
extern "C" {
#endif
int agw_open(void);
void agw_close(void);
/*
* Load and parse AGW script 'name',
* suggesting that the result be put
* in waveform 'wid'.
*
* Returns the actual id of the generated
* waveform, or -1 upon failure.
*/
int agw_load(int wid, const char *name);
#ifdef __cplusplus
};
#endif
#endif /* _A_AGW_H_ */