Skip to content

Commit

Permalink
allow setting audio track position and volume and document audio tracks
Browse files Browse the repository at this point in the history
  • Loading branch information
ianmaclarty committed Sep 27, 2018
1 parent 9c3a748 commit 20ccc1f
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 27 deletions.
46 changes: 39 additions & 7 deletions doc/audio.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ will currently only play up to two channels.

An audio buffer also has a sample rate. Amulet currently plays all
audio at a sample rate 44.1kHz and will resample an audio buffer
if it has a different sample rate.
if it has a different sample rate (this requires extra processing).

The audio data itself is stored in a raw [buffer](#buffers-and-views)
as a series of single precision floats (4 bytes each). The samples for
Expand Down Expand Up @@ -66,15 +66,49 @@ The length of the audio in seconds. Readonly.

The underlying raw [buffer](#buffers-and-views) where the audio data is stored. Readonly.

## Audio tracks {#audio-tracks}

An audio track contains the playback state of an audio buffer - that is
the current position and speed of playback. The same audio buffer can
be used with multiple audio tracks.

Note that you normally don't need to explicitly create tracks
to play audio, unless you want to change the speed and position
during playback.

### am.track(buffer [, loop [, playback_speed [, volume]]]) {#am.track .func-def}

Creates a new track with the given buffer, loop setting (`true`/`false`),
playback_speed and volume. This doesn't cause the track to start playing, use
[`am.play`](#am.play) for that.

## Audio track fields

### track.playback_speed {#track.playback_speed .field-def}

Playback speed as a multiplier of the normal speed (e.g. 0.5 means half
speed and 2 means double speed). Updatable.

### track.volume {#track.volume .field-def}

Playback volume (1 is normal volume). Updatable.

## Audio track methods

### track:reset([position]) {#track:reset .method-def}

Sets playback to the given position, measured in seconds.
If the argument is omitted, playback is reset to the start.

## Playing audio

### am.play(source, loop, pitch, gain)
### am.play(source, loop, pitch, volume) {#am.play .func-def}

Returns an action that plays audio.
Like any other [action](#node:action) it needs to be attached to a scene
node that's connected to a window to run.

`source` can either be an [audio buffer](#audio-buffers), the name of a ".ogg" file
`source` can either be an [audio track](#audio-tracks), an [audio buffer](#audio-buffers), the name of a ".ogg" file
or a seed generated by the sfxr tool (there's an online
version of that tool in the examples list of the [online editor](http://www.amulet.xyz/editor.html)).

Expand All @@ -84,7 +118,7 @@ version of that tool in the examples list of the [online editor](http://www.amul
1.0 mean play at the original speed. 2.0 means play
twice as fast and 0.5 means play at half the original speed.

`gain` should be between 0 and 1.
`volume` should be between 0 and 1.

**Note**:
When the source is an sfxr seed or a filename, an [audio buffer](#audio-buffers)
Expand Down Expand Up @@ -139,9 +173,7 @@ in the [online editor](http://www.amulet.xyz/editor.html) to generate seeds.
TODO

(Amulet has support for building graphs of audio effect nodes,
however it is currently undocumented because it will probably
be changing shortly to use syntax more consistent with the
way scene graphs are constructed.)
however it is currently undocumented because the API is unstable).

## Audio graph nodes

Expand Down
33 changes: 28 additions & 5 deletions doc/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -379,8 +379,18 @@
<li><a href="#audio_buffer.length">audio_buffer.length</a></li>
<li><a href="#audio_buffer.buffer">audio_buffer.buffer</a></li>
</ul></li>
<li><a href="#audio-tracks">Audio tracks</a><ul>
<li><a href="#am.track">am.track(buffer [, loop [, playback_speed [, volume]]])</a></li>
</ul></li>
<li><a href="#audio-track-fields">Audio track fields</a><ul>
<li><a href="#track.playback_speed">track.playback_speed</a></li>
<li><a href="#track.volume">track.volume</a></li>
</ul></li>
<li><a href="#audio-track-methods">Audio track methods</a><ul>
<li><a href="#track:reset">track:reset([position])</a></li>
</ul></li>
<li><a href="#playing-audio-1">Playing audio</a><ul>
<li><a href="#am.playsource-loop-pitch-gain">am.play(source, loop, pitch, gain)</a></li>
<li><a href="#am.play">am.play(source, loop, pitch, volume)</a></li>
</ul></li>
<li><a href="#generating-sound-effects">Generating sound effects</a><ul>
<li><a href="#am.sfxr_synth">am.sfxr_synth(settings)</a></li>
Expand Down Expand Up @@ -2908,7 +2918,7 @@ <h1 id="audio">Audio</h1>
<h2 id="audio-buffers">Audio buffers</h2>
<p>An audio buffer is a block of memory that stores an uncompressed audio sample.</p>
<p>An audio buffer may have any number of channels, although Amulet will currently only play up to two channels.</p>
<p>An audio buffer also has a sample rate. Amulet currently plays all audio at a sample rate 44.1kHz and will resample an audio buffer if it has a different sample rate.</p>
<p>An audio buffer also has a sample rate. Amulet currently plays all audio at a sample rate 44.1kHz and will resample an audio buffer if it has a different sample rate (this requires extra processing).</p>
<p>The audio data itself is stored in a raw <a href="#buffers-and-views">buffer</a> as a series of single precision floats (4 bytes each). The samples for each channel are contiguous. The first channel's data comes first, then the second channel's data, etc (the channels are not interleaved).</p>
<p>If the sample data is stored in the buffer <code>buf</code>, and there are two channels, then the following code will create <a href="#buffers-and-views">views</a> that can be used to update or read the samples for each channel:</p>
<div class="sourceCode"><pre class="sourceCode lua"><code class="sourceCode lua"><span class="kw">local</span> c <span class="ot">=</span> <span class="dv">2</span> <span class="do">-- channels</span>
Expand All @@ -2931,13 +2941,26 @@ <h3 id="audio_buffer.length" class="field-def">audio_buffer.length</h3>
<p>The length of the audio in seconds. Readonly.</p>
<h3 id="audio_buffer.buffer" class="field-def">audio_buffer.buffer</h3>
<p>The underlying raw <a href="#buffers-and-views">buffer</a> where the audio data is stored. Readonly.</p>
<h2 id="audio-tracks">Audio tracks</h2>
<p>An audio track contains the playback state of an audio buffer - that is the current position and speed of playback. The same audio buffer can be used with multiple audio tracks.</p>
<p>Note that you normally don't need to explicitly create tracks to play audio, unless you want to change the speed and position during playback.</p>
<h3 id="am.track" class="func-def">am.track(buffer [, loop [, playback_speed [, volume]]])</h3>
<p>Creates a new track with the given buffer, loop setting (<code>true</code>/<code>false</code>), playback_speed and volume. This doesn't cause the track to start playing, use <a href="#am.play"><code>am.play</code></a> for that.</p>
<h2 id="audio-track-fields">Audio track fields</h2>
<h3 id="track.playback_speed" class="field-def">track.playback_speed</h3>
<p>Playback speed as a multiplier of the normal speed (e.g. 0.5 means half speed and 2 means double speed). Updatable.</p>
<h3 id="track.volume" class="field-def">track.volume</h3>
<p>Playback volume (1 is normal volume). Updatable.</p>
<h2 id="audio-track-methods">Audio track methods</h2>
<h3 id="track:reset" class="method-def">track:reset([position])</h3>
<p>Sets playback to the given position, measured in seconds. If the argument is omitted, playback is reset to the start.</p>
<h2 id="playing-audio-1">Playing audio</h2>
<h3 id="am.playsource-loop-pitch-gain">am.play(source, loop, pitch, gain)</h3>
<h3 id="am.play" class="func-def">am.play(source, loop, pitch, volume)</h3>
<p>Returns an action that plays audio. Like any other <a href="#node:action">action</a> it needs to be attached to a scene node that's connected to a window to run.</p>
<p><code>source</code> can either be an <a href="#audio-buffers">audio buffer</a>, the name of a &quot;.ogg&quot; file or a seed generated by the sfxr tool (there's an online version of that tool in the examples list of the <a href="http://www.amulet.xyz/editor.html">online editor</a>).</p>
<p><code>source</code> can either be an <a href="#audio-tracks">audio track</a>, an <a href="#audio-buffers">audio buffer</a>, the name of a &quot;.ogg&quot; file or a seed generated by the sfxr tool (there's an online version of that tool in the examples list of the <a href="http://www.amulet.xyz/editor.html">online editor</a>).</p>
<p><code>loop</code> can be <code>true</code> or <code>false</code>.</p>
<p><code>pitch</code> is a multiplier applied to the playback speed. 1.0 mean play at the original speed. 2.0 means play twice as fast and 0.5 means play at half the original speed.</p>
<p><code>gain</code> should be between 0 and 1.</p>
<p><code>volume</code> should be between 0 and 1.</p>
<p><strong>Note</strong>: When the source is an sfxr seed or a filename, an <a href="#audio-buffers">audio buffer</a> is generated behind the scenes and cached. Therefore there might be a short delay the first time this function is called with a given seed or filename (though for short audio clips this will probably not be noticeable). Also avoid calling this function many times with different seeds or filenames, because that will cause unbounded memory growth.</p>
<h2 id="generating-sound-effects">Generating sound effects</h2>
<h3 id="am.sfxr_synth" class="func-def">am.sfxr_synth(settings)</h3>
Expand Down
27 changes: 24 additions & 3 deletions src/am_audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ am_audio_track_node::am_audio_track_node()
audio_buffer_ref = LUA_NOREF;
current_position = 0.0;
next_position = 0.0;
reset_position = 0.0;
loop = false;
needs_reset = false;
done_server = false;
Expand All @@ -420,8 +421,8 @@ void am_audio_track_node::sync_params() {
playback_speed.update_target();
gain.update_target();
if (needs_reset) {
current_position = 0.0;
next_position = 0.0;
current_position = reset_position;
next_position = reset_position;
needs_reset = false;
done_server = false;
}
Expand Down Expand Up @@ -1091,9 +1092,16 @@ static int create_audio_track_node(lua_State *L) {
}

static int reset_track(lua_State *L) {
am_check_nargs(L, 1);
int nargs = am_check_nargs(L, 1);
am_audio_track_node *node = am_get_userdata(L, am_audio_track_node, 1);
node->needs_reset = true;
if (nargs > 1) {
int buf_num_channels = node->audio_buffer->num_channels;
int buf_num_samples = node->audio_buffer->buffer->size / (buf_num_channels * sizeof(float));
node->reset_position = am_min(luaL_checknumber(L, 2) * node->audio_buffer->sample_rate, (double)(buf_num_samples-1));
} else {
node->reset_position = 0.0;
}
return 0;
}

Expand All @@ -1109,6 +1117,18 @@ static void set_track_playback_speed(lua_State *L, void *obj) {

static am_property track_playback_speed_property = {get_track_playback_speed, set_track_playback_speed};

static void get_track_volume(lua_State *L, void *obj) {
am_audio_track_node *node = (am_audio_track_node*)obj;
lua_pushnumber(L, node->gain.pending_value);
}

static void set_track_volume(lua_State *L, void *obj) {
am_audio_track_node *node = (am_audio_track_node*)obj;
node->gain.pending_value = luaL_checknumber(L, 3);
}

static am_property track_volume_property = {get_track_volume, set_track_volume};

static void register_audio_track_node_mt(lua_State *L) {
lua_newtable(L);
lua_pushcclosure(L, am_audio_node_index, 0);
Expand All @@ -1119,6 +1139,7 @@ static void register_audio_track_node_mt(lua_State *L) {
lua_setfield(L, -2, "reset");

am_register_property(L, "playback_speed", &track_playback_speed_property);
am_register_property(L, "volume", &track_volume_property);

am_register_metatable(L, "track", MT_am_audio_track_node, MT_am_audio_node);
}
Expand Down
13 changes: 1 addition & 12 deletions src/am_audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,18 +114,6 @@ struct am_gain_node : am_audio_node {
virtual void post_render(am_audio_context *context, int num_samples);
};

struct am_filter_node : am_audio_node {
am_audio_param<float> low_freq;
am_audio_param<float> high_freq;
float low_state[AM_MAX_CHANNELS];
float high_state[AM_MAX_CHANNELS];

am_filter_node();
virtual void sync_params();
virtual void render_audio(am_audio_context *context, am_audio_bus *bus);
virtual void post_render(am_audio_context *context, int num_samples);
};

struct am_biquad_filter_coeffs {
double b0;
double b1;
Expand Down Expand Up @@ -185,6 +173,7 @@ struct am_audio_track_node : am_audio_node {

double current_position;
double next_position;
double reset_position;

am_audio_track_node();
virtual void sync_params();
Expand Down

0 comments on commit 20ccc1f

Please sign in to comment.