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은 모든 오디오 방출 객체의 기본 클래스입니다. AudioStreamPlayer는 AudioStream에 바인딩되어 오디오 드라이버를 관리하는 AudioServer에 PCM 데이터를 내보냅니다.

모든 오디오 리소스에는 AudioStream 및 AudioStreamPlayback이라는 두 가지 오디오 기반 클래스가 필요합니다. 데이터 컨테이너로서 AudioStream은 리소스를 포함하고 GDScript에 자신을 노출합니다. AudioStream은 AudioStream을 PCM 데이터로 변환하는 자체 내부 사용자 정의 AudioStreamPlayback을 참조합니다.

이 가이드에서는 독자가 C++ 모듈을 만드는 방법을 알고 있다고 가정합니다. 그렇지 않은 경우 이 가이드 :ref:`doc_custom_modules_in_cpp`를 참조하세요.

참고자료:

무엇을 위해?

  • 외부 라이브러리(예: Wwise, FMOD 등) 바인딩.

  • 사용자 정의 오디오 대기열 추가

  • 더 많은 오디오 형식에 대한 지원 추가

만들기 오디오스트림

AudioStream은 데이터 컨테이너, 스트림 이름 및 AudioStreamPlayback 프렌드 클래스 생성기의 세 가지 구성 요소로 구성됩니다. 오디오 데이터는 톤 발생기용 내부 카운터, 내부/외부 버퍼 또는 파일 참조 등 다양한 방법으로 로드할 수 있습니다.

일부 AudioStream은 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은 오디오 스레드에 의해 제어되므로 i/o 및 동적 메모리 할당이 금지됩니다.

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의 AudioServer는 현재 44100Hz 샘플 속도를 사용합니다. 48000과 같은 다른 샘플링 속도가 필요한 경우 하나를 제공하거나 AudioStreamPlaybackResampled를 사용하십시오. Godot는 오디오 리샘플링을 위한 3차 보간을 제공합니다.

``mix``를 오버로드하는 대신 AudioStreamPlaybackResampled는 ``_mix_internal``를 사용하여 AudioFrame을 쿼리하고 ``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;
}

참고자료: