2
0
Эх сурвалжийг харах

Merge pull request #19443 from eska014/html5-audio-refactor

Detect channel count and mix rate in HTML5 audio driver
Rémi Verschelde 7 жил өмнө
parent
commit
69058d33fc

+ 85 - 64
platform/javascript/audio_driver_javascript.cpp

@@ -32,113 +32,134 @@
 
 #include <emscripten.h>
 
-AudioDriverJavaScript *AudioDriverJavaScript::singleton_js = NULL;
+AudioDriverJavaScript *AudioDriverJavaScript::singleton = NULL;
 
 const char *AudioDriverJavaScript::get_name() const {
 
 	return "JavaScript";
 }
 
-extern "C" EMSCRIPTEN_KEEPALIVE void js_audio_driver_mix_function(int p_frames) {
+extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_js_mix() {
 
-	//print_line("MIXI! "+itos(p_frames));
-	AudioDriverJavaScript::singleton_js->mix_to_js(p_frames);
+	AudioDriverJavaScript::singleton->mix_to_js();
 }
 
-void AudioDriverJavaScript::mix_to_js(int p_frames) {
+void AudioDriverJavaScript::mix_to_js() {
 
-	int todo = p_frames;
-	int offset = 0;
+	int channel_count = get_total_channels_by_speaker_mode(get_speaker_mode());
+	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++) {
+		internal_buffer[i] = float(stream_buffer[i] >> 16) / 32768.0;
+	}
+}
 
-	while (todo) {
+Error AudioDriverJavaScript::init() {
 
-		int tomix = MIN(todo, INTERNAL_BUFFER_SIZE);
+	/* clang-format off */
+	EM_ASM({
+		_audioDriver_audioContext = new (window.AudioContext || window.webkitAudioContext);
+		_audioDriver_scriptNode = null;
+	});
+	/* clang-format on */
 
-		audio_server_process(p_frames, stream_buffer);
-		for (int i = 0; i < tomix * internal_buffer_channels; i++) {
-			internal_buffer[i] = float(stream_buffer[i] >> 16) / 32768.0;
+	int channel_count = get_total_channels_by_speaker_mode(get_speaker_mode());
+	/* clang-format off */
+	int buffer_length = EM_ASM_INT({
+		var CHANNEL_COUNT = $0;
+
+		var channelCount = _audioDriver_audioContext.destination.channelCount;
+		try {
+			// Try letting the browser recommend a buffer length.
+			_audioDriver_scriptNode = _audioDriver_audioContext.createScriptProcessor(0, 0, channelCount);
+		} catch (e) {
+			// ...otherwise, default to 4096.
+			_audioDriver_scriptNode = _audioDriver_audioContext.createScriptProcessor(4096, 0, channelCount);
 		}
+		_audioDriver_scriptNode.connect(_audioDriver_audioContext.destination);
 
-		/* clang-format off */
-		EM_ASM_ARGS({
-			var data = HEAPF32.subarray($0 / 4, $0 / 4 + $2 * 2);
-
-			for (var channel = 0; channel < _as_output_buffer.numberOfChannels; channel++) {
-				var outputData = _as_output_buffer.getChannelData(channel);
-				// Loop through samples
-				for (var sample = 0; sample < $2; sample++) {
-					// make output equal to the same as the input
-					outputData[sample + $1] = data[sample * 2 + channel];
-				}
-			}
-		}, internal_buffer, offset, tomix);
-		/* clang-format on */
-
-		todo -= tomix;
-		offset += tomix;
+		return _audioDriver_scriptNode.bufferSize;
+	}, channel_count);
+	/* clang-format on */
+	if (!buffer_length) {
+		return FAILED;
 	}
-}
-
-Error AudioDriverJavaScript::init() {
 
-	return OK;
+	if (!internal_buffer || memarr_len(internal_buffer) != buffer_length * channel_count) {
+		if (internal_buffer)
+			memdelete_arr(internal_buffer);
+		internal_buffer = memnew_arr(float, buffer_length *channel_count);
+	}
+	return internal_buffer ? OK : ERR_OUT_OF_MEMORY;
 }
 
 void AudioDriverJavaScript::start() {
 
-	internal_buffer = memnew_arr(float, INTERNAL_BUFFER_SIZE *internal_buffer_channels);
-	stream_buffer = memnew_arr(int32_t, INTERNAL_BUFFER_SIZE * 4); //max 4 channels
-
 	/* clang-format off */
-	mix_rate = EM_ASM_INT({
-		_as_audioctx = new (window.AudioContext || window.webkitAudioContext);
-		_as_script_node = _as_audioctx.createScriptProcessor($0, 0, $1);
-		_as_script_node.connect(_as_audioctx.destination);
-		console.log(_as_script_node.bufferSize);
-		var jsAudioDriverMixFunction = cwrap('js_audio_driver_mix_function', null, ['number']);
-
-		_as_script_node.onaudioprocess = function(audioProcessingEvent) {
-			// The output buffer contains the samples that will be modified and played
-			_as_output_buffer = audioProcessingEvent.outputBuffer;
-			jsAudioDriverMixFunction([_as_output_buffer.getChannelData(0).length]);
+	EM_ASM({
+		var INTERNAL_BUFFER_PTR = $0;
+
+		var audioDriverMixFunction = cwrap('audio_driver_js_mix');
+		_audioDriver_scriptNode.onaudioprocess = function(audioProcessingEvent) {
+			audioDriverMixFunction();
+			// The output buffer contains the samples that will be modified and played.
+			var output = audioProcessingEvent.outputBuffer;
+			var input = HEAPF32.subarray(
+					INTERNAL_BUFFER_PTR / HEAPF32.BYTES_PER_ELEMENT,
+					INTERNAL_BUFFER_PTR / HEAPF32.BYTES_PER_ELEMENT + output.length * output.numberOfChannels);
+
+			for (var channel = 0; channel < output.numberOfChannels; channel++) {
+				var outputData = output.getChannelData(channel);
+				// Loop through samples.
+				for (var sample = 0; sample < outputData.length; sample++) {
+					// Set output equal to input.
+					outputData[sample] = input[sample * output.numberOfChannels + channel];
+				}
+			}
 		};
-		return _as_audioctx.sampleRate;
-	}, INTERNAL_BUFFER_SIZE, internal_buffer_channels);
+	}, internal_buffer);
 	/* clang-format on */
 }
 
 int AudioDriverJavaScript::get_mix_rate() const {
 
-	return mix_rate;
+	/* clang-format off */
+	return EM_ASM_INT_V({
+		return _audioDriver_audioContext.sampleRate;
+	});
+	/* clang-format on */
 }
 
 AudioDriver::SpeakerMode AudioDriverJavaScript::get_speaker_mode() const {
 
-	return SPEAKER_MODE_STEREO;
+	/* clang-format off */
+	return get_speaker_mode_by_total_channels(EM_ASM_INT_V({
+		return _audioDriver_audioContext.destination.channelCount;
+	}));
+	/* clang-format on */
 }
 
+// No locking, as threads are not supported.
 void AudioDriverJavaScript::lock() {
-
-	/*no locking, as threads are not supported
-	if (active && mutex)
-		mutex->lock();
-	*/
 }
 
 void AudioDriverJavaScript::unlock() {
-
-	/*no locking, as threads are not supported
-	if (active && mutex)
-		mutex->unlock();
-	*/
 }
 
 void AudioDriverJavaScript::finish() {
+
+	/* clang-format off */
+	EM_ASM({
+		_audioDriver_audioContext = null;
+		_audioDriver_scriptNode = null;
+	});
+	/* clang-format on */
+	memdelete_arr(internal_buffer);
+	internal_buffer = NULL;
 }
 
 AudioDriverJavaScript::AudioDriverJavaScript() {
 
-	internal_buffer_channels = 2;
-	mix_rate = DEFAULT_MIX_RATE;
-	singleton_js = this;
+	singleton = this;
 }

+ 2 - 9
platform/javascript/audio_driver_javascript.h

@@ -35,18 +35,11 @@
 
 class AudioDriverJavaScript : public AudioDriver {
 
-	enum {
-		INTERNAL_BUFFER_SIZE = 4096,
-	};
-
-	int mix_rate;
 	float *internal_buffer;
-	int internal_buffer_channels;
-	int32_t *stream_buffer;
 
 public:
-	void mix_to_js(int p_frames);
-	static AudioDriverJavaScript *singleton_js;
+	void mix_to_js();
+	static AudioDriverJavaScript *singleton;
 
 	virtual const char *get_name() const;