Skip to content

Voice

Warning

Prerequisites:

Any code from the prerequisites can be omitted to make it easier to read. If you do want the complete code, look at the repository examples.

Create/Join

GDScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
extends Node


var application_id: int = 123456789012345678

var client := DiscordClient.new()


func _ready() -> void:
    client.set_application_id(application_id)
    client.set_status_changed_callback(_on_status_changed)


func _process(_delta: float) -> void:
    Discord.run_callbacks()


func _on_status_changed(status: DiscordClientStatus.Enum, _error: DiscordClientError.Enum, _error_detail: int) -> void:
    var enum_str: String = Discord.enum_to_string(status, DiscordClientStatus.id)

    print("Status changed to %s" % enum_str)

    if status == DiscordClientStatus.READY:
        client.create_or_join_lobby("your-unique-lobby-secret", _on_joined_lobby)


func _on_joined_lobby(result: DiscordClientResult, lobby_id: int) -> void:
    if result.successful():
        print("🎮 Successfully joined lobby!")

        var call: DiscordCall = client.start_call(lobby_id)

        if call:
            print("🎤 Voice call operation initiated...")
        else:
            print("â„šī¸ Already in this voice channel")
    else:
        print("❌ Failed to join lobby: %s" % result.error())

Note

Once you join, you are officially in a voice chat!

Unless you mute, your microphone input will be captured and sent to others.

Controls

We can manipulate the voice settings for a specific call or for all calls.

Specific Call

GDScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
extends Node


var application_id: int = 123456789012345678

var target_id: int = 987654323109876543

var client := DiscordClient.new()


func _ready() -> void:
    client.set_application_id(application_id)
    client.set_status_changed_callback(_on_status_changed)


func _process(_delta: float) -> void:
    Discord.run_callbacks()


func _on_status_changed(status: DiscordClientStatus.Enum, _error: DiscordClientError.Enum, _error_detail: int) -> void:
    var enum_str: String = Discord.enum_to_string(status, DiscordClientStatus.id)

    print("Status changed to %s" % enum_str)

    if status == DiscordClientStatus.READY:
        client.create_or_join_lobby("your-unique-lobby-secret", _on_joined_lobby)


func _on_joined_lobby(result: DiscordClientResult, lobby_id: int) -> void:
    if result.successful():
        print("🎮 Successfully joined lobby!")

        var call: DiscordCall = client.start_call(lobby_id)

        if call:
            print("🎤 Voice call operation initiated...")
        else:
            print("â„šī¸ Already in this voice channel")

        # Let's give a second to simulate interacting from anywhere in your code.
        get_tree().create_timer(1).timeout.connect(_on_call_started.bind(lobby_id))
    else:
        print("❌ Failed to join lobby: %s" % result.error())


func _on_call_started(lobby_id: int) -> void:
    var call: DiscordCall = client.get_call(lobby_id)

    if call:
        call.set_self_mute(true)
        call.set_self_deaf(false)
        call.set_participant_volume(target_id, 150.0)
        call.set_vad_threshold(false, -30.0)

All Calls

GDScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
extends Node


var application_id: int = 123456789012345678

var target_id: int = 987654323109876543

var client := DiscordClient.new()


func _ready() -> void:
    client.set_application_id(application_id)
    client.set_status_changed_callback(_on_status_changed)


func _process(_delta: float) -> void:
    Discord.run_callbacks()


func _on_status_changed(status: DiscordClientStatus.Enum, _error: DiscordClientError.Enum, _error_detail: int) -> void:
    var enum_str: String = Discord.enum_to_string(status, DiscordClientStatus.id)

    print("Status changed to %s" % enum_str)

    if status == DiscordClientStatus.READY:
        client.create_or_join_lobby("your-unique-lobby-secret", _on_joined_lobby)


func _on_joined_lobby(result: DiscordClientResult, lobby_id: int) -> void:
    if result.successful():
        print("🎮 Successfully joined lobby!")

        var call: DiscordCall = client.start_call(lobby_id)

        if call:
            print("🎤 Voice call operation initiated...")
        else:
            print("â„šī¸ Already in this voice channel")

        # Let's give a second to simulate interacting from anywhere in your code.
        get_tree().create_timer(1).timeout.connect(_on_call_started.bind(lobby_id))
    else:
        print("❌ Failed to join lobby: %s" % result.error())


func _on_call_started(lobby_id: int) -> void:
    var call: DiscordCall = client.get_call(lobby_id)

    if call:
        call.set_self_mute(true)
        call.set_self_deaf(false)
        call.set_participant_volume(target_id, 150.0)
        call.set_vad_threshold(false, -30.0)

    client.set_self_mute_all(true)
    client.set_input_volume(75.0)
    client.set_output_volume(120.0)
    client.set_no_audio_input_threshold(-60.0)
    client.set_no_audio_input_callback(_on_audio_crossing_threshold)
    client.set_noise_suppression(true)
    client.set_echo_cancellation(true)
    client.set_automatic_gain_control(true)
    client.set_noise_cancellation(true)


func _on_audio_crossing_threshold(input_detected: bool) -> void:
    if not input_detected:
        print("🔈 Mic appears to be silent — check your device settings.")
    else:
        print("🔊 Mic is receiving audio again")

Audio Processing

Warning

While the SDK provide ways to manipulate audio when received, the GDExtension still doesn't support it.

Changing the values of the variables has no effect in the audio.

GDScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
extends Node


var application_id: int = 123456789012345678

var target_id: int = 987654323109876543

var client := DiscordClient.new()


func _ready() -> void:
    client.set_application_id(application_id)
    client.set_status_changed_callback(_on_status_changed)


func _process(_delta: float) -> void:
    Discord.run_callbacks()


func _on_status_changed(status: DiscordClientStatus.Enum, _error: DiscordClientError.Enum, _error_detail: int) -> void:
    var enum_str: String = Discord.enum_to_string(status, DiscordClientStatus.id)

    print("Status changed to %s" % enum_str)

    if status == DiscordClientStatus.READY:
        client.create_or_join_lobby("your-unique-lobby-secret", _on_joined_lobby)


func _on_joined_lobby(result: DiscordClientResult, lobby_id: int) -> void:
    if result.successful():
        print("🎮 Successfully joined lobby!")

        var call: DiscordCall = client.start_call_with_audio_callbacks(lobby_id, _on_audio_received, _on_audio_captured)

        if call:
            print("🎤 Voice call operation initiated...")
        else:
            print("â„šī¸ Already in this voice channel")
    else:
        print("❌ Failed to join lobby: %s" % result.error())


func _on_audio_received(user_id: int, data: Array[int], samples_per_channel: int, sample_rate: int, channels: int, out_should_mute: bool) -> void:
    # Changing "data" doesn't reflect into SDK.
    for i in data.size():
        data[i] *= 0.5

    # Changing "out_should_mute" doesn't reflect into SDK.
    out_should_mute = true

    var total_num_samples = samples_per_channel * channels


func _on_audio_captured(data: Array[int], samples_per_channel: int, sample_rate: int, channels: int) -> void:
    pass

Leave

Specific Call

GDScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
extends Node


var application_id: int = 123456789012345678

var target_id: int = 987654323109876543

var client := DiscordClient.new()


func _ready() -> void:
    client.set_application_id(application_id)
    client.set_status_changed_callback(_on_status_changed)


func _process(_delta: float) -> void:
    Discord.run_callbacks()


func _on_status_changed(status: DiscordClientStatus.Enum, _error: DiscordClientError.Enum, _error_detail: int) -> void:
    var enum_str: String = Discord.enum_to_string(status, DiscordClientStatus.id)

    print("Status changed to %s" % enum_str)

    if status == DiscordClientStatus.READY:
        client.create_or_join_lobby("your-unique-lobby-secret", _on_joined_lobby)


func _on_joined_lobby(result: DiscordClientResult, lobby_id: int) -> void:
    if result.successful():
        print("🎮 Successfully joined lobby!")

        var call: DiscordCall = client.start_call(lobby_id)

        if call:
            print("🎤 Voice call operation initiated...")
        else:
            print("â„šī¸ Already in this voice channel")

        # Let's give a second to simulate interacting from anywhere in your code.
        get_tree().create_timer(1).timeout.connect(_on_call_started.bind(lobby_id))
    else:
        print("❌ Failed to join lobby: %s" % result.error())


func _on_call_started(lobby_id: int) -> void:
    var call: DiscordCall = client.get_call(lobby_id)

    if call:
        call.set_self_mute(true)
        call.set_self_deaf(false)
        call.set_participant_volume(target_id, 150.0)
        call.set_vad_threshold(false, -30.0)

    client.set_self_mute_all(true)
    client.set_input_volume(75.0)
    client.set_output_volume(120.0)
    client.set_no_audio_input_threshold(-60.0)
    client.set_no_audio_input_callback(_on_audio_crossing_threshold)
    client.set_noise_suppression(true)
    client.set_echo_cancellation(true)
    client.set_automatic_gain_control(true)
    client.set_noise_cancellation(true)

    client.end_call(lobby_id, _on_call_ended)


func _on_audio_crossing_threshold(input_detected: bool) -> void:
    if not input_detected:
        print("🔈 Mic appears to be silent — check your device settings.")
    else:
        print("🔊 Mic is receiving audio again")


func _on_call_ended() -> void:
    print("🔇 Call ended successfully")

All Calls

GDScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
extends Node


var application_id: int = 123456789012345678

var target_id: int = 987654323109876543

var client := DiscordClient.new()


func _ready() -> void:
    client.set_application_id(application_id)
    client.set_status_changed_callback(_on_status_changed)


func _process(_delta: float) -> void:
    Discord.run_callbacks()


func _on_status_changed(status: DiscordClientStatus.Enum, _error: DiscordClientError.Enum, _error_detail: int) -> void:
    var enum_str: String = Discord.enum_to_string(status, DiscordClientStatus.id)

    print("Status changed to %s" % enum_str)

    if status == DiscordClientStatus.READY:
        client.create_or_join_lobby("your-unique-lobby-secret", _on_joined_lobby)


func _on_joined_lobby(result: DiscordClientResult, lobby_id: int) -> void:
    if result.successful():
        print("🎮 Successfully joined lobby!")

        var call: DiscordCall = client.start_call(lobby_id)

        if call:
            print("🎤 Voice call operation initiated...")
        else:
            print("â„šī¸ Already in this voice channel")

        # Let's give a second to simulate interacting from anywhere in your code.
        get_tree().create_timer(1).timeout.connect(_on_call_started.bind(lobby_id))
    else:
        print("❌ Failed to join lobby: %s" % result.error())


func _on_call_started(lobby_id: int) -> void:
    var call: DiscordCall = client.get_call(lobby_id)

    if call:
        call.set_self_mute(true)
        call.set_self_deaf(false)
        call.set_participant_volume(target_id, 150.0)
        call.set_vad_threshold(false, -30.0)

    client.set_self_mute_all(true)
    client.set_input_volume(75.0)
    client.set_output_volume(120.0)
    client.set_no_audio_input_threshold(-60.0)
    client.set_no_audio_input_callback(_on_audio_crossing_threshold)
    client.set_noise_suppression(true)
    client.set_echo_cancellation(true)
    client.set_automatic_gain_control(true)
    client.set_noise_cancellation(true)

    client.end_calls(_on_calls_ended)


func _on_audio_crossing_threshold(input_detected: bool) -> void:
    if not input_detected:
        print("🔈 Mic appears to be silent — check your device settings.")
    else:
        print("🔊 Mic is receiving audio again")


func _on_calls_ended() -> void:
    print("🔇 All calls ended successfully")

Check

Call

GDScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
extends Node


var application_id: int = 123456789012345678

var client := DiscordClient.new()


func _ready() -> void:
    client.set_application_id(application_id)
    client.set_status_changed_callback(_on_status_changed)


func _process(_delta: float) -> void:
    Discord.run_callbacks()


func _on_status_changed(status: DiscordClientStatus.Enum, _error: DiscordClientError.Enum, _error_detail: int) -> void:
    var enum_str: String = Discord.enum_to_string(status, DiscordClientStatus.id)

    print("Status changed to %s" % enum_str)

    if status == DiscordClientStatus.READY:
        client.create_or_join_lobby("your-unique-lobby-secret", _on_joined_lobby)


func _on_joined_lobby(result: DiscordClientResult, lobby_id: int) -> void:
    if result.successful():
        print("🎮 Successfully joined lobby!")

        var lobby = client.get_lobby_handle(lobby_id)

        if lobby is DiscordLobbyHandle:
            var call_info = lobby.get_call_info_handle()

            if call_info is DiscordCallInfoHandle:
                var participants: Array[int] = call_info.get_participants()

                print("Active call with %s participants" % participants.size())
            else:
                print("No active voice call in this lobby")
    else:
        print("❌ Failed to join lobby: %s" % result.error())

Participant

GDScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
extends Node


var application_id: int = 123456789012345678

var client := DiscordClient.new()


func _ready() -> void:
    client.set_application_id(application_id)
    client.set_status_changed_callback(_on_status_changed)


func _process(_delta: float) -> void:
    Discord.run_callbacks()


func _on_status_changed(status: DiscordClientStatus.Enum, _error: DiscordClientError.Enum, _error_detail: int) -> void:
    var enum_str: String = Discord.enum_to_string(status, DiscordClientStatus.id)

    print("Status changed to %s" % enum_str)

    if status == DiscordClientStatus.READY:
        client.create_or_join_lobby("your-unique-lobby-secret", _on_joined_lobby)


func _on_joined_lobby(result: DiscordClientResult, lobby_id: int) -> void:
    if result.successful():
        print("🎮 Successfully joined lobby!")

        var lobby = client.get_lobby_handle(lobby_id)

        if lobby is DiscordLobbyHandle:
            var call_info = lobby.get_call_info_handle()

            if call_info is DiscordCallInfoHandle:
                var participants = call_info.get_participants()

                for participant_id in participants:
                    var voice_state = call_info.get_voice_state_handle(participant_id)

                    if voice_state is DiscordVoiceStateHandle:
                        var is_muted = voice_state.self_mute()
                        var is_deafened = voice_state.self_deaf()

                        print("Participant %s - Muted: %s, Deafened: %s" % [
                            participant_id,
                            "Yes" if is_muted else "No",
                            "Yes" if is_deafened else "No",
                        ])
    else:
        print("❌ Failed to join lobby: %s" % result.error())

Import Settings

Instead of choosing a default voice settings or making the user adjust, we can import the user voice settings from the Discord client.

Fetch

GDScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
extends Node


var application_id: int = 123456789012345678

var client := DiscordClient.new()


func _ready() -> void:
    client.set_application_id(application_id)
    client.get_voice_settings(_on_voice_settings)


func _process(_delta: float) -> void:
    Discord.run_callbacks()


func _on_voice_settings(result: DiscordClientResult, settings: DiscordVoiceSettings) -> void:
    if not result.successful():
        print("❌ Failed to fetch voice settings: %s" % result.error())
        return

    print("Self mute: %s" % settings.self_mute())
    print("Self deaf: %s" % settings.self_deaf())
    print("Input volume: %s" % settings.input_volume()) # 0-100
    print("Output volume: %s" % settings.output_volume()) # 0-200

    # input_mode() is either DiscordVoiceInputModeType.VOICE_ACTIVITY or
    # DiscordVoiceInputModeType.PUSH_TO_TALK
    if settings.input_mode() == DiscordVoiceInputModeType.PUSH_TO_TALK:
        # ptt_key() is a display string, e.g. "SHIFT + F", empty if unbound
        print("Push-to-talk key: %s" % settings.ptt_key())

Changes Notification

GDScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
extends Node


var application_id: int = 123456789012345678

var client := DiscordClient.new()


func _ready() -> void:
    client.set_application_id(application_id)
    client.set_voice_settings_updated_callback(_on_voice_settings_updated)


func _process(_delta: float) -> void:
    Discord.run_callbacks()


func _on_voice_settings_updated(settings: DiscordVoiceSettings) -> void:
    print("🔄 Voice settings updated - self mute: %s" % settings.self_mute())

References