Skip to content

Commit 47c7942

Browse files
Add in the microphone access to the Input object
1 parent a8598cd commit 47c7942

File tree

11 files changed

+119
-18
lines changed

11 files changed

+119
-18
lines changed

core/input/input.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "core/input/default_controller_mappings.h"
3636
#include "core/input/input_map.h"
3737
#include "core/os/os.h"
38+
#include "servers/audio_server.h"
3839

3940
#ifdef DEV_ENABLED
4041
#include "core/os/thread.h"
@@ -170,6 +171,10 @@ void Input::_bind_methods() {
170171
ClassDB::bind_method(D_METHOD("is_emulating_mouse_from_touch"), &Input::is_emulating_mouse_from_touch);
171172
ClassDB::bind_method(D_METHOD("set_emulate_touch_from_mouse", "enable"), &Input::set_emulate_touch_from_mouse);
172173
ClassDB::bind_method(D_METHOD("is_emulating_touch_from_mouse"), &Input::is_emulating_touch_from_mouse);
174+
ClassDB::bind_method(D_METHOD("start_microphone"), &Input::start_microphone);
175+
ClassDB::bind_method(D_METHOD("stop_microphone"), &Input::stop_microphone);
176+
ClassDB::bind_method(D_METHOD("get_microphone_frames_available"), &Input::get_microphone_frames_available);
177+
ClassDB::bind_method(D_METHOD("get_microphone_buffer", "frames"), &Input::get_microphone_buffer);
173178

174179
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_mode"), "set_mouse_mode", "get_mouse_mode");
175180
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_accumulated_input"), "set_use_accumulated_input", "is_using_accumulated_input");
@@ -1837,6 +1842,53 @@ bool Input::is_input_disabled() const {
18371842
return disable_input;
18381843
}
18391844

1845+
Error Input::start_microphone() {
1846+
if (!GLOBAL_GET("audio/driver/enable_input")) {
1847+
WARN_PRINT("You must enable the project setting \"audio/driver/enable_input\" to use audio capture.");
1848+
return FAILED;
1849+
}
1850+
1851+
microphone_buffer_ofs = 0;
1852+
return AudioDriver::get_singleton()->input_start();
1853+
}
1854+
1855+
Error Input::stop_microphone() {
1856+
return AudioDriver::get_singleton()->input_stop();
1857+
}
1858+
1859+
int Input::get_microphone_frames_available() {
1860+
unsigned int input_position = AudioDriver::get_singleton()->get_input_position();
1861+
if (input_position < microphone_buffer_ofs) {
1862+
Vector<int32_t> &buf = AudioDriver::get_singleton()->get_input_buffer();
1863+
input_position += buf.size();
1864+
}
1865+
return (input_position - microphone_buffer_ofs) / 2;
1866+
}
1867+
1868+
PackedVector2Array Input::get_microphone_buffer(int p_frames) {
1869+
PackedVector2Array ret;
1870+
unsigned int input_position = AudioDriver::get_singleton()->get_input_position();
1871+
Vector<int32_t> &buf = AudioDriver::get_singleton()->get_input_buffer();
1872+
if (input_position < microphone_buffer_ofs) {
1873+
input_position += buf.size();
1874+
}
1875+
if ((microphone_buffer_ofs + p_frames * 2 <= input_position) && (p_frames >= 0)) {
1876+
ret.resize(p_frames);
1877+
for (int i = 0; i < p_frames; i++) {
1878+
float l = (buf[microphone_buffer_ofs++] >> 16) / 32768.f;
1879+
if (microphone_buffer_ofs >= buf.size()) {
1880+
microphone_buffer_ofs = 0;
1881+
}
1882+
float r = (buf[microphone_buffer_ofs++] >> 16) / 32768.f;
1883+
if (microphone_buffer_ofs >= buf.size()) {
1884+
microphone_buffer_ofs = 0;
1885+
}
1886+
ret.write[i] = Vector2(l, r);
1887+
}
1888+
}
1889+
return ret;
1890+
}
1891+
18401892
Input::Input() {
18411893
singleton = this;
18421894

core/input/input.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class Input : public Object {
105105
int64_t mouse_window = 0;
106106
bool legacy_just_pressed_behavior = false;
107107
bool disable_input = false;
108+
unsigned int microphone_buffer_ofs = 0;
108109

109110
struct ActionState {
110111
uint64_t pressed_physics_frame = UINT64_MAX;
@@ -396,6 +397,11 @@ class Input : public Object {
396397
void set_disable_input(bool p_disable);
397398
bool is_input_disabled() const;
398399

400+
Error start_microphone();
401+
Error stop_microphone();
402+
int get_microphone_frames_available();
403+
PackedVector2Array get_microphone_buffer(int p_frames);
404+
399405
Input();
400406
~Input();
401407
};

doc/classes/Input.xml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,22 @@
183183
[b]Note:[/b] For Android, [member ProjectSettings.input_devices/sensors/enable_magnetometer] must be enabled.
184184
</description>
185185
</method>
186+
<method name="get_microphone_buffer">
187+
<return type="PackedVector2Array" />
188+
<param index="0" name="frames" type="int" />
189+
<description>
190+
Gets the next [param frames] audio samples from the internal microphone ring buffer.
191+
The buffer is filled at the rate of [method AudioServer.get_input_mix_rate] frames per second after [method start_microphone] has been called.
192+
Returns a [PackedVector2Array] containing exactly [param frames] audio samples if available, or an empty [PackedVector2Array] if insufficient data was available.
193+
The samples are signed floating-point PCM between [code]-1[/code] and [code]1[/code]. You will have to scale them if you want to use them as 8 or 16-bit integer samples. ([code]v = 0x7fff * samples[0].x[/code])
194+
</description>
195+
</method>
196+
<method name="get_microphone_frames_available">
197+
<return type="int" />
198+
<description>
199+
Returns the number of frames available to read using [method get_microphone_buffer].
200+
</description>
201+
</method>
186202
<method name="get_mouse_button_mask" qualifiers="const">
187203
<return type="int" enum="MouseButtonMask" is_bitfield="true" />
188204
<description>
@@ -395,13 +411,26 @@
395411
[b]Note:[/b] For macOS, vibration is only supported in macOS 11 and later.
396412
</description>
397413
</method>
414+
<method name="start_microphone">
415+
<return type="int" enum="Error" />
416+
<description>
417+
Starts the input microphone. Call [method get_microphone_buffer] to retrieve the values.
418+
Returns [code]ERR_ALREADY_IN_USE[/code] if already running.
419+
</description>
420+
</method>
398421
<method name="stop_joy_vibration">
399422
<return type="void" />
400423
<param index="0" name="device" type="int" />
401424
<description>
402425
Stops the vibration of the joypad started with [method start_joy_vibration].
403426
</description>
404427
</method>
428+
<method name="stop_microphone">
429+
<return type="int" enum="Error" />
430+
<description>
431+
Stops the input microphone.
432+
</description>
433+
</method>
405434
<method name="vibrate_handheld">
406435
<return type="void" />
407436
<param index="0" name="duration_ms" type="int" default="500" />

drivers/pulseaudio/audio_driver_pulseaudio.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,9 @@ void AudioDriverPulseAudio::finish_input_device() {
763763
}
764764

765765
Error AudioDriverPulseAudio::input_start() {
766+
if (pa_rec_str) {
767+
return ERR_ALREADY_IN_USE;
768+
}
766769
lock();
767770
Error err = init_input_device();
768771
unlock();

drivers/wasapi/audio_driver_wasapi.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,10 @@ void AudioDriverWASAPI::finish() {
10041004
}
10051005

10061006
Error AudioDriverWASAPI::input_start() {
1007+
if (audio_input.active.is_set()) {
1008+
return ERR_ALREADY_IN_USE;
1009+
}
1010+
10071011
Error err = init_input_device();
10081012
if (err != OK) {
10091013
ERR_PRINT("WASAPI: init_input_device error");
@@ -1023,11 +1027,9 @@ Error AudioDriverWASAPI::input_stop() {
10231027
if (audio_input.active.is_set()) {
10241028
audio_input.audio_client->Stop();
10251029
audio_input.active.clear();
1026-
1027-
return OK;
10281030
}
10291031

1030-
return FAILED;
1032+
return OK;
10311033
}
10321034

10331035
PackedStringArray AudioDriverWASAPI::get_input_device_list() {

platform/android/audio_driver_opensl.cpp

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -273,28 +273,30 @@ Error AudioDriverOpenSL::input_start() {
273273
}
274274

275275
if (OS::get_singleton()->request_permission("RECORD_AUDIO")) {
276-
return init_input_device();
276+
Error err = init_input_device();
277+
if (err != OK) {
278+
input_stop();
279+
}
280+
return err;
277281
}
278282

279283
WARN_PRINT("Unable to start audio capture - No RECORD_AUDIO permission");
280284
return ERR_UNAUTHORIZED;
281285
}
282286

283287
Error AudioDriverOpenSL::input_stop() {
284-
if (!recordItf || !recordBufferQueueItf) {
285-
return ERR_CANT_OPEN;
288+
if (recordItf) {
289+
(*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_STOPPED);
290+
recordItf = nullptr;
286291
}
287292

288-
SLuint32 state;
289-
SLresult res = (*recordItf)->GetRecordState(recordItf, &state);
290-
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
291-
292-
if (state != SL_RECORDSTATE_STOPPED) {
293-
res = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_STOPPED);
294-
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
295-
296-
res = (*recordBufferQueueItf)->Clear(recordBufferQueueItf);
297-
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
293+
if (recordBufferQueueItf) {
294+
(*recordBufferQueueItf)->Clear(recordBufferQueueItf);
295+
recordBufferQueueItf = nullptr;
296+
}
297+
if (recorder) {
298+
(*recorder)->Destroy(recorder);
299+
recorder = nullptr;
298300
}
299301

300302
return OK;

platform/web/audio_driver_web.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,16 @@ void AudioDriverWeb::finish() {
190190
}
191191

192192
Error AudioDriverWeb::input_start() {
193+
if (input_started) {
194+
return ERR_ALREADY_IN_USE;
195+
}
193196
lock();
194197
input_buffer_init(buffer_length);
195198
unlock();
196199
if (godot_audio_input_start()) {
197200
return FAILED;
198201
}
202+
input_started = true;
199203
return OK;
200204
}
201205

@@ -204,6 +208,7 @@ Error AudioDriverWeb::input_stop() {
204208
lock();
205209
input_buffer.clear();
206210
unlock();
211+
input_started = false;
207212
return OK;
208213
}
209214

platform/web/audio_driver_web.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class AudioDriverWeb : public AudioDriver {
5454
int buffer_length = 0;
5555
int mix_rate = 0;
5656
int channel_count = 0;
57+
bool input_started = false;
5758

5859
WASM_EXPORT static void _state_change_callback(int p_state);
5960
WASM_EXPORT static void _latency_update_callback(float p_latency);

servers/audio/audio_stream.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ bool AudioStreamMicrophone::is_monophonic() const {
389389
int AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_frames) {
390390
AudioDriver::get_singleton()->lock();
391391

392-
Vector<int32_t> buf = AudioDriver::get_singleton()->get_input_buffer();
392+
Vector<int32_t> &buf = AudioDriver::get_singleton()->get_input_buffer();
393393
unsigned int input_size = AudioDriver::get_singleton()->get_input_size();
394394
int mix_rate = AudioDriver::get_singleton()->get_input_mix_rate();
395395
unsigned int playback_delay = MIN(((50 * mix_rate) / 1000) * 2, buf.size() >> 1);

servers/audio_server.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ void AudioDriver::input_buffer_write(int32_t sample) {
111111
input_size++;
112112
}
113113
} else {
114+
// This case could only happen if two threads were calling this function without any locking.
114115
WARN_PRINT("input_buffer_write: Invalid input_position=" + itos(input_position) + " input_buffer.size()=" + itos(input_buffer.size()));
115116
}
116117
}

0 commit comments

Comments
 (0)