Пользовательские аудиопотоки
Введение
AudioStream - это базовый класс всех объектов, передающих звук. AudioStreamPlayer привязывается к AudioStream для передачи PCM-данных в AudioServer, который управляет аудиодрайверами.
Все аудиоресурсы требуют наличия двух классов, основанных на аудио: AudioStream и AudioStreamPlayback. Как контейнер данных, AudioStream содержит ресурс и открывает себя для GDScript. AudioStream ссылается на свой собственный внутренний пользовательский AudioStreamPlayback, который переводит AudioStream в данные PCM.
Это руководство предполагает, что читатель знает, как создавать модули на C++. Если это не так, обратитесь к этому руководству Собственные модули на C++.
Источники:
Для чего это?
Привязка внешних библиотек (таких как Wwise, FMOD и т.д.).
Создание кастомных аудиоочередей
Добавление поддержки большего количества аудиоформатов
Создайте AudioStream
AudioStream состоит из трёх компонентов: контейнер данный, название потока и класс генератор AudioStreamPlayback. Аудио данные могут быть загружены множественными способами как внутренний счётчик или же генератором тона, внешним/внутренним буфером, или же при помощи указателя на файл.
Некоторые аудиопотоки (AudioStreams) должны быть без состояния, например, объекты, загруженные из ResourceLoader. ResourceLoader загружается один раз и ссылается на один и тот же объект независимо от того, сколько раз load вызывается для конкретного ресурса. Поэтому состояние воспроизведения должно быть самостоятельным в AudioStreamPlayback.
#include "core/reference.h"
#include "core/resource.h"
#include "servers/audio/audio_stream.h"
class AudioStreamMyTone : public AudioStream {
GDCLASS(AudioStreamMyTone, AudioStream)
private:
friend class AudioStreamPlaybackMyTone;
uint64_t pos;
int mix_rate;
bool stereo;
int hz;
public:
void reset();
void set_position(uint64_t pos);
virtual Ref<AudioStreamPlayback> instance_playback();
virtual String get_stream_name() const;
void gen_tone(int16_t *pcm_buf, int size);
virtual float get_length() const { return 0; } // if supported, otherwise return 0
AudioStreamMyTone();
protected:
static void _bind_methods();
};
#include "audiostream_mytone.h"
AudioStreamMyTone::AudioStreamMyTone()
: mix_rate(44100), stereo(false), hz(639) {
}
Ref<AudioStreamPlayback> AudioStreamMyTone::instance_playback() {
Ref<AudioStreamPlaybackMyTone> talking_tree;
talking_tree.instantiate();
talking_tree->base = Ref<AudioStreamMyTone>(this);
return talking_tree;
}
String AudioStreamMyTone::get_stream_name() const {
return "MyTone";
}
void AudioStreamMyTone::reset() {
set_position(0);
}
void AudioStreamMyTone::set_position(uint64_t p) {
pos = p;
}
void AudioStreamMyTone::gen_tone(int16_t *pcm_buf, int size) {
for (int i = 0; i < size; i++) {
pcm_buf[i] = 32767.0 * sin(2.0 * Math_PI * double(pos + i) / (double(mix_rate) / double(hz)));
}
pos += size;
}
void AudioStreamMyTone::_bind_methods() {
ClassDB::bind_method(D_METHOD("reset"), &AudioStreamMyTone::reset);
ClassDB::bind_method(D_METHOD("get_stream_name"), &AudioStreamMyTone::get_stream_name);
}
Источники:
Создайте AudioStreamPlayback
AudioStreamPlayer использует функцию обратного вызова mix для получения данных PCM. Функция обратного вызова должна соответствовать частоте дискретизации и заполнять буфер.
Поскольку AudioStreamPlayback контролируется аудиопотоком, то ввод/вывод и динамическое выделение памяти запрещены.
#include "core/reference.h"
#include "core/resource.h"
#include "servers/audio/audio_stream.h"
class AudioStreamPlaybackMyTone : public AudioStreamPlayback {
GDCLASS(AudioStreamPlaybackMyTone, AudioStreamPlayback)
friend class AudioStreamMyTone;
private:
enum {
PCM_BUFFER_SIZE = 4096
};
enum {
MIX_FRAC_BITS = 13,
MIX_FRAC_LEN = (1 << MIX_FRAC_BITS),
MIX_FRAC_MASK = MIX_FRAC_LEN - 1,
};
void *pcm_buffer;
Ref<AudioStreamMyTone> base;
bool active;
public:
virtual void start(float p_from_pos = 0.0);
virtual void stop();
virtual bool is_playing() const;
virtual int get_loop_count() const; // times it looped
virtual float get_playback_position() const;
virtual void seek(float p_time);
virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames);
virtual float get_length() const; // if supported, otherwise return 0
AudioStreamPlaybackMyTone();
~AudioStreamPlaybackMyTone();
};
#include "audiostreamplayer_mytone.h"
#include "core/math/math_funcs.h"
#include "core/print_string.h"
AudioStreamPlaybackMyTone::AudioStreamPlaybackMyTone()
: active(false) {
AudioServer::get_singleton()->lock();
pcm_buffer = AudioServer::get_singleton()->audio_data_alloc(PCM_BUFFER_SIZE);
zeromem(pcm_buffer, PCM_BUFFER_SIZE);
AudioServer::get_singleton()->unlock();
}
AudioStreamPlaybackMyTone::~AudioStreamPlaybackMyTone() {
if(pcm_buffer) {
AudioServer::get_singleton()->audio_data_free(pcm_buffer);
pcm_buffer = NULL;
}
}
void AudioStreamPlaybackMyTone::stop() {
active = false;
base->reset();
}
void AudioStreamPlaybackMyTone::start(float p_from_pos) {
seek(p_from_pos);
active = true;
}
void AudioStreamPlaybackMyTone::seek(float p_time) {
float max = get_length();
if (p_time < 0) {
p_time = 0;
}
base->set_position(uint64_t(p_time * base->mix_rate) << MIX_FRAC_BITS);
}
void AudioStreamPlaybackMyTone::mix(AudioFrame *p_buffer, float p_rate, int p_frames) {
ERR_FAIL_COND(!active);
if (!active) {
return;
}
zeromem(pcm_buffer, PCM_BUFFER_SIZE);
int16_t *buf = (int16_t *)pcm_buffer;
base->gen_tone(buf, p_frames);
for(int i = 0; i < p_frames; i++) {
float sample = float(buf[i]) / 32767.0;
p_buffer[i] = AudioFrame(sample, sample);
}
}
int AudioStreamPlaybackMyTone::get_loop_count() const {
return 0;
}
float AudioStreamPlaybackMyTone::get_playback_position() const {
return 0.0;
}
float AudioStreamPlaybackMyTone::get_length() const {
return 0.0;
}
bool AudioStreamPlaybackMyTone::is_playing() const {
return active;
}
Передискретизация
В настоящее время AudioServer от Godot использует частоту дискретизации 44100 Гц. Если требуются другие частоты дискретизации, например, 48000 Гц, либо укажите её, либо используйте AudioStreamPlaybackResampled. Godot обеспечивает кубическую интерполяцию для передискретизации звука.
Вместо того, чтобы перегружать mix, AudioStreamPlaybackResampled использует _mix_internal, чтобы получить AudioFrames и get_stream_sampling_rate, чтобы получить текущую скорость микширования.
#include "core/reference.h"
#include "core/resource.h"
#include "servers/audio/audio_stream.h"
class AudioStreamMyToneResampled;
class AudioStreamPlaybackResampledMyTone : public AudioStreamPlaybackResampled {
GDCLASS(AudioStreamPlaybackResampledMyTone, AudioStreamPlaybackResampled)
friend class AudioStreamMyToneResampled;
private:
enum {
PCM_BUFFER_SIZE = 4096
};
enum {
MIX_FRAC_BITS = 13,
MIX_FRAC_LEN = (1 << MIX_FRAC_BITS),
MIX_FRAC_MASK = MIX_FRAC_LEN - 1,
};
void *pcm_buffer;
Ref<AudioStreamMyToneResampled> base;
bool active;
protected:
virtual void _mix_internal(AudioFrame *p_buffer, int p_frames);
public:
virtual void start(float p_from_pos = 0.0);
virtual void stop();
virtual bool is_playing() const;
virtual int get_loop_count() const; // times it looped
virtual float get_playback_position() const;
virtual void seek(float p_time);
virtual float get_length() const; // if supported, otherwise return 0
virtual float get_stream_sampling_rate();
AudioStreamPlaybackResampledMyTone();
~AudioStreamPlaybackResampledMyTone();
};
#include "mytone_audiostream_resampled.h"
#include "core/math/math_funcs.h"
#include "core/print_string.h"
AudioStreamPlaybackResampledMyTone::AudioStreamPlaybackResampledMyTone()
: active(false) {
AudioServer::get_singleton()->lock();
pcm_buffer = AudioServer::get_singleton()->audio_data_alloc(PCM_BUFFER_SIZE);
zeromem(pcm_buffer, PCM_BUFFER_SIZE);
AudioServer::get_singleton()->unlock();
}
AudioStreamPlaybackResampledMyTone::~AudioStreamPlaybackResampledMyTone() {
if (pcm_buffer) {
AudioServer::get_singleton()->audio_data_free(pcm_buffer);
pcm_buffer = NULL;
}
}
void AudioStreamPlaybackResampledMyTone::stop() {
active = false;
base->reset();
}
void AudioStreamPlaybackResampledMyTone::start(float p_from_pos) {
seek(p_from_pos);
active = true;
}
void AudioStreamPlaybackResampledMyTone::seek(float p_time) {
float max = get_length();
if (p_time < 0) {
p_time = 0;
}
base->set_position(uint64_t(p_time * base->mix_rate) << MIX_FRAC_BITS);
}
void AudioStreamPlaybackResampledMyTone::_mix_internal(AudioFrame *p_buffer, int p_frames) {
ERR_FAIL_COND(!active);
if (!active) {
return;
}
zeromem(pcm_buffer, PCM_BUFFER_SIZE);
int16_t *buf = (int16_t *)pcm_buffer;
base->gen_tone(buf, p_frames);
for(int i = 0; i < p_frames; i++) {
float sample = float(buf[i]) / 32767.0;
p_buffer[i] = AudioFrame(sample, sample);
}
}
float AudioStreamPlaybackResampledMyTone::get_stream_sampling_rate() {
return float(base->mix_rate);
}
int AudioStreamPlaybackResampledMyTone::get_loop_count() const {
return 0;
}
float AudioStreamPlaybackResampledMyTone::get_playback_position() const {
return 0.0;
}
float AudioStreamPlaybackResampledMyTone::get_length() const {
return 0.0;
}
bool AudioStreamPlaybackResampledMyTone::is_playing() const {
return active;
}