Skip to content

Commit c72b5ab

Browse files
committed
Add support for audio channel remapping.
The AudioServer processes audio samples as a number of stereo channels, which are copied to the audio driver buffers. This commit adds the ability to remap AudioServer channels to different driver channels, primarily for debugging purposes. The way it is configured is with get/set methods on AudioServer, exposed in GDScript/C#. This depends on the speaker_mode override PR godotengine#105934. It only allows for remapping the stereo channels Godot works with, and not for mixing left/right samples between channels.
1 parent e195f67 commit c72b5ab

File tree

3 files changed

+72
-3
lines changed

3 files changed

+72
-3
lines changed

doc/classes/AudioServer.xml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,19 @@
118118
[b]Note:[/b] The returned value is equivalent to the result of [method @GlobalScope.db_to_linear] on the result of [method get_bus_volume_db].
119119
</description>
120120
</method>
121+
<method name="get_channel_remap" qualifiers="const">
122+
<return type="int" />
123+
<param index="0" name="output_channel" type="int" />
124+
<description>
125+
Returns the mapping for the stereo channel index at [param output_channel]. Channel index values correspond to the following stereo channels:
126+
- [code]0[/code]: Front left and right
127+
- [code]1[/code]: Center and LFE (bass)
128+
- [code]2[/code]: Back left and right
129+
- [code]3[/code]: Side left and right
130+
- [code]-1[/code]: Muted
131+
The default is for the [param output_channel] to map to the same corresponding internal stereo channel.
132+
</description>
133+
</method>
121134
<method name="get_driver_name" qualifiers="const">
122135
<return type="String" />
123136
<description>
@@ -322,6 +335,19 @@
322335
[b]Note:[/b] Using this method is equivalent to calling [method set_bus_volume_db] with the result of [method @GlobalScope.linear_to_db] on a value.
323336
</description>
324337
</method>
338+
<method name="set_channel_remap">
339+
<return type="void" />
340+
<param index="0" name="output_channel" type="int" />
341+
<param index="1" name="source_channel" type="int" />
342+
<description>
343+
Given a stereo channel index [param output_channel], configures the [AudioServer] to map it to the internal channel [param source_channel]. Channel index values correspond to the following stereo channels:
344+
- [code]0[/code]: Front left and right
345+
- [code]1[/code]: Center and LFE (bass)
346+
- [code]2[/code]: Back left and right
347+
- [code]3[/code]: Side left and right
348+
Setting an output channel to an invalid source channel (e.g. [code]-1[/code]) will mute output from that channel. If the current [method get_speaker_mode] does not use a particular channel, mapping that source channel to an output channel will mute the output channel.
349+
</description>
350+
</method>
325351
<method name="set_enable_tagging_used_audio_streams">
326352
<return type="void" />
327353
<param index="0" name="enable" type="bool" />

servers/audio_server.cpp

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,12 +293,14 @@ void AudioServer::_driver_process(int p_frames, int32_t *p_buffer) {
293293
// The destination start for data will be the same in all cases.
294294
int32_t *dest = &p_buffer[from_buf * (cs * 2) + (k * 2)];
295295

296+
int rk = channel_remap[k];
297+
296298
#ifdef DEBUG_ENABLED
297-
if (!debug_mute && k < mcs && master->channels[k].active) {
299+
if (!debug_mute && rk < mcs && master->channels[rk].active) {
298300
#else
299-
if (k < mcs && master->channels[k].active) {
301+
if (rk < mcs && master->channels[rk].active) {
300302
#endif // DEBUG_ENABLED
301-
const AudioFrame *buf = master->channels[k].buffer.ptr();
303+
const AudioFrame *buf = master->channels[rk].buffer.ptr();
302304

303305
for (int j = 0; j < to_copy; j++) {
304306
float l = CLAMP(buf[from + j].left, -1.0, 1.0);
@@ -1504,6 +1506,38 @@ int AudioServer::get_driver_channel_count() const {
15041506
ERR_FAIL_V(1);
15051507
}
15061508

1509+
void AudioServer::set_channel_remap(int p_output_channel, int p_source_channel) {
1510+
ERR_FAIL_INDEX_MSG(p_output_channel, MAX_CHANNELS_PER_BUS, "Invalid output channel, out of range");
1511+
bool remap_changed;
1512+
1513+
lock();
1514+
if (p_source_channel < 0 || p_source_channel > MAX_CHANNELS_PER_BUS) {
1515+
remap_changed = channel_remap[p_output_channel] != MAX_CHANNELS_PER_BUS;
1516+
1517+
// Source channels outside valid range will get muted.
1518+
channel_remap[p_output_channel] = MAX_CHANNELS_PER_BUS;
1519+
} else {
1520+
remap_changed = channel_remap[p_output_channel] != p_source_channel;
1521+
channel_remap[p_output_channel] = p_source_channel;
1522+
}
1523+
unlock();
1524+
1525+
if (remap_changed) {
1526+
print_verbose(vformat("AudioServer: channel remap changed: output channel [%d] -> source channel [%d]", p_output_channel, channel_remap[p_output_channel]))
1527+
}
1528+
}
1529+
1530+
int AudioServer::get_channel_remap(int p_output_channel) const {
1531+
ERR_FAIL_INDEX_V_MSG(p_output_channel, MAX_CHANNELS_PER_BUS, -1, "Invalid output channel, out of range");
1532+
return channel_remap[p_output_channel] == MAX_CHANNELS_PER_BUS ? -1 : channel_remap[p_output_channel];
1533+
}
1534+
1535+
void AudioServer::init_channel_remap() {
1536+
for (int i = 0; i < MAX_CHANNELS_PER_BUS; i++) {
1537+
channel_remap[i] = i;
1538+
}
1539+
}
1540+
15071541
void AudioServer::notify_listener_changed() {
15081542
for (CallbackItem *ci : listener_changed_callback_list) {
15091543
ci->callback(ci->userdata);
@@ -1536,6 +1570,7 @@ void AudioServer::init() {
15361570
channel_disable_frames = float(GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/buses/channel_disable_time", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 2.0)) * get_mix_rate();
15371571

15381572
speaker_mode_config = GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/general/speaker_mode", PROPERTY_HINT_ENUM, "Default:-1,Stereo:0,Surround 3.1:1,Surround 5.1:2,Surround 7.1:3"), -1);
1573+
init_channel_remap();
15391574

15401575
// TODO: Buffer size is hardcoded for now. This would be really nice to have as a project setting because currently it limits audio latency to an absolute minimum of 11ms with default mix rate, but there's some additional work required to make that happen. See TODOs in `_mix_step_for_channel`.
15411576
// When this becomes a project setting, it should be specified in milliseconds rather than raw sample count, because 512 samples at 192khz is shorter than it is at 48khz, for example.
@@ -2048,6 +2083,9 @@ void AudioServer::_bind_methods() {
20482083
ClassDB::bind_method(D_METHOD("set_bus_effect_enabled", "bus_idx", "effect_idx", "enabled"), &AudioServer::set_bus_effect_enabled);
20492084
ClassDB::bind_method(D_METHOD("is_bus_effect_enabled", "bus_idx", "effect_idx"), &AudioServer::is_bus_effect_enabled);
20502085

2086+
ClassDB::bind_method(D_METHOD("set_channel_remap", "output_channel", "source_channel"), &AudioServer::set_channel_remap);
2087+
ClassDB::bind_method(D_METHOD("get_channel_remap", "output_channel"), &AudioServer::get_channel_remap);
2088+
20512089
ClassDB::bind_method(D_METHOD("get_bus_peak_volume_left_db", "bus_idx", "channel"), &AudioServer::get_bus_peak_volume_left_db);
20522090
ClassDB::bind_method(D_METHOD("get_bus_peak_volume_right_db", "bus_idx", "channel"), &AudioServer::get_bus_peak_volume_right_db);
20532091

servers/audio_server.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ class AudioServer : public Object {
225225
int to_mix = 0;
226226

227227
int speaker_mode_config = -1;
228+
int channel_remap[MAX_CHANNELS_PER_BUS];
228229

229230
float playback_speed_scale = 1.0f;
230231

@@ -329,6 +330,7 @@ class AudioServer : public Object {
329330
static AudioServer *singleton;
330331

331332
void init_channels_and_buffers();
333+
void init_channel_remap();
332334

333335
void _mix_step();
334336
void _mix_step_for_channel(AudioFrame *p_out_buf, AudioFrame *p_source_buf, AudioFrame p_vol_start, AudioFrame p_vol_final, float p_attenuation_filter_cutoff_hz, float p_highshelf_gain, AudioFilterSW::Processor *p_processor_l, AudioFilterSW::Processor *p_processor_r);
@@ -430,6 +432,9 @@ class AudioServer : public Object {
430432

431433
bool is_bus_channel_active(int p_bus, int p_channel) const;
432434

435+
void set_channel_remap(int p_output_channel, int p_source_channel);
436+
int get_channel_remap(int p_output_channel) const;
437+
433438
void set_playback_speed_scale(float p_scale);
434439
float get_playback_speed_scale() const;
435440

0 commit comments

Comments
 (0)