Path: blob/master/modules/minimp3/audio_stream_mp3.cpp
10277 views
/**************************************************************************/1/* audio_stream_mp3.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#define MINIMP3_FLOAT_OUTPUT31#define MINIMP3_IMPLEMENTATION32#define MINIMP3_NO_STDIO3334#include "audio_stream_mp3.h"3536int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {37if (!active) {38return 0;39}4041int todo = p_frames;4243int frames_mixed_this_step = p_frames;4445int beat_length_frames = -1;46bool use_loop = looping_override ? looping : mp3_stream->loop;4748bool beat_loop = use_loop && mp3_stream->get_bpm() > 0 && mp3_stream->get_beat_count() > 0;49if (beat_loop) {50beat_length_frames = mp3_stream->get_beat_count() * mp3_stream->sample_rate * 60 / mp3_stream->get_bpm();51}5253while (todo && active) {54mp3dec_frame_info_t frame_info;55mp3d_sample_t *buf_frame = nullptr;5657int samples_mixed = mp3dec_ex_read_frame(&mp3d, &buf_frame, &frame_info, mp3_stream->channels);5859if (samples_mixed) {60p_buffer[p_frames - todo] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]);61if (loop_fade_remaining < FADE_SIZE) {62p_buffer[p_frames - todo] += loop_fade[loop_fade_remaining] * (float(FADE_SIZE - loop_fade_remaining) / float(FADE_SIZE));63loop_fade_remaining++;64}65--todo;66++frames_mixed;6768if (beat_loop && (int)frames_mixed >= beat_length_frames) {69for (int i = 0; i < FADE_SIZE; i++) {70samples_mixed = mp3dec_ex_read_frame(&mp3d, &buf_frame, &frame_info, mp3_stream->channels);71loop_fade[i] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]);72if (!samples_mixed) {73break;74}75}76loop_fade_remaining = 0;77seek(mp3_stream->loop_offset);78loops++;79}80}8182else {83//EOF84if (use_loop) {85seek(mp3_stream->loop_offset);86loops++;87} else {88frames_mixed_this_step = p_frames - todo;89//fill remainder with silence90for (int i = p_frames - todo; i < p_frames; i++) {91p_buffer[i] = AudioFrame(0, 0);92}93active = false;94todo = 0;95}96}97}98return frames_mixed_this_step;99}100101float AudioStreamPlaybackMP3::get_stream_sampling_rate() {102return mp3_stream->sample_rate;103}104105void AudioStreamPlaybackMP3::start(double p_from_pos) {106active = true;107seek(p_from_pos);108loops = 0;109begin_resample();110}111112void AudioStreamPlaybackMP3::stop() {113active = false;114}115116bool AudioStreamPlaybackMP3::is_playing() const {117return active;118}119120int AudioStreamPlaybackMP3::get_loop_count() const {121return loops;122}123124double AudioStreamPlaybackMP3::get_playback_position() const {125return double(frames_mixed) / mp3_stream->sample_rate;126}127128void AudioStreamPlaybackMP3::seek(double p_time) {129if (!active) {130return;131}132133if (p_time >= mp3_stream->get_length()) {134p_time = 0;135}136137frames_mixed = uint32_t(mp3_stream->sample_rate * p_time);138mp3dec_ex_seek(&mp3d, (uint64_t)frames_mixed * mp3_stream->channels);139}140141void AudioStreamPlaybackMP3::tag_used_streams() {142mp3_stream->tag_used(get_playback_position());143}144145void AudioStreamPlaybackMP3::set_is_sample(bool p_is_sample) {146_is_sample = p_is_sample;147}148149bool AudioStreamPlaybackMP3::get_is_sample() const {150return _is_sample;151}152153Ref<AudioSamplePlayback> AudioStreamPlaybackMP3::get_sample_playback() const {154return sample_playback;155}156157void AudioStreamPlaybackMP3::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {158sample_playback = p_playback;159if (sample_playback.is_valid()) {160sample_playback->stream_playback = Ref<AudioStreamPlayback>(this);161}162}163164void AudioStreamPlaybackMP3::set_parameter(const StringName &p_name, const Variant &p_value) {165if (p_name == SNAME("looping")) {166if (p_value == Variant()) {167looping_override = false;168looping = false;169} else {170looping_override = true;171looping = p_value;172}173}174}175176Variant AudioStreamPlaybackMP3::get_parameter(const StringName &p_name) const {177if (looping_override && p_name == SNAME("looping")) {178return looping;179}180return Variant();181}182183AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {184mp3dec_ex_close(&mp3d);185}186187Ref<AudioStreamPlayback> AudioStreamMP3::instantiate_playback() {188Ref<AudioStreamPlaybackMP3> mp3s;189190ERR_FAIL_COND_V_MSG(data.is_empty(), mp3s,191"This AudioStreamMP3 does not have an audio file assigned "192"to it. AudioStreamMP3 should not be created from the "193"inspector or with `.new()`. Instead, load an audio file.");194195mp3s.instantiate();196mp3s->mp3_stream = Ref<AudioStreamMP3>(this);197198int errorcode = mp3dec_ex_open_buf(&mp3s->mp3d, data.ptr(), data_len, MP3D_SEEK_TO_SAMPLE);199200mp3s->frames_mixed = 0;201mp3s->active = false;202mp3s->loops = 0;203204if (errorcode) {205ERR_FAIL_COND_V(errorcode, Ref<AudioStreamPlaybackMP3>());206}207208return mp3s;209}210211String AudioStreamMP3::get_stream_name() const {212return ""; //return stream_name;213}214215void AudioStreamMP3::clear_data() {216data.clear();217}218219void AudioStreamMP3::set_data(const Vector<uint8_t> &p_data) {220int src_data_len = p_data.size();221222mp3dec_ex_t *mp3d = memnew(mp3dec_ex_t);223int err = mp3dec_ex_open_buf(mp3d, p_data.ptr(), src_data_len, MP3D_SEEK_TO_SAMPLE);224if (err || mp3d->info.hz == 0) {225memdelete(mp3d);226ERR_FAIL_MSG("Failed to decode mp3 file. Make sure it is a valid mp3 audio file.");227}228229channels = mp3d->info.channels;230sample_rate = mp3d->info.hz;231length = float(mp3d->samples) / (sample_rate * float(channels));232233mp3dec_ex_close(mp3d);234memdelete(mp3d);235236data = p_data;237data_len = src_data_len;238}239240Vector<uint8_t> AudioStreamMP3::get_data() const {241return data;242}243244void AudioStreamMP3::set_loop(bool p_enable) {245loop = p_enable;246}247248bool AudioStreamMP3::has_loop() const {249return loop;250}251252void AudioStreamMP3::set_loop_offset(double p_seconds) {253loop_offset = p_seconds;254}255256double AudioStreamMP3::get_loop_offset() const {257return loop_offset;258}259260double AudioStreamMP3::get_length() const {261return length;262}263264bool AudioStreamMP3::is_monophonic() const {265return false;266}267268void AudioStreamMP3::get_parameter_list(List<Parameter> *r_parameters) {269r_parameters->push_back(Parameter(PropertyInfo(Variant::BOOL, "looping", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_CHECKABLE), Variant()));270}271272void AudioStreamMP3::set_bpm(double p_bpm) {273ERR_FAIL_COND(p_bpm < 0);274bpm = p_bpm;275emit_changed();276}277278double AudioStreamMP3::get_bpm() const {279return bpm;280}281282void AudioStreamMP3::set_beat_count(int p_beat_count) {283ERR_FAIL_COND(p_beat_count < 0);284beat_count = p_beat_count;285emit_changed();286}287288int AudioStreamMP3::get_beat_count() const {289return beat_count;290}291292void AudioStreamMP3::set_bar_beats(int p_bar_beats) {293ERR_FAIL_COND(p_bar_beats < 0);294bar_beats = p_bar_beats;295emit_changed();296}297298int AudioStreamMP3::get_bar_beats() const {299return bar_beats;300}301302Ref<AudioSample> AudioStreamMP3::generate_sample() const {303Ref<AudioSample> sample;304sample.instantiate();305sample->stream = this;306sample->loop_mode = loop307? AudioSample::LoopMode::LOOP_FORWARD308: AudioSample::LoopMode::LOOP_DISABLED;309sample->loop_begin = loop_offset;310sample->loop_end = 0;311return sample;312}313314Ref<AudioStreamMP3> AudioStreamMP3::load_from_buffer(const Vector<uint8_t> &p_stream_data) {315Ref<AudioStreamMP3> mp3_stream;316mp3_stream.instantiate();317mp3_stream->set_data(p_stream_data);318ERR_FAIL_COND_V_MSG(mp3_stream->get_data().is_empty(), Ref<AudioStreamMP3>(), "MP3 decoding failed. Check that your data is a valid MP3 audio stream.");319return mp3_stream;320}321322Ref<AudioStreamMP3> AudioStreamMP3::load_from_file(const String &p_path) {323const Vector<uint8_t> stream_data = FileAccess::get_file_as_bytes(p_path);324ERR_FAIL_COND_V_MSG(stream_data.is_empty(), Ref<AudioStreamMP3>(), vformat("Cannot open file '%s'.", p_path));325return load_from_buffer(stream_data);326}327328void AudioStreamMP3::_bind_methods() {329ClassDB::bind_static_method("AudioStreamMP3", D_METHOD("load_from_buffer", "stream_data"), &AudioStreamMP3::load_from_buffer);330ClassDB::bind_static_method("AudioStreamMP3", D_METHOD("load_from_file", "path"), &AudioStreamMP3::load_from_file);331332ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMP3::set_data);333ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMP3::get_data);334335ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamMP3::set_loop);336ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamMP3::has_loop);337338ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamMP3::set_loop_offset);339ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamMP3::get_loop_offset);340341ClassDB::bind_method(D_METHOD("set_bpm", "bpm"), &AudioStreamMP3::set_bpm);342ClassDB::bind_method(D_METHOD("get_bpm"), &AudioStreamMP3::get_bpm);343344ClassDB::bind_method(D_METHOD("set_beat_count", "count"), &AudioStreamMP3::set_beat_count);345ClassDB::bind_method(D_METHOD("get_beat_count"), &AudioStreamMP3::get_beat_count);346347ClassDB::bind_method(D_METHOD("set_bar_beats", "count"), &AudioStreamMP3::set_bar_beats);348ClassDB::bind_method(D_METHOD("get_bar_beats"), &AudioStreamMP3::get_bar_beats);349350ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");351ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), "set_bpm", "get_bpm");352ADD_PROPERTY(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,1,or_greater"), "set_beat_count", "get_beat_count");353ADD_PROPERTY(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,1,or_greater"), "set_bar_beats", "get_bar_beats");354ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");355ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset");356}357358AudioStreamMP3::AudioStreamMP3() {359}360361AudioStreamMP3::~AudioStreamMP3() {362clear_data();363}364365366