2014-02-10 02:10:30 +01:00
|
|
|
/*************************************************************************/
|
|
|
|
/* audio_driver_javascript.cpp */
|
|
|
|
/*************************************************************************/
|
|
|
|
/* This file is part of: */
|
|
|
|
/* GODOT ENGINE */
|
2017-08-27 14:16:55 +02:00
|
|
|
/* https://godotengine.org */
|
2014-02-10 02:10:30 +01:00
|
|
|
/*************************************************************************/
|
2020-01-01 11:16:22 +01:00
|
|
|
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
|
|
|
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
2014-02-10 02:10:30 +01:00
|
|
|
/* */
|
|
|
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
|
|
/* a copy of this software and associated documentation files (the */
|
|
|
|
/* "Software"), to deal in the Software without restriction, including */
|
|
|
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
|
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
|
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
|
|
/* the following conditions: */
|
|
|
|
/* */
|
|
|
|
/* The above copyright notice and this permission notice shall be */
|
|
|
|
/* included in all copies or substantial portions of the Software. */
|
|
|
|
/* */
|
|
|
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
|
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
|
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
|
|
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
|
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
|
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
|
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
|
|
/*************************************************************************/
|
2018-01-05 00:50:27 +01:00
|
|
|
|
2014-02-10 02:10:30 +01:00
|
|
|
#include "audio_driver_javascript.h"
|
|
|
|
|
2020-05-18 02:19:35 +02:00
|
|
|
#include "core/project_settings.h"
|
|
|
|
|
2017-10-27 03:34:49 +02:00
|
|
|
#include <emscripten.h>
|
2014-02-10 02:10:30 +01:00
|
|
|
|
2020-09-28 15:43:48 +02:00
|
|
|
#include "godot_audio.h"
|
|
|
|
|
2017-11-05 23:48:13 +01:00
|
|
|
AudioDriverJavaScript *AudioDriverJavaScript::singleton = NULL;
|
2017-11-04 20:24:06 +01:00
|
|
|
|
2020-06-29 18:51:53 +02:00
|
|
|
bool AudioDriverJavaScript::is_available() {
|
2020-09-28 15:43:48 +02:00
|
|
|
return godot_audio_is_available() != 0;
|
2020-06-29 18:51:53 +02:00
|
|
|
}
|
|
|
|
|
2017-03-05 16:44:50 +01:00
|
|
|
const char *AudioDriverJavaScript::get_name() const {
|
2014-02-10 02:10:30 +01:00
|
|
|
return "JavaScript";
|
|
|
|
}
|
|
|
|
|
2020-09-28 15:43:48 +02:00
|
|
|
#ifndef NO_THREADS
|
|
|
|
void AudioDriverJavaScript::_audio_thread_func(void *p_data) {
|
|
|
|
AudioDriverJavaScript *obj = static_cast<AudioDriverJavaScript *>(p_data);
|
|
|
|
while (!obj->quit) {
|
|
|
|
obj->lock();
|
|
|
|
if (!obj->needs_process) {
|
|
|
|
obj->unlock();
|
|
|
|
OS::get_singleton()->delay_usec(1000); // Give the browser some slack.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
obj->_js_driver_process();
|
|
|
|
obj->needs_process = false;
|
|
|
|
obj->unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_process_start() {
|
|
|
|
#ifndef NO_THREADS
|
|
|
|
AudioDriverJavaScript::singleton->lock();
|
|
|
|
#else
|
|
|
|
AudioDriverJavaScript::singleton->_js_driver_process();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_process_end() {
|
|
|
|
#ifndef NO_THREADS
|
|
|
|
AudioDriverJavaScript::singleton->needs_process = true;
|
|
|
|
AudioDriverJavaScript::singleton->unlock();
|
|
|
|
#endif
|
2017-10-27 03:34:49 +02:00
|
|
|
}
|
|
|
|
|
2018-10-08 20:46:21 +02:00
|
|
|
extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_process_capture(float sample) {
|
|
|
|
AudioDriverJavaScript::singleton->process_capture(sample);
|
|
|
|
}
|
|
|
|
|
2020-09-28 15:43:48 +02:00
|
|
|
void AudioDriverJavaScript::_js_driver_process() {
|
2017-11-05 23:48:13 +01:00
|
|
|
int sample_count = memarr_len(internal_buffer) / channel_count;
|
|
|
|
int32_t *stream_buffer = reinterpret_cast<int32_t *>(internal_buffer);
|
|
|
|
audio_server_process(sample_count, stream_buffer);
|
|
|
|
for (int i = 0; i < sample_count * channel_count; i++) {
|
2018-10-08 20:46:21 +02:00
|
|
|
internal_buffer[i] = float(stream_buffer[i] >> 16) / 32768.f;
|
2017-11-05 23:48:13 +01:00
|
|
|
}
|
|
|
|
}
|
2017-10-27 03:34:49 +02:00
|
|
|
|
2018-10-08 20:46:21 +02:00
|
|
|
void AudioDriverJavaScript::process_capture(float sample) {
|
|
|
|
int32_t sample32 = int32_t(sample * 32768.f) * (1U << 16);
|
2020-01-20 21:35:13 +01:00
|
|
|
input_buffer_write(sample32);
|
2018-10-08 20:46:21 +02:00
|
|
|
}
|
|
|
|
|
2017-11-05 23:48:13 +01:00
|
|
|
Error AudioDriverJavaScript::init() {
|
2020-09-28 15:43:48 +02:00
|
|
|
mix_rate = GLOBAL_GET("audio/mix_rate");
|
2020-05-18 02:31:38 +02:00
|
|
|
int latency = GLOBAL_GET("audio/output_latency");
|
2020-05-18 02:19:35 +02:00
|
|
|
|
2020-09-28 15:43:48 +02:00
|
|
|
channel_count = godot_audio_init(mix_rate, latency);
|
2020-11-06 15:10:57 +01:00
|
|
|
buffer_length = closest_power_of_2(latency * mix_rate / 1000);
|
2020-09-28 15:43:48 +02:00
|
|
|
buffer_length = godot_audio_create_processor(buffer_length, channel_count);
|
2017-11-05 23:48:13 +01:00
|
|
|
if (!buffer_length) {
|
|
|
|
return FAILED;
|
2017-10-27 03:34:49 +02:00
|
|
|
}
|
2014-02-10 02:10:30 +01:00
|
|
|
|
2019-06-15 12:11:30 +02:00
|
|
|
if (!internal_buffer || (int)memarr_len(internal_buffer) != buffer_length * channel_count) {
|
2017-11-05 23:48:13 +01:00
|
|
|
if (internal_buffer)
|
|
|
|
memdelete_arr(internal_buffer);
|
|
|
|
internal_buffer = memnew_arr(float, buffer_length *channel_count);
|
|
|
|
}
|
2018-10-08 20:46:21 +02:00
|
|
|
|
2020-09-28 15:43:48 +02:00
|
|
|
if (!internal_buffer) {
|
|
|
|
return ERR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
return OK;
|
2014-02-10 02:10:30 +01:00
|
|
|
}
|
2017-01-16 19:19:45 +01:00
|
|
|
|
2017-03-05 16:44:50 +01:00
|
|
|
void AudioDriverJavaScript::start() {
|
2020-09-28 15:43:48 +02:00
|
|
|
#ifndef NO_THREADS
|
|
|
|
mutex = Mutex::create();
|
|
|
|
thread = Thread::create(_audio_thread_func, this);
|
|
|
|
#endif
|
|
|
|
godot_audio_start(internal_buffer);
|
2014-02-10 02:10:30 +01:00
|
|
|
}
|
2017-01-16 19:19:45 +01:00
|
|
|
|
2019-01-20 14:25:15 +01:00
|
|
|
void AudioDriverJavaScript::resume() {
|
2020-09-28 15:43:48 +02:00
|
|
|
godot_audio_resume();
|
2019-01-20 14:25:15 +01:00
|
|
|
}
|
|
|
|
|
2020-05-18 02:19:35 +02:00
|
|
|
float AudioDriverJavaScript::get_latency() {
|
2020-09-28 15:43:48 +02:00
|
|
|
return godot_audio_get_latency();
|
2020-05-18 02:19:35 +02:00
|
|
|
}
|
|
|
|
|
2014-02-10 02:10:30 +01:00
|
|
|
int AudioDriverJavaScript::get_mix_rate() const {
|
2020-09-28 15:43:48 +02:00
|
|
|
return mix_rate;
|
2014-02-10 02:10:30 +01:00
|
|
|
}
|
|
|
|
|
2017-03-05 16:44:50 +01:00
|
|
|
AudioDriver::SpeakerMode AudioDriverJavaScript::get_speaker_mode() const {
|
2020-09-28 15:43:48 +02:00
|
|
|
return get_speaker_mode_by_total_channels(channel_count);
|
2014-02-10 02:10:30 +01:00
|
|
|
}
|
2017-01-16 19:19:45 +01:00
|
|
|
|
2017-03-05 16:44:50 +01:00
|
|
|
void AudioDriverJavaScript::lock() {
|
2020-09-28 15:43:48 +02:00
|
|
|
#ifndef NO_THREADS
|
|
|
|
if (mutex) {
|
|
|
|
mutex->lock();
|
|
|
|
}
|
|
|
|
#endif
|
2014-02-10 02:10:30 +01:00
|
|
|
}
|
2017-01-16 19:19:45 +01:00
|
|
|
|
2014-02-10 02:10:30 +01:00
|
|
|
void AudioDriverJavaScript::unlock() {
|
2020-09-28 15:43:48 +02:00
|
|
|
#ifndef NO_THREADS
|
|
|
|
if (mutex) {
|
|
|
|
mutex->unlock();
|
|
|
|
}
|
|
|
|
#endif
|
2014-02-10 02:10:30 +01:00
|
|
|
}
|
2017-01-16 19:19:45 +01:00
|
|
|
|
2020-01-19 11:44:19 +01:00
|
|
|
void AudioDriverJavaScript::finish_async() {
|
2020-09-28 15:43:48 +02:00
|
|
|
#ifndef NO_THREADS
|
|
|
|
quit = true; // Ask thread to quit.
|
|
|
|
#endif
|
|
|
|
godot_audio_finish_async();
|
2020-01-19 11:44:19 +01:00
|
|
|
}
|
2018-10-08 20:46:21 +02:00
|
|
|
|
2020-01-19 11:44:19 +01:00
|
|
|
void AudioDriverJavaScript::finish() {
|
2020-09-28 15:43:48 +02:00
|
|
|
#ifndef NO_THREADS
|
|
|
|
Thread::wait_to_finish(thread);
|
|
|
|
memdelete(thread);
|
|
|
|
thread = NULL;
|
|
|
|
memdelete(mutex);
|
|
|
|
mutex = NULL;
|
|
|
|
#endif
|
2018-10-08 20:46:21 +02:00
|
|
|
if (internal_buffer) {
|
|
|
|
memdelete_arr(internal_buffer);
|
|
|
|
internal_buffer = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Error AudioDriverJavaScript::capture_start() {
|
2020-01-20 21:35:13 +01:00
|
|
|
input_buffer_init(buffer_length);
|
2020-09-28 15:43:48 +02:00
|
|
|
godot_audio_capture_start();
|
2018-10-08 20:46:21 +02:00
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
Error AudioDriverJavaScript::capture_stop() {
|
2020-09-28 15:43:48 +02:00
|
|
|
godot_audio_capture_stop();
|
2020-01-20 21:35:13 +01:00
|
|
|
input_buffer.clear();
|
2018-10-08 20:46:21 +02:00
|
|
|
return OK;
|
2014-02-10 02:10:30 +01:00
|
|
|
}
|
|
|
|
|
2017-03-05 16:44:50 +01:00
|
|
|
AudioDriverJavaScript::AudioDriverJavaScript() {
|
2018-10-08 20:46:21 +02:00
|
|
|
internal_buffer = NULL;
|
2020-01-19 11:44:19 +01:00
|
|
|
buffer_length = 0;
|
2020-09-28 15:43:48 +02:00
|
|
|
mix_rate = 0;
|
|
|
|
channel_count = 0;
|
|
|
|
|
|
|
|
#ifndef NO_THREADS
|
|
|
|
mutex = NULL;
|
|
|
|
thread = NULL;
|
|
|
|
quit = false;
|
|
|
|
needs_process = true;
|
|
|
|
#endif
|
2018-10-08 20:46:21 +02:00
|
|
|
|
2017-11-05 23:48:13 +01:00
|
|
|
singleton = this;
|
2014-02-10 02:10:30 +01:00
|
|
|
}
|