| 
									
										
										
										
											2018-08-29 22:38:13 +02:00
										 |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*  audio_stream_preview.cpp                                             */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*                       This file is part of:                           */ | 
					
						
							|  |  |  | /*                           GODOT ENGINE                                */ | 
					
						
							|  |  |  | /*                      https://godotengine.org                          */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							| 
									
										
										
										
											2022-01-03 21:27:34 +01:00
										 |  |  | /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */ | 
					
						
							|  |  |  | /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */ | 
					
						
							| 
									
										
										
										
											2018-08-29 22:38:13 +02: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-06-07 12:46:14 -03:00
										 |  |  | #include "audio_stream_preview.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | float AudioStreamPreview::get_length() const { | 
					
						
							|  |  |  | 	return length; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-05-14 14:29:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | float AudioStreamPreview::get_max(float p_time, float p_time_next) const { | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 	if (length == 0) { | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	int max = preview.size() / 2; | 
					
						
							|  |  |  | 	int time_from = p_time / length * max; | 
					
						
							|  |  |  | 	int time_to = p_time_next / length * max; | 
					
						
							|  |  |  | 	time_from = CLAMP(time_from, 0, max - 1); | 
					
						
							|  |  |  | 	time_to = CLAMP(time_to, 0, max - 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (time_to <= time_from) { | 
					
						
							|  |  |  | 		time_to = time_from + 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-04 13:04:58 +02:00
										 |  |  | 	uint8_t vmax = 0; | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for (int i = time_from; i < time_to; i++) { | 
					
						
							|  |  |  | 		uint8_t v = preview[i * 2 + 1]; | 
					
						
							|  |  |  | 		if (i == 0 || v > vmax) { | 
					
						
							|  |  |  | 			vmax = v; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return (vmax / 255.0) * 2.0 - 1.0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-05-14 14:29:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | float AudioStreamPreview::get_min(float p_time, float p_time_next) const { | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 	if (length == 0) { | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	int max = preview.size() / 2; | 
					
						
							|  |  |  | 	int time_from = p_time / length * max; | 
					
						
							|  |  |  | 	int time_to = p_time_next / length * max; | 
					
						
							|  |  |  | 	time_from = CLAMP(time_from, 0, max - 1); | 
					
						
							|  |  |  | 	time_to = CLAMP(time_to, 0, max - 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (time_to <= time_from) { | 
					
						
							|  |  |  | 		time_to = time_from + 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-21 17:50:02 -03:00
										 |  |  | 	uint8_t vmin = 255; | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for (int i = time_from; i < time_to; i++) { | 
					
						
							|  |  |  | 		uint8_t v = preview[i * 2]; | 
					
						
							|  |  |  | 		if (i == 0 || v < vmin) { | 
					
						
							|  |  |  | 			vmin = v; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return (vmin / 255.0) * 2.0 - 1.0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AudioStreamPreview::AudioStreamPreview() { | 
					
						
							|  |  |  | 	length = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AudioStreamPreviewGenerator::_update_emit(ObjectID p_id) { | 
					
						
							| 
									
										
										
										
											2021-07-17 18:22:52 -03:00
										 |  |  | 	emit_signal(SNAME("preview_updated"), p_id); | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AudioStreamPreviewGenerator::_preview_thread(void *p_preview) { | 
					
						
							| 
									
										
										
										
											2022-04-07 13:23:40 +03:00
										 |  |  | 	Preview *preview = static_cast<Preview *>(p_preview); | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	float muxbuff_chunk_s = 0.25; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int mixbuff_chunk_frames = AudioServer::get_singleton()->get_mix_rate() * muxbuff_chunk_s; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Vector<AudioFrame> mix_chunk; | 
					
						
							|  |  |  | 	mix_chunk.resize(mixbuff_chunk_frames); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int frames_total = AudioServer::get_singleton()->get_mix_rate() * preview->preview->length; | 
					
						
							|  |  |  | 	int frames_todo = frames_total; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	preview->playback->start(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (frames_todo) { | 
					
						
							|  |  |  | 		int ofs_write = uint64_t(frames_total - frames_todo) * uint64_t(preview->preview->preview.size() / 2) / uint64_t(frames_total); | 
					
						
							|  |  |  | 		int to_read = MIN(frames_todo, mixbuff_chunk_frames); | 
					
						
							|  |  |  | 		int to_write = uint64_t(to_read) * uint64_t(preview->preview->preview.size() / 2) / uint64_t(frames_total); | 
					
						
							|  |  |  | 		to_write = MIN(to_write, (preview->preview->preview.size() / 2) - ofs_write); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		preview->playback->mix(mix_chunk.ptrw(), 1.0, to_read); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (int i = 0; i < to_write; i++) { | 
					
						
							|  |  |  | 			float max = -1000; | 
					
						
							|  |  |  | 			float min = 1000; | 
					
						
							|  |  |  | 			int from = uint64_t(i) * to_read / to_write; | 
					
						
							| 
									
										
										
										
											2019-06-03 21:52:50 +02:00
										 |  |  | 			int to = (uint64_t(i) + 1) * to_read / to_write; | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 			to = MIN(to, to_read); | 
					
						
							|  |  |  | 			from = MIN(from, to_read - 1); | 
					
						
							|  |  |  | 			if (to == from) { | 
					
						
							|  |  |  | 				to = from + 1; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for (int j = from; j < to; j++) { | 
					
						
							|  |  |  | 				max = MAX(max, mix_chunk[j].l); | 
					
						
							|  |  |  | 				max = MAX(max, mix_chunk[j].r); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				min = MIN(min, mix_chunk[j].l); | 
					
						
							|  |  |  | 				min = MIN(min, mix_chunk[j].r); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			uint8_t pfrom = CLAMP((min * 0.5 + 0.5) * 255, 0, 255); | 
					
						
							|  |  |  | 			uint8_t pto = CLAMP((max * 0.5 + 0.5) * 255, 0, 255); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 03:11:03 +02:00
										 |  |  | 			preview->preview->preview.write[(ofs_write + i) * 2 + 0] = pfrom; | 
					
						
							|  |  |  | 			preview->preview->preview.write[(ofs_write + i) * 2 + 1] = pto; | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		frames_todo -= to_read; | 
					
						
							| 
									
										
										
										
											2021-07-17 18:22:52 -03:00
										 |  |  | 		singleton->call_deferred(SNAME("_update_emit"), preview->id); | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-21 01:00:58 +02:00
										 |  |  | 	preview->preview->version++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 	preview->playback->stop(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-10 19:22:13 +01:00
										 |  |  | 	preview->generating.clear(); | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Ref<AudioStreamPreview> AudioStreamPreviewGenerator::generate_preview(const Ref<AudioStream> &p_stream) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(p_stream.is_null(), Ref<AudioStreamPreview>()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (previews.has(p_stream->get_instance_id())) { | 
					
						
							|  |  |  | 		return previews[p_stream->get_instance_id()].preview; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//no preview exists
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	previews[p_stream->get_instance_id()] = Preview(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Preview *preview = &previews[p_stream->get_instance_id()]; | 
					
						
							|  |  |  | 	preview->base_stream = p_stream; | 
					
						
							| 
									
										
										
										
											2022-07-21 01:00:58 +02:00
										 |  |  | 	preview->playback = preview->base_stream->instantiate_playback(); | 
					
						
							| 
									
										
										
										
											2021-02-10 19:22:13 +01:00
										 |  |  | 	preview->generating.set(); | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 	preview->id = p_stream->get_instance_id(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	float len_s = preview->base_stream->get_length(); | 
					
						
							|  |  |  | 	if (len_s == 0) { | 
					
						
							|  |  |  | 		len_s = 60 * 5; //five minutes
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int frames = AudioServer::get_singleton()->get_mix_rate() * len_s; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Vector<uint8_t> maxmin; | 
					
						
							|  |  |  | 	int pw = frames / 20; | 
					
						
							|  |  |  | 	maxmin.resize(pw * 2); | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		uint8_t *ptr = maxmin.ptrw(); | 
					
						
							|  |  |  | 		for (int i = 0; i < pw * 2; i++) { | 
					
						
							|  |  |  | 			ptr[i] = 127; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-17 16:03:09 -06:00
										 |  |  | 	preview->preview.instantiate(); | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 	preview->preview->preview = maxmin; | 
					
						
							|  |  |  | 	preview->preview->length = len_s; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 	if (preview->playback.is_valid()) { | 
					
						
							| 
									
										
										
										
											2021-01-19 13:29:41 +01:00
										 |  |  | 		preview->thread = memnew(Thread); | 
					
						
							| 
									
										
										
										
											2022-07-28 22:56:41 +02:00
										 |  |  | 		preview->thread->set_name("AudioStreamPreviewGenerator"); | 
					
						
							| 
									
										
										
										
											2021-01-19 13:29:41 +01:00
										 |  |  | 		preview->thread->start(_preview_thread, preview); | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return preview->preview; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AudioStreamPreviewGenerator::_bind_methods() { | 
					
						
							|  |  |  | 	ClassDB::bind_method("_update_emit", &AudioStreamPreviewGenerator::_update_emit); | 
					
						
							|  |  |  | 	ClassDB::bind_method(D_METHOD("generate_preview", "stream"), &AudioStreamPreviewGenerator::generate_preview); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ADD_SIGNAL(MethodInfo("preview_updated", PropertyInfo(Variant::INT, "obj_id"))); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | AudioStreamPreviewGenerator *AudioStreamPreviewGenerator::singleton = nullptr; | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | void AudioStreamPreviewGenerator::_notification(int p_what) { | 
					
						
							| 
									
										
										
										
											2022-02-16 00:52:32 +01:00
										 |  |  | 	switch (p_what) { | 
					
						
							|  |  |  | 		case NOTIFICATION_PROCESS: { | 
					
						
							|  |  |  | 			List<ObjectID> to_erase; | 
					
						
							|  |  |  | 			for (KeyValue<ObjectID, Preview> &E : previews) { | 
					
						
							|  |  |  | 				if (!E.value.generating.is_set()) { | 
					
						
							|  |  |  | 					if (E.value.thread) { | 
					
						
							|  |  |  | 						E.value.thread->wait_to_finish(); | 
					
						
							|  |  |  | 						memdelete(E.value.thread); | 
					
						
							|  |  |  | 						E.value.thread = nullptr; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					if (!ObjectDB::get_instance(E.key)) { //no longer in use, get rid of preview
 | 
					
						
							|  |  |  | 						to_erase.push_back(E.key); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-16 00:52:32 +01:00
										 |  |  | 			while (to_erase.front()) { | 
					
						
							|  |  |  | 				previews.erase(to_erase.front()->get()); | 
					
						
							|  |  |  | 				to_erase.pop_front(); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} break; | 
					
						
							| 
									
										
										
										
											2018-06-07 12:46:14 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AudioStreamPreviewGenerator::AudioStreamPreviewGenerator() { | 
					
						
							|  |  |  | 	singleton = this; | 
					
						
							|  |  |  | 	set_process(true); | 
					
						
							|  |  |  | } |