Path: blob/master/editor/audio/audio_stream_preview.cpp
9897 views
/**************************************************************************/1/* audio_stream_preview.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "audio_stream_preview.h"3132/////////////////////3334float AudioStreamPreview::get_length() const {35return length;36}3738float AudioStreamPreview::get_max(float p_time, float p_time_next) const {39if (length == 0) {40return 0;41}4243int max = preview.size() / 2;44if (max == 0) {45return 0;46}4748int time_from = p_time / length * max;49int time_to = p_time_next / length * max;50time_from = CLAMP(time_from, 0, max - 1);51time_to = CLAMP(time_to, 0, max - 1);5253if (time_to <= time_from) {54time_to = time_from + 1;55}5657uint8_t vmax = 0;5859for (int i = time_from; i < time_to; i++) {60uint8_t v = preview[i * 2 + 1];61if (i == 0 || v > vmax) {62vmax = v;63}64}6566return (vmax / 255.0) * 2.0 - 1.0;67}6869float AudioStreamPreview::get_min(float p_time, float p_time_next) const {70if (length == 0) {71return 0;72}7374int max = preview.size() / 2;75if (max == 0) {76return 0;77}7879int time_from = p_time / length * max;80int time_to = p_time_next / length * max;81time_from = CLAMP(time_from, 0, max - 1);82time_to = CLAMP(time_to, 0, max - 1);8384if (time_to <= time_from) {85time_to = time_from + 1;86}8788uint8_t vmin = 255;8990for (int i = time_from; i < time_to; i++) {91uint8_t v = preview[i * 2];92if (i == 0 || v < vmin) {93vmin = v;94}95}9697return (vmin / 255.0) * 2.0 - 1.0;98}99100AudioStreamPreview::AudioStreamPreview() {101length = 0;102}103104////105106void AudioStreamPreviewGenerator::_update_emit(ObjectID p_id) {107emit_signal(SNAME("preview_updated"), p_id);108}109110void AudioStreamPreviewGenerator::_preview_thread(void *p_preview) {111Thread::set_name("AudioStreamPreviewGenerator");112113Preview *preview = static_cast<Preview *>(p_preview);114115float muxbuff_chunk_s = 0.25;116117int mixbuff_chunk_frames = AudioServer::get_singleton()->get_mix_rate() * muxbuff_chunk_s;118119Vector<AudioFrame> mix_chunk;120mix_chunk.resize(mixbuff_chunk_frames);121122int frames_total = AudioServer::get_singleton()->get_mix_rate() * preview->preview->length;123int frames_todo = frames_total;124125preview->playback->start();126127while (frames_todo) {128int ofs_write = uint64_t(frames_total - frames_todo) * uint64_t(preview->preview->preview.size() / 2) / uint64_t(frames_total);129int to_read = MIN(frames_todo, mixbuff_chunk_frames);130int to_write = uint64_t(to_read) * uint64_t(preview->preview->preview.size() / 2) / uint64_t(frames_total);131to_write = MIN(to_write, (preview->preview->preview.size() / 2) - ofs_write);132133preview->playback->mix(mix_chunk.ptrw(), 1.0, to_read);134135for (int i = 0; i < to_write; i++) {136float max = -1000;137float min = 1000;138int from = uint64_t(i) * to_read / to_write;139int to = (uint64_t(i) + 1) * to_read / to_write;140to = MIN(to, to_read);141from = MIN(from, to_read - 1);142if (to == from) {143to = from + 1;144}145146for (int j = from; j < to; j++) {147max = MAX(max, mix_chunk[j].left);148max = MAX(max, mix_chunk[j].right);149150min = MIN(min, mix_chunk[j].left);151min = MIN(min, mix_chunk[j].right);152}153154uint8_t pfrom = CLAMP((min * 0.5 + 0.5) * 255, 0, 255);155uint8_t pto = CLAMP((max * 0.5 + 0.5) * 255, 0, 255);156157preview->preview->preview.write[(ofs_write + i) * 2 + 0] = pfrom;158preview->preview->preview.write[(ofs_write + i) * 2 + 1] = pto;159}160161frames_todo -= to_read;162callable_mp(singleton, &AudioStreamPreviewGenerator::_update_emit).call_deferred(preview->id);163}164165preview->preview->version++;166167preview->playback->stop();168169preview->generating.clear();170}171172Ref<AudioStreamPreview> AudioStreamPreviewGenerator::generate_preview(const Ref<AudioStream> &p_stream) {173ERR_FAIL_COND_V(p_stream.is_null(), Ref<AudioStreamPreview>());174175if (previews.has(p_stream->get_instance_id())) {176return previews[p_stream->get_instance_id()].preview;177}178179//no preview exists180181previews[p_stream->get_instance_id()] = Preview();182183Preview *preview = &previews[p_stream->get_instance_id()];184preview->base_stream = p_stream;185preview->playback = preview->base_stream->instantiate_playback();186preview->generating.set();187preview->id = p_stream->get_instance_id();188189float len_s = preview->base_stream->get_length();190if (len_s == 0) {191len_s = 60 * 5; //five minutes192}193194int frames = AudioServer::get_singleton()->get_mix_rate() * len_s;195196Vector<uint8_t> maxmin;197int pw = frames / 20;198maxmin.resize(pw * 2);199{200uint8_t *ptr = maxmin.ptrw();201for (int i = 0; i < pw * 2; i++) {202ptr[i] = 127;203}204}205206preview->preview.instantiate();207preview->preview->preview = maxmin;208preview->preview->length = len_s;209210if (preview->playback.is_valid()) {211preview->thread = memnew(Thread);212preview->thread->start(_preview_thread, preview);213}214215return preview->preview;216}217218void AudioStreamPreviewGenerator::_bind_methods() {219ClassDB::bind_method(D_METHOD("generate_preview", "stream"), &AudioStreamPreviewGenerator::generate_preview);220221ADD_SIGNAL(MethodInfo("preview_updated", PropertyInfo(Variant::INT, "obj_id")));222}223224AudioStreamPreviewGenerator *AudioStreamPreviewGenerator::singleton = nullptr;225226void AudioStreamPreviewGenerator::_notification(int p_what) {227switch (p_what) {228case NOTIFICATION_PROCESS: {229List<ObjectID> to_erase;230for (KeyValue<ObjectID, Preview> &E : previews) {231if (!E.value.generating.is_set()) {232if (E.value.thread) {233E.value.thread->wait_to_finish();234memdelete(E.value.thread);235E.value.thread = nullptr;236}237if (!ObjectDB::get_instance(E.key)) { //no longer in use, get rid of preview238to_erase.push_back(E.key);239}240}241}242243while (to_erase.front()) {244previews.erase(to_erase.front()->get());245to_erase.pop_front();246}247} break;248}249}250251AudioStreamPreviewGenerator::AudioStreamPreviewGenerator() {252singleton = this;253set_process(true);254}255256257