Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Нетипові AudioStream-и

Вступ

AudioStream є базовим класом усіх об’єктів, що випромінюють звук. AudioStreamPlayer прив’язується до AudioStream, щоб передавати дані PCM на AudioServer, який керує аудіодрайверами.

Для всіх аудіоресурсів потрібні два аудіокласи: AudioStream і AudioStreamPlayback. Як контейнер даних, AudioStream містить ресурс і надає себе GDScript. AudioStream посилається на власний внутрішній настроюваний AudioStreamPlayback, який перетворює AudioStream у дані PCM.

Цей посібник передбачає, що читач знає, як створювати модулі C++. Якщо ні, зверніться до цього посібника Спеціальні модулі в C++.

Посилання:

для чого?

  • Прив’язка зовнішніх бібліотек (наприклад, Wwise, FMOD тощо).

  • Додавання нетипових черг обробки звукових даних

  • Додавання підтримки додаткових форматів звукових даних

Створення AudioStream

AudioStream складається з трьох компонентів: контейнер даних, ім’я потоку та генератор другорядного класу AudioStreamPlayback. Аудіодані можна завантажувати кількома способами, наприклад за допомогою внутрішнього лічильника тонального генератора, внутрішнього/зовнішнього буфера або посилання на файл.

Деякі аудіопотоки мають бути без стану, наприклад об’єкти, завантажені з ResourceLoader. ResourceLoader завантажується один раз і посилається на той самий об’єкт незалежно від того, скільки разів load викликається на певному ресурсі. Тому стан відтворення має бути самодостатнім у AudioStreamPlayback.

audiostream_mytone.h
#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();
};
audiostream_mytone.cpp
#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 контролюється звуковим потоком, введення/виведення та динамічний розподіл пам’яті заборонені.

audiostreamplayer_mytone.h
#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();
};
audiostreamplayer_mytone.cpp
#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;
}

Зміна частоти

Аудіосервер Godot наразі використовує частоту дискретизації 44100 Гц. Якщо потрібні інші частоти дискретизації, наприклад 48 000, задайте одну або використовуйте AudioStreamPlaybackResampled. Godot забезпечує кубічну інтерполяцію для повторної дискретизації звуку.

Замість перевантаження mix AudioStreamPlaybackResampled використовує _mix_internal для запиту AudioFrames і get_stream_sampling_rate для запиту поточної швидкості мікшування.

mytone_audiostream_resampled.h
#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();
};
mytone_audiostream_resampled.cpp
#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;
}

Посилання: