Rewrite of the AudioStream API
-Fixes long-standing issues regarding to playing a single stream multiple times simultanteously -Fixes wrong-looping, starting, caching, etc. Issues resulting from bad original design -Allows more interesting kinds of streams (stream graphs with streams inside streams!) in the future
This commit is contained in:
parent
b0aa49accb
commit
9741374617
21 changed files with 1433 additions and 506 deletions
|
@ -1,7 +1,7 @@
|
|||
#include "audio_stream_mpc.h"
|
||||
|
||||
|
||||
Error AudioStreamMPC::_open_file() {
|
||||
Error AudioStreamPlaybackMPC::_open_file() {
|
||||
|
||||
if (f) {
|
||||
memdelete(f);
|
||||
|
@ -41,7 +41,7 @@ Error AudioStreamMPC::_open_file() {
|
|||
return OK;
|
||||
}
|
||||
|
||||
void AudioStreamMPC::_close_file() {
|
||||
void AudioStreamPlaybackMPC::_close_file() {
|
||||
|
||||
if (f) {
|
||||
memdelete(f);
|
||||
|
@ -52,7 +52,7 @@ void AudioStreamMPC::_close_file() {
|
|||
data_ofs=0;
|
||||
}
|
||||
|
||||
int AudioStreamMPC::_read_file(void *p_dst,int p_bytes) {
|
||||
int AudioStreamPlaybackMPC::_read_file(void *p_dst,int p_bytes) {
|
||||
|
||||
if (f)
|
||||
return f->get_buffer((uint8_t*)p_dst,p_bytes);
|
||||
|
@ -68,7 +68,7 @@ int AudioStreamMPC::_read_file(void *p_dst,int p_bytes) {
|
|||
return p_bytes;
|
||||
}
|
||||
|
||||
bool AudioStreamMPC::_seek_file(int p_pos){
|
||||
bool AudioStreamPlaybackMPC::_seek_file(int p_pos){
|
||||
|
||||
if (p_pos<0 || p_pos>streamlen)
|
||||
return false;
|
||||
|
@ -83,7 +83,7 @@ bool AudioStreamMPC::_seek_file(int p_pos){
|
|||
return true;
|
||||
|
||||
}
|
||||
int AudioStreamMPC::_tell_file() const{
|
||||
int AudioStreamPlaybackMPC::_tell_file() const{
|
||||
|
||||
if (f)
|
||||
return f->get_pos();
|
||||
|
@ -93,13 +93,13 @@ int AudioStreamMPC::_tell_file() const{
|
|||
|
||||
}
|
||||
|
||||
int AudioStreamMPC::_sizeof_file() const{
|
||||
int AudioStreamPlaybackMPC::_sizeof_file() const{
|
||||
|
||||
//print_line("sizeof file, get: "+itos(streamlen));
|
||||
return streamlen;
|
||||
}
|
||||
|
||||
bool AudioStreamMPC::_canseek_file() const{
|
||||
bool AudioStreamPlaybackMPC::_canseek_file() const{
|
||||
|
||||
//print_line("canseek file, get true");
|
||||
return true;
|
||||
|
@ -107,51 +107,46 @@ bool AudioStreamMPC::_canseek_file() const{
|
|||
|
||||
/////////////////////
|
||||
|
||||
mpc_int32_t AudioStreamMPC::_mpc_read(mpc_reader *p_reader,void *p_dst, mpc_int32_t p_bytes) {
|
||||
mpc_int32_t AudioStreamPlaybackMPC::_mpc_read(mpc_reader *p_reader,void *p_dst, mpc_int32_t p_bytes) {
|
||||
|
||||
AudioStreamMPC *smpc=(AudioStreamMPC *)p_reader->data;
|
||||
AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
|
||||
return smpc->_read_file(p_dst,p_bytes);
|
||||
}
|
||||
|
||||
mpc_bool_t AudioStreamMPC::_mpc_seek(mpc_reader *p_reader,mpc_int32_t p_offset) {
|
||||
mpc_bool_t AudioStreamPlaybackMPC::_mpc_seek(mpc_reader *p_reader,mpc_int32_t p_offset) {
|
||||
|
||||
AudioStreamMPC *smpc=(AudioStreamMPC *)p_reader->data;
|
||||
AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
|
||||
return smpc->_seek_file(p_offset);
|
||||
|
||||
}
|
||||
mpc_int32_t AudioStreamMPC::_mpc_tell(mpc_reader *p_reader) {
|
||||
mpc_int32_t AudioStreamPlaybackMPC::_mpc_tell(mpc_reader *p_reader) {
|
||||
|
||||
AudioStreamMPC *smpc=(AudioStreamMPC *)p_reader->data;
|
||||
AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
|
||||
return smpc->_tell_file();
|
||||
|
||||
}
|
||||
mpc_int32_t AudioStreamMPC::_mpc_get_size(mpc_reader *p_reader) {
|
||||
mpc_int32_t AudioStreamPlaybackMPC::_mpc_get_size(mpc_reader *p_reader) {
|
||||
|
||||
AudioStreamMPC *smpc=(AudioStreamMPC *)p_reader->data;
|
||||
AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
|
||||
return smpc->_sizeof_file();
|
||||
|
||||
|
||||
}
|
||||
mpc_bool_t AudioStreamMPC::_mpc_canseek(mpc_reader *p_reader) {
|
||||
mpc_bool_t AudioStreamPlaybackMPC::_mpc_canseek(mpc_reader *p_reader) {
|
||||
|
||||
AudioStreamMPC *smpc=(AudioStreamMPC *)p_reader->data;
|
||||
AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
|
||||
return smpc->_canseek_file();
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool AudioStreamMPC::_can_mix() const {
|
||||
|
||||
return /*active &&*/ !paused;
|
||||
}
|
||||
|
||||
|
||||
void AudioStreamMPC::update() {
|
||||
int AudioStreamPlaybackMPC::mix(int16_t* p_bufer,int p_frames) {
|
||||
|
||||
if (!active || paused)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
int todo=get_todo();
|
||||
int todo=p_frames;
|
||||
|
||||
while(todo>MPC_DECODER_BUFFER_LENGTH/si.channels) {
|
||||
|
||||
|
@ -162,7 +157,7 @@ void AudioStreamMPC::update() {
|
|||
mpc_status err = mpc_demux_decode(demux, &frame);
|
||||
if (frame.bits!=-1) {
|
||||
|
||||
int16_t *dst_buff = get_write_buffer();
|
||||
int16_t *dst_buff = p_bufer;
|
||||
|
||||
#ifdef MPC_FIXED_POINT
|
||||
|
||||
|
@ -185,21 +180,21 @@ void AudioStreamMPC::update() {
|
|||
#endif
|
||||
|
||||
int frames = frame.samples;
|
||||
write(frames);
|
||||
p_bufer+=si.channels*frames;
|
||||
todo-=frames;
|
||||
} else {
|
||||
|
||||
if (err != MPC_STATUS_OK) {
|
||||
|
||||
stop();
|
||||
ERR_EXPLAIN("Error decoding MPC");
|
||||
ERR_FAIL();
|
||||
ERR_PRINT("Error decoding MPC");
|
||||
break;
|
||||
} else {
|
||||
|
||||
//finished
|
||||
if (!loop) {
|
||||
stop();
|
||||
return;
|
||||
break;
|
||||
} else {
|
||||
|
||||
|
||||
|
@ -213,9 +208,11 @@ void AudioStreamMPC::update() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return p_frames-todo;
|
||||
}
|
||||
|
||||
Error AudioStreamMPC::_reload() {
|
||||
Error AudioStreamPlaybackMPC::_reload() {
|
||||
|
||||
ERR_FAIL_COND_V(demux!=NULL, ERR_FILE_ALREADY_IN_USE);
|
||||
|
||||
|
@ -224,31 +221,40 @@ Error AudioStreamMPC::_reload() {
|
|||
|
||||
demux = mpc_demux_init(&reader);
|
||||
ERR_FAIL_COND_V(!demux,ERR_CANT_CREATE);
|
||||
|
||||
mpc_demux_get_info(demux, &si);
|
||||
_setup(si.channels,si.sample_freq,MPC_DECODER_BUFFER_LENGTH*2/si.channels);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void AudioStreamMPC::set_file(const String& p_file) {
|
||||
void AudioStreamPlaybackMPC::set_file(const String& p_file) {
|
||||
|
||||
file=p_file;
|
||||
|
||||
Error err = _open_file();
|
||||
ERR_FAIL_COND(err!=OK);
|
||||
demux = mpc_demux_init(&reader);
|
||||
ERR_FAIL_COND(!demux);
|
||||
mpc_demux_get_info(demux, &si);
|
||||
stream_min_size=MPC_DECODER_BUFFER_LENGTH*2/si.channels;
|
||||
stream_rate=si.sample_freq;
|
||||
stream_channels=si.channels;
|
||||
|
||||
mpc_demux_exit(demux);
|
||||
demux=NULL;
|
||||
_close_file();
|
||||
|
||||
}
|
||||
|
||||
|
||||
String AudioStreamMPC::get_file() const {
|
||||
String AudioStreamPlaybackMPC::get_file() const {
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
void AudioStreamMPC::play() {
|
||||
void AudioStreamPlaybackMPC::play(float p_offset) {
|
||||
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
if (active)
|
||||
stop();
|
||||
active=false;
|
||||
|
@ -262,9 +268,9 @@ void AudioStreamMPC::play() {
|
|||
|
||||
}
|
||||
|
||||
void AudioStreamMPC::stop() {
|
||||
void AudioStreamPlaybackMPC::stop() {
|
||||
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
if (!active)
|
||||
return;
|
||||
if (demux) {
|
||||
|
@ -275,70 +281,58 @@ void AudioStreamMPC::stop() {
|
|||
active=false;
|
||||
|
||||
}
|
||||
bool AudioStreamMPC::is_playing() const {
|
||||
bool AudioStreamPlaybackMPC::is_playing() const {
|
||||
|
||||
return active || (get_total() - get_todo() -1 > 0);
|
||||
return active;
|
||||
}
|
||||
|
||||
void AudioStreamMPC::set_paused(bool p_paused) {
|
||||
|
||||
paused=p_paused;
|
||||
}
|
||||
bool AudioStreamMPC::is_paused(bool p_paused) const {
|
||||
|
||||
return paused;
|
||||
}
|
||||
|
||||
void AudioStreamMPC::set_loop(bool p_enable) {
|
||||
void AudioStreamPlaybackMPC::set_loop(bool p_enable) {
|
||||
|
||||
loop=p_enable;
|
||||
}
|
||||
bool AudioStreamMPC::has_loop() const {
|
||||
bool AudioStreamPlaybackMPC::has_loop() const {
|
||||
|
||||
return loop;
|
||||
}
|
||||
|
||||
float AudioStreamMPC::get_length() const {
|
||||
float AudioStreamPlaybackMPC::get_length() const {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
String AudioStreamMPC::get_stream_name() const {
|
||||
String AudioStreamPlaybackMPC::get_stream_name() const {
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
int AudioStreamMPC::get_loop_count() const {
|
||||
int AudioStreamPlaybackMPC::get_loop_count() const {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
float AudioStreamMPC::get_pos() const {
|
||||
float AudioStreamPlaybackMPC::get_pos() const {
|
||||
|
||||
return 0;
|
||||
}
|
||||
void AudioStreamMPC::seek_pos(float p_time) {
|
||||
void AudioStreamPlaybackMPC::seek_pos(float p_time) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
AudioStream::UpdateMode AudioStreamMPC::get_update_mode() const {
|
||||
|
||||
return UPDATE_THREAD;
|
||||
}
|
||||
void AudioStreamPlaybackMPC::_bind_methods() {
|
||||
|
||||
void AudioStreamMPC::_bind_methods() {
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("set_file","name"),&AudioStreamMPC::set_file);
|
||||
ObjectTypeDB::bind_method(_MD("get_file"),&AudioStreamMPC::get_file);
|
||||
ObjectTypeDB::bind_method(_MD("set_file","name"),&AudioStreamPlaybackMPC::set_file);
|
||||
ObjectTypeDB::bind_method(_MD("get_file"),&AudioStreamPlaybackMPC::get_file);
|
||||
|
||||
ADD_PROPERTYNZ( PropertyInfo(Variant::STRING,"file",PROPERTY_HINT_FILE,"mpc"), _SCS("set_file"), _SCS("get_file"));
|
||||
|
||||
}
|
||||
|
||||
AudioStreamMPC::AudioStreamMPC() {
|
||||
AudioStreamPlaybackMPC::AudioStreamPlaybackMPC() {
|
||||
|
||||
preload=true;
|
||||
preload=false;
|
||||
f=NULL;
|
||||
streamlen=0;
|
||||
data_ofs=0;
|
||||
|
@ -356,7 +350,7 @@ AudioStreamMPC::AudioStreamMPC() {
|
|||
|
||||
}
|
||||
|
||||
AudioStreamMPC::~AudioStreamMPC() {
|
||||
AudioStreamPlaybackMPC::~AudioStreamPlaybackMPC() {
|
||||
|
||||
stop();
|
||||
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
#ifndef AUDIO_STREAM_MPC_H
|
||||
#define AUDIO_STREAM_MPC_H
|
||||
|
||||
#include "scene/resources/audio_stream_resampled.h"
|
||||
#include "scene/resources/audio_stream.h"
|
||||
#include "os/file_access.h"
|
||||
#include "mpc/mpcdec.h"
|
||||
#include "os/thread_safe.h"
|
||||
#include "io/resource_loader.h"
|
||||
//#include "../libmpcdec/decoder.h"
|
||||
//#include "../libmpcdec/internal.h"
|
||||
class AudioStreamMPC : public AudioStreamResampled {
|
||||
|
||||
OBJ_TYPE( AudioStreamMPC, AudioStreamResampled );
|
||||
class AudioStreamPlaybackMPC : public AudioStreamPlayback {
|
||||
|
||||
_THREAD_SAFE_CLASS_
|
||||
OBJ_TYPE( AudioStreamPlaybackMPC, AudioStreamPlayback );
|
||||
|
||||
bool preload;
|
||||
FileAccess *f;
|
||||
|
@ -39,7 +38,9 @@ class AudioStreamMPC : public AudioStreamResampled {
|
|||
static mpc_int32_t _mpc_get_size(mpc_reader *p_reader);
|
||||
static mpc_bool_t _mpc_canseek(mpc_reader *p_reader);
|
||||
|
||||
virtual bool _can_mix() const ;
|
||||
int stream_min_size;
|
||||
int stream_rate;
|
||||
int stream_channels;
|
||||
|
||||
protected:
|
||||
Error _open_file();
|
||||
|
@ -59,12 +60,10 @@ public:
|
|||
void set_file(const String& p_file);
|
||||
String get_file() const;
|
||||
|
||||
virtual void play();
|
||||
virtual void play(float p_offset=0);
|
||||
virtual void stop();
|
||||
virtual bool is_playing() const;
|
||||
|
||||
virtual void set_paused(bool p_paused);
|
||||
virtual bool is_paused(bool p_paused) const;
|
||||
|
||||
virtual void set_loop(bool p_enable);
|
||||
virtual bool has_loop() const;
|
||||
|
@ -78,13 +77,35 @@ public:
|
|||
virtual float get_pos() const;
|
||||
virtual void seek_pos(float p_time);
|
||||
|
||||
virtual UpdateMode get_update_mode() const;
|
||||
virtual void update();
|
||||
virtual int get_channels() const { return stream_channels; }
|
||||
virtual int get_mix_rate() const { return stream_rate; }
|
||||
|
||||
AudioStreamMPC();
|
||||
~AudioStreamMPC();
|
||||
virtual int get_minimum_buffer_size() const { return stream_min_size; }
|
||||
virtual int mix(int16_t* p_bufer,int p_frames);
|
||||
|
||||
virtual void set_loop_restart_time(float p_time) { }
|
||||
|
||||
AudioStreamPlaybackMPC();
|
||||
~AudioStreamPlaybackMPC();
|
||||
};
|
||||
|
||||
class AudioStreamMPC : public AudioStream {
|
||||
|
||||
OBJ_TYPE( AudioStreamMPC, AudioStream );
|
||||
|
||||
String file;
|
||||
public:
|
||||
|
||||
Ref<AudioStreamPlayback> instance_playback() {
|
||||
Ref<AudioStreamPlaybackMPC> pb = memnew( AudioStreamPlaybackMPC );
|
||||
pb->set_file(file);
|
||||
return pb;
|
||||
}
|
||||
|
||||
void set_file(const String& p_file) { file=p_file; }
|
||||
|
||||
|
||||
};
|
||||
|
||||
class ResourceFormatLoaderAudioStreamMPC : public ResourceFormatLoader {
|
||||
public:
|
||||
|
|
|
@ -15,14 +15,15 @@ static _FORCE_INLINE_ uint16_t le_short(uint16_t s)
|
|||
}
|
||||
|
||||
|
||||
void AudioStreamSpeex::update() {
|
||||
int AudioStreamPlaybackSpeex::mix(int16_t* p_buffer,int p_frames) {
|
||||
|
||||
|
||||
|
||||
_THREAD_SAFE_METHOD_;
|
||||
//printf("update, loops %i, read ofs %i\n", (int)loops, read_ofs);
|
||||
//printf("playing %i, paused %i\n", (int)playing, (int)paused);
|
||||
|
||||
if (!active || !playing || paused || !data.size())
|
||||
return;
|
||||
return 0;
|
||||
|
||||
/*
|
||||
if (read_ofs >= data.size()) {
|
||||
|
@ -35,12 +36,13 @@ void AudioStreamSpeex::update() {
|
|||
};
|
||||
*/
|
||||
|
||||
int todo = get_todo();
|
||||
int todo = p_frames;
|
||||
if (todo < page_size) {
|
||||
return;
|
||||
return 0;
|
||||
};
|
||||
|
||||
int eos = 0;
|
||||
bool reloaded=false;
|
||||
|
||||
while (todo > page_size) {
|
||||
|
||||
|
@ -92,7 +94,7 @@ void AudioStreamSpeex::update() {
|
|||
for (int j=0;j!=nframes;j++)
|
||||
{
|
||||
|
||||
int16_t* out = get_write_buffer();
|
||||
int16_t* out = p_buffer;
|
||||
|
||||
int ret;
|
||||
/*Decode frame*/
|
||||
|
@ -120,7 +122,7 @@ void AudioStreamSpeex::update() {
|
|||
|
||||
|
||||
/*Convert to short and save to output file*/
|
||||
for (int i=0;i<frame_size*get_channel_count();i++) {
|
||||
for (int i=0;i<frame_size*stream_channels;i++) {
|
||||
out[i]=le_short(out[i]);
|
||||
}
|
||||
|
||||
|
@ -149,7 +151,7 @@ void AudioStreamSpeex::update() {
|
|||
}
|
||||
|
||||
|
||||
write(new_frame_size);
|
||||
p_buffer+=new_frame_size*stream_channels;
|
||||
todo-=new_frame_size;
|
||||
}
|
||||
}
|
||||
|
@ -175,6 +177,7 @@ void AudioStreamSpeex::update() {
|
|||
if (loops) {
|
||||
reload();
|
||||
++loop_count;
|
||||
//break;
|
||||
} else {
|
||||
playing=false;
|
||||
unload();
|
||||
|
@ -183,18 +186,22 @@ void AudioStreamSpeex::update() {
|
|||
}
|
||||
};
|
||||
};
|
||||
|
||||
return p_frames-todo;
|
||||
};
|
||||
|
||||
|
||||
void AudioStreamSpeex::unload() {
|
||||
void AudioStreamPlaybackSpeex::unload() {
|
||||
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
if (!active) return;
|
||||
|
||||
speex_bits_destroy(&bits);
|
||||
if (st)
|
||||
speex_decoder_destroy(st);
|
||||
|
||||
ogg_sync_clear(&oy);
|
||||
active = false;
|
||||
//data.resize(0);
|
||||
st = NULL;
|
||||
|
@ -204,7 +211,7 @@ void AudioStreamSpeex::unload() {
|
|||
loop_count = 0;
|
||||
}
|
||||
|
||||
void *AudioStreamSpeex::process_header(ogg_packet *op, int *frame_size, int *rate, int *nframes, int *channels, int *extra_headers) {
|
||||
void *AudioStreamPlaybackSpeex::process_header(ogg_packet *op, int *frame_size, int *rate, int *nframes, int *channels, int *extra_headers) {
|
||||
|
||||
void *st;
|
||||
SpeexHeader *header;
|
||||
|
@ -276,9 +283,9 @@ void *AudioStreamSpeex::process_header(ogg_packet *op, int *frame_size, int *rat
|
|||
|
||||
|
||||
|
||||
void AudioStreamSpeex::reload() {
|
||||
void AudioStreamPlaybackSpeex::reload() {
|
||||
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
if (active)
|
||||
unload();
|
||||
|
@ -359,8 +366,10 @@ void AudioStreamSpeex::reload() {
|
|||
};
|
||||
|
||||
page_size = nframes * frame_size;
|
||||
stream_srate=rate;
|
||||
stream_channels=channels;
|
||||
stream_minbuff_size=page_size;
|
||||
|
||||
_setup(channels, rate,page_size);
|
||||
|
||||
} else if (packet_count==1)
|
||||
{
|
||||
|
@ -374,23 +383,23 @@ void AudioStreamSpeex::reload() {
|
|||
|
||||
} while (packet_count <= extra_headers);
|
||||
|
||||
active = true;
|
||||
active=true;
|
||||
|
||||
}
|
||||
|
||||
void AudioStreamSpeex::_bind_methods() {
|
||||
void AudioStreamPlaybackSpeex::_bind_methods() {
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("set_file","file"),&AudioStreamSpeex::set_file);
|
||||
ObjectTypeDB::bind_method(_MD("get_file"),&AudioStreamSpeex::get_file);
|
||||
//ObjectTypeDB::bind_method(_MD("set_file","file"),&AudioStreamPlaybackSpeex::set_file);
|
||||
// ObjectTypeDB::bind_method(_MD("get_file"),&AudioStreamPlaybackSpeex::get_file);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("_set_bundled"),&AudioStreamSpeex::_set_bundled);
|
||||
ObjectTypeDB::bind_method(_MD("_get_bundled"),&AudioStreamSpeex::_get_bundled);
|
||||
ObjectTypeDB::bind_method(_MD("_set_bundled"),&AudioStreamPlaybackSpeex::_set_bundled);
|
||||
ObjectTypeDB::bind_method(_MD("_get_bundled"),&AudioStreamPlaybackSpeex::_get_bundled);
|
||||
|
||||
ADD_PROPERTY( PropertyInfo(Variant::DICTIONARY,"_bundled",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_BUNDLE),_SCS("_set_bundled"),_SCS("_get_bundled"));
|
||||
ADD_PROPERTY( PropertyInfo(Variant::STRING,"file",PROPERTY_HINT_FILE,"*.spx"),_SCS("set_file"),_SCS("get_file"));
|
||||
//ADD_PROPERTY( PropertyInfo(Variant::STRING,"file",PROPERTY_HINT_FILE,"*.spx"),_SCS("set_file"),_SCS("get_file"));
|
||||
};
|
||||
|
||||
void AudioStreamSpeex::_set_bundled(const Dictionary& dict) {
|
||||
void AudioStreamPlaybackSpeex::_set_bundled(const Dictionary& dict) {
|
||||
|
||||
ERR_FAIL_COND( !dict.has("filename"));
|
||||
ERR_FAIL_COND( !dict.has("data"));
|
||||
|
@ -399,7 +408,7 @@ void AudioStreamSpeex::_set_bundled(const Dictionary& dict) {
|
|||
data = dict["data"];
|
||||
};
|
||||
|
||||
Dictionary AudioStreamSpeex::_get_bundled() const {
|
||||
Dictionary AudioStreamPlaybackSpeex::_get_bundled() const {
|
||||
|
||||
Dictionary d;
|
||||
d["filename"] = filename;
|
||||
|
@ -408,19 +417,101 @@ Dictionary AudioStreamSpeex::_get_bundled() const {
|
|||
};
|
||||
|
||||
|
||||
String AudioStreamSpeex::get_file() const {
|
||||
|
||||
return filename;
|
||||
void AudioStreamPlaybackSpeex::set_data(const Vector<uint8_t>& p_data) {
|
||||
|
||||
data=p_data;
|
||||
reload();
|
||||
}
|
||||
|
||||
|
||||
void AudioStreamPlaybackSpeex::play(float p_from_pos) {
|
||||
|
||||
|
||||
|
||||
reload();
|
||||
if (!active)
|
||||
return;
|
||||
playing = true;
|
||||
|
||||
}
|
||||
void AudioStreamPlaybackSpeex::stop(){
|
||||
|
||||
|
||||
unload();
|
||||
playing = false;
|
||||
|
||||
}
|
||||
bool AudioStreamPlaybackSpeex::is_playing() const{
|
||||
|
||||
return playing;
|
||||
}
|
||||
|
||||
|
||||
void AudioStreamPlaybackSpeex::set_loop(bool p_enable){
|
||||
|
||||
loops = p_enable;
|
||||
}
|
||||
bool AudioStreamPlaybackSpeex::has_loop() const{
|
||||
|
||||
return loops;
|
||||
}
|
||||
|
||||
float AudioStreamPlaybackSpeex::get_length() const{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
String AudioStreamPlaybackSpeex::get_stream_name() const{
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
int AudioStreamPlaybackSpeex::get_loop_count() const{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
float AudioStreamPlaybackSpeex::get_pos() const{
|
||||
|
||||
return 0;
|
||||
}
|
||||
void AudioStreamPlaybackSpeex::seek_pos(float p_time){
|
||||
|
||||
|
||||
};
|
||||
|
||||
void AudioStreamSpeex::set_file(const String& p_file){
|
||||
|
||||
if (filename == p_file)
|
||||
|
||||
AudioStreamPlaybackSpeex::AudioStreamPlaybackSpeex() {
|
||||
|
||||
active=false;
|
||||
st = NULL;
|
||||
stream_channels=1;
|
||||
stream_srate=1;
|
||||
stream_minbuff_size=1;
|
||||
|
||||
}
|
||||
|
||||
AudioStreamPlaybackSpeex::~AudioStreamPlaybackSpeex() {
|
||||
|
||||
unload();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
void AudioStreamSpeex::set_file(const String& p_file) {
|
||||
|
||||
if (this->file == p_file)
|
||||
return;
|
||||
|
||||
if (active) {
|
||||
unload();
|
||||
}
|
||||
this->file=p_file;
|
||||
|
||||
if (p_file == "") {
|
||||
data.resize(0);
|
||||
|
@ -434,100 +525,11 @@ void AudioStreamSpeex::set_file(const String& p_file){
|
|||
};
|
||||
ERR_FAIL_COND(err != OK);
|
||||
|
||||
filename = p_file;
|
||||
this->file = p_file;
|
||||
data.resize(file->get_len());
|
||||
int read = file->get_buffer(&data[0], data.size());
|
||||
memdelete(file);
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
void AudioStreamSpeex::play() {
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
reload();
|
||||
if (!active)
|
||||
return;
|
||||
playing = true;
|
||||
|
||||
}
|
||||
void AudioStreamSpeex::stop(){
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
unload();
|
||||
playing = false;
|
||||
_clear();
|
||||
}
|
||||
bool AudioStreamSpeex::is_playing() const{
|
||||
|
||||
return _is_ready() && (playing || (get_total() - get_todo() -1 > 0));
|
||||
}
|
||||
|
||||
void AudioStreamSpeex::set_paused(bool p_paused){
|
||||
|
||||
playing = !p_paused;
|
||||
paused = p_paused;
|
||||
}
|
||||
bool AudioStreamSpeex::is_paused(bool p_paused) const{
|
||||
|
||||
return paused;
|
||||
}
|
||||
|
||||
void AudioStreamSpeex::set_loop(bool p_enable){
|
||||
|
||||
loops = p_enable;
|
||||
}
|
||||
bool AudioStreamSpeex::has_loop() const{
|
||||
|
||||
return loops;
|
||||
}
|
||||
|
||||
float AudioStreamSpeex::get_length() const{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
String AudioStreamSpeex::get_stream_name() const{
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
int AudioStreamSpeex::get_loop_count() const{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
float AudioStreamSpeex::get_pos() const{
|
||||
|
||||
return 0;
|
||||
}
|
||||
void AudioStreamSpeex::seek_pos(float p_time){
|
||||
|
||||
|
||||
};
|
||||
|
||||
bool AudioStreamSpeex::_can_mix() const {
|
||||
|
||||
//return playing;
|
||||
return data.size() != 0;
|
||||
};
|
||||
|
||||
|
||||
AudioStream::UpdateMode AudioStreamSpeex::get_update_mode() const {
|
||||
|
||||
return UPDATE_THREAD;
|
||||
}
|
||||
|
||||
AudioStreamSpeex::AudioStreamSpeex() {
|
||||
|
||||
active=false;
|
||||
st = NULL;
|
||||
}
|
||||
|
||||
AudioStreamSpeex::~AudioStreamSpeex() {
|
||||
|
||||
unload();
|
||||
}
|
||||
|
||||
RES ResourceFormatLoaderAudioStreamSpeex::load(const String &p_path, const String& p_original_path, Error *r_error) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef AUDIO_STREAM_SPEEX_H
|
||||
#define AUDIO_STREAM_SPEEX_H
|
||||
|
||||
#include "scene/resources/audio_stream_resampled.h"
|
||||
#include "scene/resources/audio_stream.h"
|
||||
#include "speex/speex.h"
|
||||
#include "os/file_access.h"
|
||||
#include "io/resource_loader.h"
|
||||
|
@ -14,10 +14,10 @@
|
|||
|
||||
#include <ogg/ogg.h>
|
||||
|
||||
class AudioStreamSpeex : public AudioStreamResampled {
|
||||
class AudioStreamPlaybackSpeex : public AudioStreamPlayback {
|
||||
|
||||
OBJ_TYPE(AudioStreamPlaybackSpeex, AudioStreamPlayback);
|
||||
|
||||
OBJ_TYPE(AudioStreamSpeex, AudioStreamResampled);
|
||||
_THREAD_SAFE_CLASS_
|
||||
|
||||
void *st;
|
||||
SpeexBits bits;
|
||||
|
@ -45,6 +45,9 @@ class AudioStreamSpeex : public AudioStreamResampled {
|
|||
|
||||
ogg_int64_t page_granule, last_granule;
|
||||
int skip_samples, page_nb_packets;
|
||||
int stream_channels;
|
||||
int stream_srate;
|
||||
int stream_minbuff_size;
|
||||
|
||||
void* process_header(ogg_packet *op, int *frame_size, int *rate, int *nframes, int *channels, int *extra_headers);
|
||||
|
||||
|
@ -52,7 +55,7 @@ class AudioStreamSpeex : public AudioStreamResampled {
|
|||
|
||||
protected:
|
||||
|
||||
virtual bool _can_mix() const;
|
||||
//virtual bool _can_mix() const;
|
||||
|
||||
Dictionary _get_bundled() const;
|
||||
void _set_bundled(const Dictionary& dict);
|
||||
|
@ -60,16 +63,12 @@ protected:
|
|||
public:
|
||||
|
||||
|
||||
void set_file(const String& p_file);
|
||||
String get_file() const;
|
||||
void set_data(const Vector<uint8_t>& p_data);
|
||||
|
||||
virtual void play();
|
||||
virtual void play(float p_from_pos=0);
|
||||
virtual void stop();
|
||||
virtual bool is_playing() const;
|
||||
|
||||
virtual void set_paused(bool p_paused);
|
||||
virtual bool is_paused(bool p_paused) const;
|
||||
|
||||
virtual void set_loop(bool p_enable);
|
||||
virtual bool has_loop() const;
|
||||
|
||||
|
@ -82,13 +81,39 @@ public:
|
|||
virtual float get_pos() const;
|
||||
virtual void seek_pos(float p_time);
|
||||
|
||||
virtual UpdateMode get_update_mode() const;
|
||||
virtual void update();
|
||||
virtual int get_channels() const { return stream_channels; }
|
||||
virtual int get_mix_rate() const { return stream_srate; }
|
||||
|
||||
AudioStreamSpeex();
|
||||
~AudioStreamSpeex();
|
||||
virtual int get_minimum_buffer_size() const { return stream_minbuff_size; }
|
||||
virtual int mix(int16_t* p_bufer,int p_frames);
|
||||
|
||||
virtual void set_loop_restart_time(float p_time) { } //no loop restart, ignore
|
||||
|
||||
AudioStreamPlaybackSpeex();
|
||||
~AudioStreamPlaybackSpeex();
|
||||
};
|
||||
|
||||
|
||||
|
||||
class AudioStreamSpeex : public AudioStream {
|
||||
|
||||
OBJ_TYPE(AudioStreamSpeex,AudioStream);
|
||||
|
||||
Vector<uint8_t> data;
|
||||
String file;
|
||||
public:
|
||||
|
||||
Ref<AudioStreamPlayback> instance_playback() {
|
||||
Ref<AudioStreamPlaybackSpeex> pb = memnew( AudioStreamPlaybackSpeex );
|
||||
pb->set_data(data);
|
||||
return pb;
|
||||
}
|
||||
|
||||
void set_file(const String& p_file);
|
||||
|
||||
};
|
||||
|
||||
|
||||
class ResourceFormatLoaderAudioStreamSpeex : public ResourceFormatLoader {
|
||||
public:
|
||||
virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
|
||||
|
||||
size_t AudioStreamOGGVorbis::_ov_read_func(void *p_dst,size_t p_data, size_t p_count, void *_f) {
|
||||
size_t AudioStreamPlaybackOGGVorbis::_ov_read_func(void *p_dst,size_t p_data, size_t p_count, void *_f) {
|
||||
|
||||
//printf("read to %p, %i bytes, %i nmemb, %p\n",p_dst,p_data,p_count,_f);
|
||||
FileAccess *fa=(FileAccess*)_f;
|
||||
|
@ -46,7 +46,7 @@ size_t AudioStreamOGGVorbis::_ov_read_func(void *p_dst,size_t p_data, size_t p_c
|
|||
return read;
|
||||
}
|
||||
|
||||
int AudioStreamOGGVorbis::_ov_seek_func(void *_f,ogg_int64_t offs, int whence) {
|
||||
int AudioStreamPlaybackOGGVorbis::_ov_seek_func(void *_f,ogg_int64_t offs, int whence) {
|
||||
|
||||
//printf("seek to %p, offs %i, whence %i\n",_f,(int)offs,whence);
|
||||
|
||||
|
@ -76,7 +76,7 @@ int AudioStreamOGGVorbis::_ov_seek_func(void *_f,ogg_int64_t offs, int whence) {
|
|||
#endif
|
||||
|
||||
}
|
||||
int AudioStreamOGGVorbis::_ov_close_func(void *_f) {
|
||||
int AudioStreamPlaybackOGGVorbis::_ov_close_func(void *_f) {
|
||||
|
||||
// printf("close %p\n",_f);
|
||||
if (!_f)
|
||||
|
@ -86,7 +86,7 @@ int AudioStreamOGGVorbis::_ov_close_func(void *_f) {
|
|||
fa->close();
|
||||
return 0;
|
||||
}
|
||||
long AudioStreamOGGVorbis::_ov_tell_func(void *_f) {
|
||||
long AudioStreamPlaybackOGGVorbis::_ov_tell_func(void *_f) {
|
||||
|
||||
//printf("close %p\n",_f);
|
||||
|
||||
|
@ -95,38 +95,32 @@ long AudioStreamOGGVorbis::_ov_tell_func(void *_f) {
|
|||
}
|
||||
|
||||
|
||||
bool AudioStreamOGGVorbis::_can_mix() const {
|
||||
|
||||
return /*playing &&*/ !paused;
|
||||
}
|
||||
int AudioStreamPlaybackOGGVorbis::mix(int16_t* p_bufer,int p_frames) {
|
||||
|
||||
if (!playing)
|
||||
return 0;
|
||||
|
||||
void AudioStreamOGGVorbis::update() {
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
if (!playing && !setting_up)
|
||||
return;
|
||||
|
||||
int total=p_frames;
|
||||
while (true) {
|
||||
|
||||
int todo = get_todo();
|
||||
int todo = p_frames;
|
||||
|
||||
if (todo==0 || todo<MIN_MIX)
|
||||
if (todo==0 || todo<MIN_MIX) {
|
||||
break;
|
||||
}
|
||||
|
||||
//printf("to mix %i - mix me %i bytes\n",to_mix,to_mix*stream_channels*sizeof(int16_t));
|
||||
|
||||
#ifdef BIG_ENDIAN_ENABLED
|
||||
long ret=ov_read(&vf,(char*)get_write_buffer(),todo*stream_channels*sizeof(int16_t), 1, 2, 1, ¤t_section);
|
||||
long ret=ov_read(&vf,(char*)p_bufer,todo*stream_channels*sizeof(int16_t), 1, 2, 1, ¤t_section);
|
||||
#else
|
||||
long ret=ov_read(&vf,(char*)get_write_buffer(),todo*stream_channels*sizeof(int16_t), 0, 2, 1, ¤t_section);
|
||||
long ret=ov_read(&vf,(char*)p_bufer,todo*stream_channels*sizeof(int16_t), 0, 2, 1, ¤t_section);
|
||||
#endif
|
||||
|
||||
if (ret<0) {
|
||||
|
||||
playing = false;
|
||||
setting_up=false;
|
||||
|
||||
ERR_EXPLAIN("Error reading OGG Vorbis File: "+file);
|
||||
ERR_BREAK(ret<0);
|
||||
} else if (ret==0) { // end of song, reload?
|
||||
|
@ -138,9 +132,8 @@ void AudioStreamOGGVorbis::update() {
|
|||
if (!has_loop()) {
|
||||
|
||||
playing=false;
|
||||
setting_up=false;
|
||||
repeats=1;
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
f=FileAccess::open(file,FileAccess::READ);
|
||||
|
@ -148,11 +141,22 @@ void AudioStreamOGGVorbis::update() {
|
|||
int errv = ov_open_callbacks(f,&vf,NULL,0,_ov_callbacks);
|
||||
if (errv!=0) {
|
||||
playing=false;
|
||||
setting_up=false;
|
||||
return; // :(
|
||||
break;; // :(
|
||||
}
|
||||
|
||||
if (loop_restart_time) {
|
||||
bool ok = ov_time_seek(&vf,loop_restart_time)==0;
|
||||
if (!ok) {
|
||||
playing=false;
|
||||
//ERR_EXPLAIN("loop restart time rejected");
|
||||
ERR_PRINT("loop restart time rejected")
|
||||
}
|
||||
|
||||
frames_mixed=stream_srate*loop_restart_time;
|
||||
} else {
|
||||
|
||||
frames_mixed=0;
|
||||
}
|
||||
repeats++;
|
||||
continue;
|
||||
|
||||
|
@ -162,16 +166,19 @@ void AudioStreamOGGVorbis::update() {
|
|||
ret/=sizeof(int16_t);
|
||||
|
||||
frames_mixed+=ret;
|
||||
write(ret);
|
||||
|
||||
p_bufer+=ret*stream_channels;
|
||||
p_frames-=ret;
|
||||
|
||||
}
|
||||
|
||||
return total-p_frames;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioStreamOGGVorbis::play() {
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
void AudioStreamPlaybackOGGVorbis::play(float p_from) {
|
||||
|
||||
if (playing)
|
||||
stop();
|
||||
|
@ -179,56 +186,46 @@ void AudioStreamOGGVorbis::play() {
|
|||
if (_load_stream()!=OK)
|
||||
return;
|
||||
|
||||
|
||||
frames_mixed=0;
|
||||
playing=false;
|
||||
setting_up=true;
|
||||
update();
|
||||
if (!setting_up)
|
||||
return;
|
||||
setting_up=false;
|
||||
playing=true;
|
||||
if (p_from>0) {
|
||||
seek_pos(p_from);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamOGGVorbis::_close_file() {
|
||||
void AudioStreamPlaybackOGGVorbis::_close_file() {
|
||||
|
||||
if (f) {
|
||||
|
||||
memdelete(f);
|
||||
f=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamOGGVorbis::stop() {
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
bool AudioStreamPlaybackOGGVorbis::is_playing() const {
|
||||
return playing;
|
||||
}
|
||||
void AudioStreamPlaybackOGGVorbis::stop() {
|
||||
|
||||
_clear_stream();
|
||||
playing=false;
|
||||
_clear();
|
||||
}
|
||||
|
||||
AudioStreamOGGVorbis::UpdateMode AudioStreamOGGVorbis::get_update_mode() const {
|
||||
|
||||
return UPDATE_THREAD;
|
||||
//_clear();
|
||||
}
|
||||
|
||||
|
||||
bool AudioStreamOGGVorbis::is_playing() const {
|
||||
|
||||
float AudioStreamPlaybackOGGVorbis::get_pos() const {
|
||||
|
||||
return playing || (get_total() - get_todo() -1 > 0);
|
||||
}
|
||||
|
||||
float AudioStreamOGGVorbis::get_pos() const {
|
||||
|
||||
int32_t frames = int32_t(frames_mixed) - (int32_t(get_total()) - get_todo());
|
||||
int32_t frames = int32_t(frames_mixed);
|
||||
if (frames < 0)
|
||||
frames=0;
|
||||
return double(frames) / stream_srate;
|
||||
}
|
||||
|
||||
void AudioStreamOGGVorbis::seek_pos(float p_time) {
|
||||
void AudioStreamPlaybackOGGVorbis::seek_pos(float p_time) {
|
||||
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
if (!playing)
|
||||
return;
|
||||
|
@ -237,32 +234,75 @@ void AudioStreamOGGVorbis::seek_pos(float p_time) {
|
|||
frames_mixed=stream_srate*p_time;
|
||||
}
|
||||
|
||||
String AudioStreamOGGVorbis::get_stream_name() const {
|
||||
String AudioStreamPlaybackOGGVorbis::get_stream_name() const {
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void AudioStreamOGGVorbis::set_loop(bool p_enable) {
|
||||
void AudioStreamPlaybackOGGVorbis::set_loop(bool p_enable) {
|
||||
|
||||
loops=p_enable;
|
||||
}
|
||||
|
||||
bool AudioStreamOGGVorbis::has_loop() const {
|
||||
bool AudioStreamPlaybackOGGVorbis::has_loop() const {
|
||||
|
||||
return loops;
|
||||
}
|
||||
|
||||
int AudioStreamOGGVorbis::get_loop_count() const {
|
||||
int AudioStreamPlaybackOGGVorbis::get_loop_count() const {
|
||||
return repeats;
|
||||
}
|
||||
|
||||
|
||||
void AudioStreamOGGVorbis::set_file(const String& p_file) {
|
||||
Error AudioStreamPlaybackOGGVorbis::set_file(const String& p_file) {
|
||||
|
||||
file=p_file;
|
||||
stream_valid=false;
|
||||
Error err;
|
||||
f=FileAccess::open(file,FileAccess::READ,&err);
|
||||
|
||||
if (err) {
|
||||
ERR_FAIL_COND_V( err, err );
|
||||
}
|
||||
|
||||
int errv = ov_open_callbacks(f,&vf,NULL,0,_ov_callbacks);
|
||||
switch(errv) {
|
||||
|
||||
case OV_EREAD: { // - A read from media returned an error.
|
||||
memdelete(f); f=NULL;
|
||||
ERR_FAIL_V( ERR_FILE_CANT_READ );
|
||||
} break;
|
||||
case OV_EVERSION: // - Vorbis version mismatch.
|
||||
case OV_ENOTVORBIS: { // - Bitstream is not Vorbis data.
|
||||
memdelete(f); f=NULL;
|
||||
ERR_FAIL_V( ERR_FILE_UNRECOGNIZED );
|
||||
} break;
|
||||
case OV_EBADHEADER: { // - Invalid Vorbis bitstream header.
|
||||
memdelete(f); f=NULL;
|
||||
ERR_FAIL_V( ERR_FILE_CORRUPT );
|
||||
} break;
|
||||
case OV_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption.
|
||||
memdelete(f); f=NULL;
|
||||
ERR_FAIL_V( ERR_BUG );
|
||||
} break;
|
||||
}
|
||||
const vorbis_info *vinfo=ov_info(&vf,-1);
|
||||
stream_channels=vinfo->channels;
|
||||
stream_srate=vinfo->rate;
|
||||
ogg_int64_t len = ov_time_total(&vf,-1);
|
||||
length=len/1000.0;
|
||||
ov_clear(&vf);
|
||||
memdelete(f);
|
||||
f=NULL;
|
||||
stream_valid=true;
|
||||
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error AudioStreamOGGVorbis::_load_stream() {
|
||||
Error AudioStreamPlaybackOGGVorbis::_load_stream() {
|
||||
|
||||
ERR_FAIL_COND_V(!stream_valid,ERR_UNCONFIGURED);
|
||||
|
||||
_clear_stream();
|
||||
if (file=="")
|
||||
|
@ -270,52 +310,31 @@ Error AudioStreamOGGVorbis::_load_stream() {
|
|||
|
||||
Error err;
|
||||
f=FileAccess::open(file,FileAccess::READ,&err);
|
||||
|
||||
|
||||
if (err) {
|
||||
ERR_FAIL_COND_V( err, err );
|
||||
}
|
||||
|
||||
int errv = ov_open_callbacks(f,&vf,NULL,0,_ov_callbacks);
|
||||
|
||||
|
||||
const vorbis_info *vinfo=ov_info(&vf,-1);
|
||||
stream_channels=vinfo->channels;
|
||||
stream_srate=vinfo->rate;
|
||||
Error serr = _setup(stream_channels,stream_srate);
|
||||
|
||||
if (serr) {
|
||||
_close_file();
|
||||
ERR_FAIL_V( ERR_INVALID_DATA );
|
||||
}
|
||||
|
||||
switch(errv) {
|
||||
|
||||
case OV_EREAD: { // - A read from media returned an error.
|
||||
_close_file();
|
||||
memdelete(f); f=NULL;
|
||||
ERR_FAIL_V( ERR_FILE_CANT_READ );
|
||||
} break;
|
||||
case OV_EVERSION: // - Vorbis version mismatch.
|
||||
case OV_ENOTVORBIS: { // - Bitstream is not Vorbis data.
|
||||
_close_file();
|
||||
memdelete(f); f=NULL;
|
||||
ERR_FAIL_V( ERR_FILE_UNRECOGNIZED );
|
||||
} break;
|
||||
case OV_EBADHEADER: { // - Invalid Vorbis bitstream header.
|
||||
_close_file();
|
||||
memdelete(f); f=NULL;
|
||||
ERR_FAIL_V( ERR_FILE_CORRUPT );
|
||||
} break;
|
||||
case OV_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption.
|
||||
|
||||
_close_file();
|
||||
memdelete(f); f=NULL;
|
||||
ERR_FAIL_V( ERR_BUG );
|
||||
} break;
|
||||
}
|
||||
|
||||
|
||||
ogg_int64_t len = ov_time_total(&vf,-1);
|
||||
|
||||
length=len/1000.0;
|
||||
|
||||
repeats=0;
|
||||
stream_loaded=true;
|
||||
|
||||
|
@ -324,16 +343,16 @@ Error AudioStreamOGGVorbis::_load_stream() {
|
|||
}
|
||||
|
||||
|
||||
float AudioStreamOGGVorbis::get_length() const {
|
||||
float AudioStreamPlaybackOGGVorbis::get_length() const {
|
||||
|
||||
if (!stream_loaded) {
|
||||
if (const_cast<AudioStreamOGGVorbis*>(this)->_load_stream()!=OK)
|
||||
if (const_cast<AudioStreamPlaybackOGGVorbis*>(this)->_load_stream()!=OK)
|
||||
return 0;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
void AudioStreamOGGVorbis::_clear_stream() {
|
||||
void AudioStreamPlaybackOGGVorbis::_clear_stream() {
|
||||
|
||||
if (!stream_loaded)
|
||||
return;
|
||||
|
@ -346,18 +365,18 @@ void AudioStreamOGGVorbis::_clear_stream() {
|
|||
playing=false;
|
||||
}
|
||||
|
||||
void AudioStreamOGGVorbis::set_paused(bool p_paused) {
|
||||
void AudioStreamPlaybackOGGVorbis::set_paused(bool p_paused) {
|
||||
|
||||
paused=p_paused;
|
||||
}
|
||||
|
||||
bool AudioStreamOGGVorbis::is_paused(bool p_paused) const {
|
||||
bool AudioStreamPlaybackOGGVorbis::is_paused(bool p_paused) const {
|
||||
|
||||
return paused;
|
||||
}
|
||||
|
||||
|
||||
AudioStreamOGGVorbis::AudioStreamOGGVorbis() {
|
||||
AudioStreamPlaybackOGGVorbis::AudioStreamPlaybackOGGVorbis() {
|
||||
|
||||
loops=false;
|
||||
playing=false;
|
||||
|
@ -367,17 +386,18 @@ AudioStreamOGGVorbis::AudioStreamOGGVorbis() {
|
|||
_ov_callbacks.tell_func=_ov_tell_func;
|
||||
f = NULL;
|
||||
stream_loaded=false;
|
||||
stream_valid=false;
|
||||
repeats=0;
|
||||
setting_up=false;
|
||||
paused=true;
|
||||
stream_channels=0;
|
||||
stream_srate=0;
|
||||
current_section=0;
|
||||
length=0;
|
||||
loop_restart_time=0;
|
||||
}
|
||||
|
||||
|
||||
AudioStreamOGGVorbis::~AudioStreamOGGVorbis() {
|
||||
AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
|
||||
|
||||
_clear_stream();
|
||||
|
||||
|
|
|
@ -29,17 +29,16 @@
|
|||
#ifndef AUDIO_STREAM_OGG_VORBIS_H
|
||||
#define AUDIO_STREAM_OGG_VORBIS_H
|
||||
|
||||
#include "scene/resources/audio_stream_resampled.h"
|
||||
#include "scene/resources/audio_stream.h"
|
||||
#include "vorbis/vorbisfile.h"
|
||||
#include "os/file_access.h"
|
||||
#include "io/resource_loader.h"
|
||||
#include "os/thread_safe.h"
|
||||
|
||||
class AudioStreamOGGVorbis : public AudioStreamResampled {
|
||||
|
||||
OBJ_TYPE(AudioStreamOGGVorbis,AudioStreamResampled);
|
||||
_THREAD_SAFE_CLASS_
|
||||
class AudioStreamPlaybackOGGVorbis : public AudioStreamPlayback {
|
||||
|
||||
OBJ_TYPE(AudioStreamPlaybackOGGVorbis,AudioStreamPlayback);
|
||||
|
||||
enum {
|
||||
MIN_MIX=1024
|
||||
|
@ -54,9 +53,6 @@ class AudioStreamOGGVorbis : public AudioStreamResampled {
|
|||
static int _ov_close_func(void *_f);
|
||||
static long _ov_tell_func(void *_f);
|
||||
|
||||
|
||||
virtual bool _can_mix() const;
|
||||
|
||||
String file;
|
||||
int64_t frames_mixed;
|
||||
|
||||
|
@ -67,7 +63,7 @@ class AudioStreamOGGVorbis : public AudioStreamResampled {
|
|||
int stream_srate;
|
||||
int current_section;
|
||||
|
||||
volatile bool setting_up;
|
||||
|
||||
bool paused;
|
||||
bool loops;
|
||||
int repeats;
|
||||
|
@ -76,17 +72,21 @@ class AudioStreamOGGVorbis : public AudioStreamResampled {
|
|||
void _clear_stream();
|
||||
void _close_file();
|
||||
|
||||
bool stream_valid;
|
||||
float loop_restart_time;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
|
||||
void set_file(const String& p_file);
|
||||
Error set_file(const String& p_file);
|
||||
|
||||
|
||||
virtual void play();
|
||||
virtual void play(float p_from=0);
|
||||
virtual void stop();
|
||||
virtual bool is_playing() const;
|
||||
|
||||
virtual void set_loop_restart_time(float p_time) { loop_restart_time=0; }
|
||||
|
||||
virtual void set_paused(bool p_paused);
|
||||
virtual bool is_paused(bool p_paused) const;
|
||||
|
||||
|
@ -102,11 +102,32 @@ public:
|
|||
virtual float get_pos() const;
|
||||
virtual void seek_pos(float p_time);
|
||||
|
||||
virtual UpdateMode get_update_mode() const;
|
||||
virtual void update();
|
||||
virtual int get_channels() const { return stream_channels; }
|
||||
virtual int get_mix_rate() const { return stream_srate; }
|
||||
|
||||
virtual int get_minimum_buffer_size() const { return 0; }
|
||||
virtual int mix(int16_t* p_bufer,int p_frames);
|
||||
|
||||
AudioStreamPlaybackOGGVorbis();
|
||||
~AudioStreamPlaybackOGGVorbis();
|
||||
};
|
||||
|
||||
|
||||
class AudioStreamOGGVorbis : public AudioStream {
|
||||
|
||||
OBJ_TYPE(AudioStreamOGGVorbis,AudioStream);
|
||||
|
||||
String file;
|
||||
public:
|
||||
|
||||
Ref<AudioStreamPlayback> instance_playback() {
|
||||
Ref<AudioStreamPlaybackOGGVorbis> pb = memnew( AudioStreamPlaybackOGGVorbis );
|
||||
pb->set_file(file);
|
||||
return pb;
|
||||
}
|
||||
|
||||
void set_file(const String& p_file) { file=p_file; }
|
||||
|
||||
AudioStreamOGGVorbis();
|
||||
~AudioStreamOGGVorbis();
|
||||
};
|
||||
|
||||
class ResourceFormatLoaderAudioStreamOGGVorbis : public ResourceFormatLoader {
|
||||
|
|
|
@ -30,21 +30,79 @@
|
|||
|
||||
|
||||
|
||||
int SpatialStreamPlayer::InternalStream::get_channel_count() const {
|
||||
|
||||
return player->sp_get_channel_count();
|
||||
}
|
||||
void SpatialStreamPlayer::InternalStream::set_mix_rate(int p_rate){
|
||||
|
||||
return player->sp_set_mix_rate(p_rate);
|
||||
}
|
||||
bool SpatialStreamPlayer::InternalStream::mix(int32_t *p_buffer,int p_frames){
|
||||
|
||||
return player->sp_mix(p_buffer,p_frames);
|
||||
}
|
||||
void SpatialStreamPlayer::InternalStream::update(){
|
||||
|
||||
player->sp_update();
|
||||
}
|
||||
|
||||
|
||||
int SpatialStreamPlayer::sp_get_channel_count() const {
|
||||
|
||||
return playback->get_channels();
|
||||
}
|
||||
|
||||
void SpatialStreamPlayer::sp_set_mix_rate(int p_rate){
|
||||
|
||||
server_mix_rate=p_rate;
|
||||
}
|
||||
|
||||
bool SpatialStreamPlayer::sp_mix(int32_t *p_buffer,int p_frames) {
|
||||
|
||||
if (resampler.is_ready()) {
|
||||
return resampler.mix(p_buffer,p_frames);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SpatialStreamPlayer::sp_update() {
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
if (!paused && resampler.is_ready() && playback.is_valid()) {
|
||||
|
||||
if (!playback->is_playing()) {
|
||||
//stream depleted data, but there's still audio in the ringbuffer
|
||||
//check that all this audio has been flushed before stopping the stream
|
||||
int to_mix = resampler.get_total() - resampler.get_todo();
|
||||
if (to_mix==0) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int todo =resampler.get_todo();
|
||||
int wrote = playback->mix(resampler.get_write_buffer(),todo);
|
||||
resampler.write(wrote);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SpatialStreamPlayer::_notification(int p_what) {
|
||||
|
||||
switch(p_what) {
|
||||
|
||||
case NOTIFICATION_ENTER_WORLD: {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
|
||||
// set_idle_process(false); //don't annoy
|
||||
//set_idle_process(false); //don't annoy
|
||||
if (stream.is_valid() && autoplay && !get_tree()->is_editor_hint())
|
||||
play();
|
||||
} break;
|
||||
case NOTIFICATION_PROCESS: {
|
||||
|
||||
// if (!stream.is_null())
|
||||
// stream->update();
|
||||
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_WORLD: {
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
|
||||
stop(); //wathever it may be doing, stop
|
||||
} break;
|
||||
|
@ -58,12 +116,20 @@ void SpatialStreamPlayer::set_stream(const Ref<AudioStream> &p_stream) {
|
|||
stop();
|
||||
|
||||
stream=p_stream;
|
||||
|
||||
if (!stream.is_null()) {
|
||||
|
||||
stream->set_loop(loops);
|
||||
playback=stream->instance_playback();
|
||||
playback->set_loop(loops);
|
||||
playback->set_loop_restart_time(loop_point);
|
||||
AudioServer::get_singleton()->lock();
|
||||
resampler.setup(playback->get_channels(),playback->get_mix_rate(),server_mix_rate,buffering_ms,playback->get_minimum_buffer_size());
|
||||
AudioServer::get_singleton()->unlock();
|
||||
} else {
|
||||
AudioServer::get_singleton()->lock();
|
||||
resampler.clear();
|
||||
playback.unref();
|
||||
AudioServer::get_singleton()->unlock();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Ref<AudioStream> SpatialStreamPlayer::get_stream() const {
|
||||
|
@ -72,18 +138,25 @@ Ref<AudioStream> SpatialStreamPlayer::get_stream() const {
|
|||
}
|
||||
|
||||
|
||||
void SpatialStreamPlayer::play() {
|
||||
void SpatialStreamPlayer::play(float p_from_offset) {
|
||||
|
||||
if (!is_inside_tree())
|
||||
ERR_FAIL_COND(!is_inside_tree());
|
||||
if (playback.is_null())
|
||||
return;
|
||||
if (stream.is_null())
|
||||
return;
|
||||
if (stream->is_playing())
|
||||
if (playback->is_playing())
|
||||
stop();
|
||||
stream->play();
|
||||
SpatialSoundServer::get_singleton()->source_set_audio_stream(get_source_rid(),stream->get_audio_stream());
|
||||
//if (stream->get_update_mode()!=AudioStream::UPDATE_NONE)
|
||||
// set_idle_process(true);
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
playback->play(p_from_offset);
|
||||
//feed the ringbuffer as long as no update callback is going on
|
||||
sp_update();
|
||||
|
||||
SpatialSoundServer::get_singleton()->source_set_audio_stream(get_source_rid(),&internal_stream);
|
||||
|
||||
// AudioServer::get_singleton()->stream_set_active(stream_rid,true);
|
||||
// AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume);
|
||||
// if (stream->get_update_mode()!=AudioStream::UPDATE_NONE)
|
||||
// set_idle_process(true);
|
||||
|
||||
}
|
||||
|
||||
|
@ -91,28 +164,30 @@ void SpatialStreamPlayer::stop() {
|
|||
|
||||
if (!is_inside_tree())
|
||||
return;
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return;
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
//AudioServer::get_singleton()->stream_set_active(stream_rid,false);
|
||||
SpatialSoundServer::get_singleton()->source_set_audio_stream(get_source_rid(),NULL);
|
||||
stream->stop();
|
||||
playback->stop();
|
||||
//set_idle_process(false);
|
||||
}
|
||||
|
||||
bool SpatialStreamPlayer::is_playing() const {
|
||||
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return false;
|
||||
|
||||
return stream->is_playing();
|
||||
return playback->is_playing();
|
||||
}
|
||||
|
||||
void SpatialStreamPlayer::set_loop(bool p_enable) {
|
||||
|
||||
loops=p_enable;
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return;
|
||||
stream->set_loop(loops);
|
||||
playback->set_loop(loops);
|
||||
|
||||
}
|
||||
bool SpatialStreamPlayer::has_loop() const {
|
||||
|
@ -120,6 +195,46 @@ bool SpatialStreamPlayer::has_loop() const {
|
|||
return loops;
|
||||
}
|
||||
|
||||
void SpatialStreamPlayer::set_volume(float p_vol) {
|
||||
|
||||
volume=p_vol;
|
||||
if (stream_rid.is_valid())
|
||||
AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume);
|
||||
}
|
||||
|
||||
float SpatialStreamPlayer::get_volume() const {
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
void SpatialStreamPlayer::set_loop_restart_time(float p_secs) {
|
||||
|
||||
loop_point=p_secs;
|
||||
if (playback.is_valid())
|
||||
playback->set_loop_restart_time(p_secs);
|
||||
}
|
||||
|
||||
float SpatialStreamPlayer::get_loop_restart_time() const {
|
||||
|
||||
return loop_point;
|
||||
}
|
||||
|
||||
|
||||
void SpatialStreamPlayer::set_volume_db(float p_db) {
|
||||
|
||||
if (p_db<-79)
|
||||
set_volume(0);
|
||||
else
|
||||
set_volume(Math::db2linear(p_db));
|
||||
}
|
||||
|
||||
float SpatialStreamPlayer::get_volume_db() const {
|
||||
|
||||
if (volume==0)
|
||||
return -80;
|
||||
else
|
||||
return Math::linear2db(volume);
|
||||
}
|
||||
|
||||
|
||||
String SpatialStreamPlayer::get_stream_name() const {
|
||||
|
@ -132,27 +247,85 @@ String SpatialStreamPlayer::get_stream_name() const {
|
|||
|
||||
int SpatialStreamPlayer::get_loop_count() const {
|
||||
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return 0;
|
||||
return stream->get_loop_count();
|
||||
return playback->get_loop_count();
|
||||
|
||||
}
|
||||
|
||||
float SpatialStreamPlayer::get_pos() const {
|
||||
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return 0;
|
||||
return stream->get_pos();
|
||||
return playback->get_pos();
|
||||
|
||||
}
|
||||
|
||||
float SpatialStreamPlayer::get_length() const {
|
||||
|
||||
if (playback.is_null())
|
||||
return 0;
|
||||
return playback->get_length();
|
||||
}
|
||||
void SpatialStreamPlayer::seek_pos(float p_time) {
|
||||
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return;
|
||||
return stream->seek_pos(p_time);
|
||||
return playback->seek_pos(p_time);
|
||||
|
||||
}
|
||||
|
||||
void SpatialStreamPlayer::set_autoplay(bool p_enable) {
|
||||
|
||||
autoplay=p_enable;
|
||||
}
|
||||
|
||||
bool SpatialStreamPlayer::has_autoplay() const {
|
||||
|
||||
return autoplay;
|
||||
}
|
||||
|
||||
void SpatialStreamPlayer::set_paused(bool p_paused) {
|
||||
|
||||
paused=p_paused;
|
||||
//if (stream.is_valid())
|
||||
// stream->set_paused(p_paused);
|
||||
}
|
||||
|
||||
bool SpatialStreamPlayer::is_paused() const {
|
||||
|
||||
return paused;
|
||||
}
|
||||
|
||||
void SpatialStreamPlayer::_set_play(bool p_play) {
|
||||
|
||||
_play=p_play;
|
||||
if (is_inside_tree()) {
|
||||
if(_play)
|
||||
play();
|
||||
else
|
||||
stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool SpatialStreamPlayer::_get_play() const{
|
||||
|
||||
return _play;
|
||||
}
|
||||
|
||||
void SpatialStreamPlayer::set_buffering_msec(int p_msec) {
|
||||
|
||||
buffering_ms=p_msec;
|
||||
}
|
||||
|
||||
int SpatialStreamPlayer::get_buffering_msec() const{
|
||||
|
||||
return buffering_ms;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SpatialStreamPlayer::_bind_methods() {
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("set_stream","stream:Stream"),&SpatialStreamPlayer::set_stream);
|
||||
|
@ -163,28 +336,67 @@ void SpatialStreamPlayer::_bind_methods() {
|
|||
|
||||
ObjectTypeDB::bind_method(_MD("is_playing"),&SpatialStreamPlayer::is_playing);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("set_paused","paused"),&SpatialStreamPlayer::set_paused);
|
||||
ObjectTypeDB::bind_method(_MD("is_paused"),&SpatialStreamPlayer::is_paused);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("set_loop","enabled"),&SpatialStreamPlayer::set_loop);
|
||||
ObjectTypeDB::bind_method(_MD("has_loop"),&SpatialStreamPlayer::has_loop);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("set_volume","volume"),&SpatialStreamPlayer::set_volume);
|
||||
ObjectTypeDB::bind_method(_MD("get_volume"),&SpatialStreamPlayer::get_volume);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("set_volume_db","db"),&SpatialStreamPlayer::set_volume_db);
|
||||
ObjectTypeDB::bind_method(_MD("get_volume_db"),&SpatialStreamPlayer::get_volume_db);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("set_buffering_msec","msec"),&SpatialStreamPlayer::set_buffering_msec);
|
||||
ObjectTypeDB::bind_method(_MD("get_buffering_msec"),&SpatialStreamPlayer::get_buffering_msec);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("set_loop_restart_time","secs"),&SpatialStreamPlayer::set_loop_restart_time);
|
||||
ObjectTypeDB::bind_method(_MD("get_loop_restart_time"),&SpatialStreamPlayer::get_loop_restart_time);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("get_stream_name"),&SpatialStreamPlayer::get_stream_name);
|
||||
ObjectTypeDB::bind_method(_MD("get_loop_count"),&SpatialStreamPlayer::get_loop_count);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("get_pos"),&SpatialStreamPlayer::get_pos);
|
||||
ObjectTypeDB::bind_method(_MD("seek_pos","time"),&SpatialStreamPlayer::seek_pos);
|
||||
|
||||
ADD_PROPERTY( PropertyInfo(Variant::OBJECT, "stream/stream", PROPERTY_HINT_RESOURCE_TYPE,"AudioStream"), _SCS("set_stream"),_SCS("get_stream") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), _SCS("set_loop"),_SCS("has_loop") );
|
||||
ObjectTypeDB::bind_method(_MD("set_autoplay","enabled"),&SpatialStreamPlayer::set_autoplay);
|
||||
ObjectTypeDB::bind_method(_MD("has_autoplay"),&SpatialStreamPlayer::has_autoplay);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("get_length"),&SpatialStreamPlayer::get_length);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("_set_play","play"),&SpatialStreamPlayer::_set_play);
|
||||
ObjectTypeDB::bind_method(_MD("_get_play"),&SpatialStreamPlayer::_get_play);
|
||||
|
||||
ADD_PROPERTY( PropertyInfo(Variant::OBJECT, "stream/stream", PROPERTY_HINT_RESOURCE_TYPE,"AudioStream"), _SCS("set_stream"), _SCS("get_stream") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/play"), _SCS("_set_play"), _SCS("_get_play") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), _SCS("set_loop"), _SCS("has_loop") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::REAL, "stream/volume_db", PROPERTY_HINT_RANGE,"-80,24,0.01"), _SCS("set_volume_db"), _SCS("get_volume_db") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/autoplay"), _SCS("set_autoplay"), _SCS("has_autoplay") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/paused"), _SCS("set_paused"), _SCS("is_paused") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::INT, "stream/loop_restart_time"), _SCS("set_loop_restart_time"), _SCS("get_loop_restart_time") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::INT, "stream/buffering_ms"), _SCS("set_buffering_msec"), _SCS("get_buffering_msec") );
|
||||
}
|
||||
|
||||
|
||||
SpatialStreamPlayer::SpatialStreamPlayer() {
|
||||
|
||||
volume=1;
|
||||
loops=false;
|
||||
paused=false;
|
||||
autoplay=false;
|
||||
_play=false;
|
||||
server_mix_rate=1;
|
||||
internal_stream.player=this;
|
||||
stream_rid=AudioServer::get_singleton()->audio_stream_create(&internal_stream);
|
||||
buffering_ms=500;
|
||||
loop_point=0;
|
||||
|
||||
}
|
||||
|
||||
SpatialStreamPlayer::~SpatialStreamPlayer() {
|
||||
AudioServer::get_singleton()->free(stream_rid);
|
||||
resampler.clear();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -31,16 +31,48 @@
|
|||
|
||||
#include "scene/resources/audio_stream.h"
|
||||
#include "scene/3d/spatial_player.h"
|
||||
|
||||
#include "servers/audio/audio_rb_resampler.h"
|
||||
|
||||
class SpatialStreamPlayer : public SpatialPlayer {
|
||||
|
||||
OBJ_TYPE(SpatialStreamPlayer,SpatialPlayer);
|
||||
|
||||
Ref<AudioStream> stream;
|
||||
bool loops;
|
||||
protected:
|
||||
_THREAD_SAFE_CLASS_
|
||||
|
||||
struct InternalStream : public AudioServer::AudioStream {
|
||||
SpatialStreamPlayer *player;
|
||||
virtual int get_channel_count() const;
|
||||
virtual void set_mix_rate(int p_rate); //notify the stream of the mix rate
|
||||
virtual bool mix(int32_t *p_buffer,int p_frames);
|
||||
virtual void update();
|
||||
};
|
||||
|
||||
|
||||
InternalStream internal_stream;
|
||||
Ref<AudioStreamPlayback> playback;
|
||||
Ref<AudioStream> stream;
|
||||
|
||||
int sp_get_channel_count() const;
|
||||
void sp_set_mix_rate(int p_rate); //notify the stream of the mix rate
|
||||
bool sp_mix(int32_t *p_buffer,int p_frames);
|
||||
void sp_update();
|
||||
|
||||
int server_mix_rate;
|
||||
|
||||
RID stream_rid;
|
||||
bool paused;
|
||||
bool autoplay;
|
||||
bool loops;
|
||||
float volume;
|
||||
float loop_point;
|
||||
int buffering_ms;
|
||||
|
||||
AudioRBResampler resampler;
|
||||
|
||||
bool _play;
|
||||
void _set_play(bool p_play);
|
||||
bool _get_play() const;
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
static void _bind_methods();
|
||||
|
@ -49,21 +81,37 @@ public:
|
|||
void set_stream(const Ref<AudioStream> &p_stream);
|
||||
Ref<AudioStream> get_stream() const;
|
||||
|
||||
void play();
|
||||
void play(float p_from_offset=0);
|
||||
void stop();
|
||||
bool is_playing() const;
|
||||
|
||||
void set_paused(bool p_paused);
|
||||
bool is_paused() const;
|
||||
|
||||
void set_loop(bool p_enable);
|
||||
bool has_loop() const;
|
||||
|
||||
void set_volume(float p_vol);
|
||||
float get_volume() const;
|
||||
|
||||
void set_loop_restart_time(float p_secs);
|
||||
float get_loop_restart_time() const;
|
||||
|
||||
void set_volume_db(float p_db);
|
||||
float get_volume_db() const;
|
||||
|
||||
String get_stream_name() const;
|
||||
int get_loop_count() const;
|
||||
|
||||
int get_loop_count() const;
|
||||
|
||||
float get_pos() const;
|
||||
void seek_pos(float p_time);
|
||||
float get_length() const;
|
||||
void set_autoplay(bool p_vol);
|
||||
bool has_autoplay() const;
|
||||
|
||||
void set_buffering_msec(int p_msec);
|
||||
int get_buffering_msec() const;
|
||||
|
||||
SpatialStreamPlayer();
|
||||
~SpatialStreamPlayer();
|
||||
|
|
|
@ -28,6 +28,67 @@
|
|||
/*************************************************************************/
|
||||
#include "stream_player.h"
|
||||
|
||||
int StreamPlayer::InternalStream::get_channel_count() const {
|
||||
|
||||
return player->sp_get_channel_count();
|
||||
}
|
||||
void StreamPlayer::InternalStream::set_mix_rate(int p_rate){
|
||||
|
||||
return player->sp_set_mix_rate(p_rate);
|
||||
}
|
||||
bool StreamPlayer::InternalStream::mix(int32_t *p_buffer,int p_frames){
|
||||
|
||||
return player->sp_mix(p_buffer,p_frames);
|
||||
}
|
||||
void StreamPlayer::InternalStream::update(){
|
||||
|
||||
player->sp_update();
|
||||
}
|
||||
|
||||
|
||||
int StreamPlayer::sp_get_channel_count() const {
|
||||
|
||||
return playback->get_channels();
|
||||
}
|
||||
|
||||
void StreamPlayer::sp_set_mix_rate(int p_rate){
|
||||
|
||||
server_mix_rate=p_rate;
|
||||
}
|
||||
|
||||
bool StreamPlayer::sp_mix(int32_t *p_buffer,int p_frames) {
|
||||
|
||||
if (resampler.is_ready()) {
|
||||
return resampler.mix(p_buffer,p_frames);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void StreamPlayer::sp_update() {
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
if (!paused && resampler.is_ready() && playback.is_valid()) {
|
||||
|
||||
if (!playback->is_playing()) {
|
||||
//stream depleted data, but there's still audio in the ringbuffer
|
||||
//check that all this audio has been flushed before stopping the stream
|
||||
int to_mix = resampler.get_total() - resampler.get_todo();
|
||||
if (to_mix==0) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int todo =resampler.get_todo();
|
||||
int wrote = playback->mix(resampler.get_write_buffer(),todo);
|
||||
resampler.write(wrote);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void StreamPlayer::_notification(int p_what) {
|
||||
|
||||
|
@ -52,19 +113,21 @@ void StreamPlayer::set_stream(const Ref<AudioStream> &p_stream) {
|
|||
|
||||
stop();
|
||||
|
||||
if (stream_rid.is_valid())
|
||||
AudioServer::get_singleton()->free(stream_rid);
|
||||
stream_rid=RID();
|
||||
|
||||
stream=p_stream;
|
||||
|
||||
if (!stream.is_null()) {
|
||||
|
||||
stream->set_loop(loops);
|
||||
stream->set_paused(paused);
|
||||
stream_rid=AudioServer::get_singleton()->audio_stream_create(stream->get_audio_stream());
|
||||
playback=stream->instance_playback();
|
||||
playback->set_loop(loops);
|
||||
playback->set_loop_restart_time(loop_point);
|
||||
AudioServer::get_singleton()->lock();
|
||||
resampler.setup(playback->get_channels(),playback->get_mix_rate(),server_mix_rate,buffering_ms,playback->get_minimum_buffer_size());
|
||||
AudioServer::get_singleton()->unlock();
|
||||
} else {
|
||||
AudioServer::get_singleton()->lock();
|
||||
resampler.clear();
|
||||
playback.unref();
|
||||
AudioServer::get_singleton()->unlock();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Ref<AudioStream> StreamPlayer::get_stream() const {
|
||||
|
@ -73,15 +136,18 @@ Ref<AudioStream> StreamPlayer::get_stream() const {
|
|||
}
|
||||
|
||||
|
||||
void StreamPlayer::play() {
|
||||
void StreamPlayer::play(float p_from_offset) {
|
||||
|
||||
ERR_FAIL_COND(!is_inside_tree());
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return;
|
||||
if (stream->is_playing())
|
||||
if (playback->is_playing())
|
||||
stop();
|
||||
|
||||
stream->play();
|
||||
_THREAD_SAFE_METHOD_
|
||||
playback->play(p_from_offset);
|
||||
//feed the ringbuffer as long as no update callback is going on
|
||||
sp_update();
|
||||
AudioServer::get_singleton()->stream_set_active(stream_rid,true);
|
||||
AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume);
|
||||
// if (stream->get_update_mode()!=AudioStream::UPDATE_NONE)
|
||||
|
@ -93,28 +159,29 @@ void StreamPlayer::stop() {
|
|||
|
||||
if (!is_inside_tree())
|
||||
return;
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return;
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
AudioServer::get_singleton()->stream_set_active(stream_rid,false);
|
||||
stream->stop();
|
||||
playback->stop();
|
||||
//set_idle_process(false);
|
||||
}
|
||||
|
||||
bool StreamPlayer::is_playing() const {
|
||||
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return false;
|
||||
|
||||
return stream->is_playing();
|
||||
return playback->is_playing();
|
||||
}
|
||||
|
||||
void StreamPlayer::set_loop(bool p_enable) {
|
||||
|
||||
loops=p_enable;
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return;
|
||||
stream->set_loop(loops);
|
||||
playback->set_loop(loops);
|
||||
|
||||
}
|
||||
bool StreamPlayer::has_loop() const {
|
||||
|
@ -134,6 +201,19 @@ float StreamPlayer::get_volume() const {
|
|||
return volume;
|
||||
}
|
||||
|
||||
void StreamPlayer::set_loop_restart_time(float p_secs) {
|
||||
|
||||
loop_point=p_secs;
|
||||
if (playback.is_valid())
|
||||
playback->set_loop_restart_time(p_secs);
|
||||
}
|
||||
|
||||
float StreamPlayer::get_loop_restart_time() const {
|
||||
|
||||
return loop_point;
|
||||
}
|
||||
|
||||
|
||||
void StreamPlayer::set_volume_db(float p_db) {
|
||||
|
||||
if (p_db<-79)
|
||||
|
@ -161,31 +241,31 @@ String StreamPlayer::get_stream_name() const {
|
|||
|
||||
int StreamPlayer::get_loop_count() const {
|
||||
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return 0;
|
||||
return stream->get_loop_count();
|
||||
return playback->get_loop_count();
|
||||
|
||||
}
|
||||
|
||||
float StreamPlayer::get_pos() const {
|
||||
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return 0;
|
||||
return stream->get_pos();
|
||||
return playback->get_pos();
|
||||
|
||||
}
|
||||
|
||||
float StreamPlayer::get_length() const {
|
||||
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return 0;
|
||||
return stream->get_length();
|
||||
return playback->get_length();
|
||||
}
|
||||
void StreamPlayer::seek_pos(float p_time) {
|
||||
|
||||
if (stream.is_null())
|
||||
if (playback.is_null())
|
||||
return;
|
||||
return stream->seek_pos(p_time);
|
||||
return playback->seek_pos(p_time);
|
||||
|
||||
}
|
||||
|
||||
|
@ -202,8 +282,8 @@ bool StreamPlayer::has_autoplay() const {
|
|||
void StreamPlayer::set_paused(bool p_paused) {
|
||||
|
||||
paused=p_paused;
|
||||
if (stream.is_valid())
|
||||
stream->set_paused(p_paused);
|
||||
//if (stream.is_valid())
|
||||
// stream->set_paused(p_paused);
|
||||
}
|
||||
|
||||
bool StreamPlayer::is_paused() const {
|
||||
|
@ -228,6 +308,17 @@ bool StreamPlayer::_get_play() const{
|
|||
return _play;
|
||||
}
|
||||
|
||||
void StreamPlayer::set_buffering_msec(int p_msec) {
|
||||
|
||||
buffering_ms=p_msec;
|
||||
}
|
||||
|
||||
int StreamPlayer::get_buffering_msec() const{
|
||||
|
||||
return buffering_ms;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void StreamPlayer::_bind_methods() {
|
||||
|
||||
|
@ -251,6 +342,12 @@ void StreamPlayer::_bind_methods() {
|
|||
ObjectTypeDB::bind_method(_MD("set_volume_db","db"),&StreamPlayer::set_volume_db);
|
||||
ObjectTypeDB::bind_method(_MD("get_volume_db"),&StreamPlayer::get_volume_db);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("set_buffering_msec","msec"),&StreamPlayer::set_buffering_msec);
|
||||
ObjectTypeDB::bind_method(_MD("get_buffering_msec"),&StreamPlayer::get_buffering_msec);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("set_loop_restart_time","secs"),&StreamPlayer::set_loop_restart_time);
|
||||
ObjectTypeDB::bind_method(_MD("get_loop_restart_time"),&StreamPlayer::get_loop_restart_time);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("get_stream_name"),&StreamPlayer::get_stream_name);
|
||||
ObjectTypeDB::bind_method(_MD("get_loop_count"),&StreamPlayer::get_loop_count);
|
||||
|
||||
|
@ -271,6 +368,8 @@ void StreamPlayer::_bind_methods() {
|
|||
ADD_PROPERTY( PropertyInfo(Variant::REAL, "stream/volume_db", PROPERTY_HINT_RANGE,"-80,24,0.01"), _SCS("set_volume_db"), _SCS("get_volume_db") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/autoplay"), _SCS("set_autoplay"), _SCS("has_autoplay") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/paused"), _SCS("set_paused"), _SCS("is_paused") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::INT, "stream/loop_restart_time"), _SCS("set_loop_restart_time"), _SCS("get_loop_restart_time") );
|
||||
ADD_PROPERTY( PropertyInfo(Variant::INT, "stream/buffering_ms"), _SCS("set_buffering_msec"), _SCS("get_buffering_msec") );
|
||||
}
|
||||
|
||||
|
||||
|
@ -281,10 +380,17 @@ StreamPlayer::StreamPlayer() {
|
|||
paused=false;
|
||||
autoplay=false;
|
||||
_play=false;
|
||||
server_mix_rate=1;
|
||||
internal_stream.player=this;
|
||||
stream_rid=AudioServer::get_singleton()->audio_stream_create(&internal_stream);
|
||||
buffering_ms=500;
|
||||
loop_point=0;
|
||||
|
||||
}
|
||||
|
||||
StreamPlayer::~StreamPlayer() {
|
||||
if (stream_rid.is_valid())
|
||||
AudioServer::get_singleton()->free(stream_rid);
|
||||
resampler.clear();
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -31,17 +31,43 @@
|
|||
|
||||
#include "scene/resources/audio_stream.h"
|
||||
#include "scene/main/node.h"
|
||||
#include "servers/audio/audio_rb_resampler.h"
|
||||
|
||||
class StreamPlayer : public Node {
|
||||
|
||||
OBJ_TYPE(StreamPlayer,Node);
|
||||
|
||||
_THREAD_SAFE_CLASS_
|
||||
|
||||
struct InternalStream : public AudioServer::AudioStream {
|
||||
StreamPlayer *player;
|
||||
virtual int get_channel_count() const;
|
||||
virtual void set_mix_rate(int p_rate); //notify the stream of the mix rate
|
||||
virtual bool mix(int32_t *p_buffer,int p_frames);
|
||||
virtual void update();
|
||||
};
|
||||
|
||||
|
||||
InternalStream internal_stream;
|
||||
Ref<AudioStreamPlayback> playback;
|
||||
Ref<AudioStream> stream;
|
||||
|
||||
int sp_get_channel_count() const;
|
||||
void sp_set_mix_rate(int p_rate); //notify the stream of the mix rate
|
||||
bool sp_mix(int32_t *p_buffer,int p_frames);
|
||||
void sp_update();
|
||||
|
||||
int server_mix_rate;
|
||||
|
||||
RID stream_rid;
|
||||
bool paused;
|
||||
bool autoplay;
|
||||
bool loops;
|
||||
float volume;
|
||||
float loop_point;
|
||||
int buffering_ms;
|
||||
|
||||
AudioRBResampler resampler;
|
||||
|
||||
bool _play;
|
||||
void _set_play(bool p_play);
|
||||
|
@ -55,7 +81,7 @@ public:
|
|||
void set_stream(const Ref<AudioStream> &p_stream);
|
||||
Ref<AudioStream> get_stream() const;
|
||||
|
||||
void play();
|
||||
void play(float p_from_offset=0);
|
||||
void stop();
|
||||
bool is_playing() const;
|
||||
|
||||
|
@ -68,6 +94,9 @@ public:
|
|||
void set_volume(float p_vol);
|
||||
float get_volume() const;
|
||||
|
||||
void set_loop_restart_time(float p_secs);
|
||||
float get_loop_restart_time() const;
|
||||
|
||||
void set_volume_db(float p_db);
|
||||
float get_volume_db() const;
|
||||
|
||||
|
@ -81,6 +110,8 @@ public:
|
|||
void set_autoplay(bool p_vol);
|
||||
bool has_autoplay() const;
|
||||
|
||||
void set_buffering_msec(int p_msec);
|
||||
int get_buffering_msec() const;
|
||||
|
||||
StreamPlayer();
|
||||
~StreamPlayer();
|
||||
|
|
|
@ -578,7 +578,8 @@ void register_scene_types() {
|
|||
ObjectTypeDB::register_type<Sample>();
|
||||
ObjectTypeDB::register_type<SampleLibrary>();
|
||||
ObjectTypeDB::register_virtual_type<AudioStream>();
|
||||
ObjectTypeDB::register_type<AudioStreamGibberish>();
|
||||
ObjectTypeDB::register_virtual_type<AudioStreamPlayback>();
|
||||
// ObjectTypeDB::register_type<AudioStreamGibberish>();
|
||||
ObjectTypeDB::register_virtual_type<VideoStream>();
|
||||
|
||||
OS::get_singleton()->yield(); //may take time to init
|
||||
|
|
|
@ -28,76 +28,34 @@
|
|||
/*************************************************************************/
|
||||
#include "audio_stream.h"
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
|
||||
int AudioStream::InternalAudioStream::get_channel_count() const {
|
||||
void AudioStreamPlayback::_bind_methods() {
|
||||
|
||||
return owner->get_channel_count();
|
||||
ObjectTypeDB::bind_method(_MD("play","from_pos_sec"),&AudioStreamPlayback::play,DEFVAL(0));
|
||||
ObjectTypeDB::bind_method(_MD("stop"),&AudioStreamPlayback::stop);
|
||||
ObjectTypeDB::bind_method(_MD("is_playing"),&AudioStreamPlayback::is_playing);
|
||||
|
||||
}
|
||||
ObjectTypeDB::bind_method(_MD("set_loop","enabled"),&AudioStreamPlayback::set_loop);
|
||||
ObjectTypeDB::bind_method(_MD("has_loop"),&AudioStreamPlayback::has_loop);
|
||||
|
||||
void AudioStream::InternalAudioStream::set_mix_rate(int p_rate) {
|
||||
ObjectTypeDB::bind_method(_MD("get_loop_count"),&AudioStreamPlayback::get_loop_count);
|
||||
|
||||
owner->_mix_rate=p_rate;
|
||||
}
|
||||
ObjectTypeDB::bind_method(_MD("seek_pos","pos"),&AudioStreamPlayback::seek_pos);
|
||||
ObjectTypeDB::bind_method(_MD("get_pos"),&AudioStreamPlayback::get_pos);
|
||||
|
||||
bool AudioStream::InternalAudioStream::mix(int32_t *p_buffer,int p_frames) {
|
||||
ObjectTypeDB::bind_method(_MD("get_length"),&AudioStreamPlayback::get_length);
|
||||
ObjectTypeDB::bind_method(_MD("get_channels"),&AudioStreamPlayback::get_channels);
|
||||
ObjectTypeDB::bind_method(_MD("get_mix_rate"),&AudioStreamPlayback::get_mix_rate);
|
||||
ObjectTypeDB::bind_method(_MD("get_minimum_buffer_size"),&AudioStreamPlayback::get_minimum_buffer_size);
|
||||
|
||||
return owner->mix(p_buffer,p_frames);
|
||||
}
|
||||
|
||||
bool AudioStream::InternalAudioStream::can_update_mt() const {
|
||||
|
||||
return owner->get_update_mode()==UPDATE_THREAD;
|
||||
}
|
||||
|
||||
void AudioStream::InternalAudioStream::update() {
|
||||
|
||||
owner->update();
|
||||
}
|
||||
|
||||
AudioServer::AudioStream *AudioStream::get_audio_stream() {
|
||||
|
||||
return internal_audio_stream;
|
||||
}
|
||||
|
||||
|
||||
void AudioStream::_bind_methods() {
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("play"),&AudioStream::play);
|
||||
ObjectTypeDB::bind_method(_MD("stop"),&AudioStream::stop);
|
||||
ObjectTypeDB::bind_method(_MD("is_playing"),&AudioStream::is_playing);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("set_loop","enabled"),&AudioStream::set_loop);
|
||||
ObjectTypeDB::bind_method(_MD("has_loop"),&AudioStream::has_loop);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("get_stream_name"),&AudioStream::get_stream_name);
|
||||
ObjectTypeDB::bind_method(_MD("get_loop_count"),&AudioStream::get_loop_count);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("seek_pos","pos"),&AudioStream::seek_pos);
|
||||
ObjectTypeDB::bind_method(_MD("get_pos"),&AudioStream::get_pos);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("get_length"),&AudioStream::get_length);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("get_update_mode"),&AudioStream::get_update_mode);
|
||||
|
||||
ObjectTypeDB::bind_method(_MD("update"),&AudioStream::update);
|
||||
|
||||
BIND_CONSTANT( UPDATE_NONE );
|
||||
BIND_CONSTANT( UPDATE_IDLE );
|
||||
BIND_CONSTANT( UPDATE_THREAD );
|
||||
|
||||
}
|
||||
|
||||
AudioStream::AudioStream() {
|
||||
|
||||
_mix_rate=44100;
|
||||
internal_audio_stream = memnew( InternalAudioStream );
|
||||
internal_audio_stream->owner=this;
|
||||
}
|
||||
|
||||
|
||||
AudioStream::~AudioStream() {
|
||||
|
||||
memdelete(internal_audio_stream);
|
||||
}
|
||||
|
|
|
@ -31,72 +31,53 @@
|
|||
|
||||
#include "resource.h"
|
||||
#include "servers/audio_server.h"
|
||||
#include "scene/resources/audio_stream.h"
|
||||
|
||||
class AudioStream : public Resource {
|
||||
class AudioStreamPlayback : public Reference {
|
||||
|
||||
OBJ_TYPE( AudioStream, Resource );
|
||||
OBJ_SAVE_TYPE( AudioStream ); //children are all saved as AudioStream, so they can be exchanged
|
||||
|
||||
friend class InternalAudioStream;
|
||||
|
||||
struct InternalAudioStream : public AudioServer::AudioStream {
|
||||
|
||||
::AudioStream *owner;
|
||||
virtual int get_channel_count() const;
|
||||
virtual void set_mix_rate(int p_rate); //notify the stream of the mix rate
|
||||
virtual bool mix(int32_t *p_buffer,int p_frames);
|
||||
virtual bool can_update_mt() const;
|
||||
virtual void update();
|
||||
};
|
||||
|
||||
|
||||
int _mix_rate;
|
||||
InternalAudioStream *internal_audio_stream;
|
||||
OBJ_TYPE( AudioStreamPlayback, Reference );
|
||||
protected:
|
||||
|
||||
_FORCE_INLINE_ int get_mix_rate() const { return _mix_rate; }
|
||||
virtual int get_channel_count() const=0;
|
||||
virtual bool mix(int32_t *p_buffer, int p_frames)=0;
|
||||
|
||||
static void _bind_methods();
|
||||
public:
|
||||
|
||||
enum UpdateMode {
|
||||
UPDATE_NONE,
|
||||
UPDATE_IDLE,
|
||||
UPDATE_THREAD
|
||||
};
|
||||
|
||||
AudioServer::AudioStream *get_audio_stream();
|
||||
|
||||
virtual void play()=0;
|
||||
virtual void play(float p_from_pos=0)=0;
|
||||
virtual void stop()=0;
|
||||
virtual bool is_playing() const=0;
|
||||
|
||||
virtual void set_paused(bool p_paused)=0;
|
||||
virtual bool is_paused(bool p_paused) const=0;
|
||||
|
||||
virtual void set_loop(bool p_enable)=0;
|
||||
virtual bool has_loop() const=0;
|
||||
|
||||
virtual float get_length() const=0;
|
||||
|
||||
virtual String get_stream_name() const=0;
|
||||
virtual void set_loop_restart_time(float p_time)=0;
|
||||
|
||||
virtual int get_loop_count() const=0;
|
||||
|
||||
virtual float get_pos() const=0;
|
||||
virtual void seek_pos(float p_time)=0;
|
||||
|
||||
virtual UpdateMode get_update_mode() const=0;
|
||||
virtual void update()=0;
|
||||
virtual int mix(int16_t* p_bufer,int p_frames)=0;
|
||||
|
||||
virtual float get_length() const=0;
|
||||
virtual String get_stream_name() const=0;
|
||||
|
||||
virtual int get_channels() const=0;
|
||||
virtual int get_mix_rate() const=0;
|
||||
virtual int get_minimum_buffer_size() const=0;
|
||||
|
||||
};
|
||||
|
||||
class AudioStream : public Resource {
|
||||
|
||||
OBJ_TYPE( AudioStream, Resource );
|
||||
OBJ_SAVE_TYPE( AudioStream ); //children are all saved as AudioStream, so they can be exchanged
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
public:
|
||||
|
||||
virtual Ref<AudioStreamPlayback> instance_playback()=0;
|
||||
|
||||
|
||||
AudioStream();
|
||||
~AudioStream();
|
||||
};
|
||||
|
||||
|
||||
VARIANT_ENUM_CAST( AudioStream::UpdateMode );
|
||||
|
||||
#endif // AUDIO_STREAM_H
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
/*************************************************************************/
|
||||
#include "audio_stream_resampled.h"
|
||||
#include "globals.h"
|
||||
|
||||
|
||||
#if 0
|
||||
int AudioStreamResampled::get_channel_count() const {
|
||||
|
||||
if (!rb)
|
||||
|
@ -382,3 +385,4 @@ AudioStreamResampled::~AudioStreamResampled() {
|
|||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include "scene/resources/audio_stream.h"
|
||||
|
||||
#if 0
|
||||
|
||||
class AudioStreamResampled : public AudioStream {
|
||||
OBJ_TYPE(AudioStreamResampled,AudioStream);
|
||||
|
@ -160,5 +161,5 @@ public:
|
|||
AudioStreamResampled();
|
||||
~AudioStreamResampled();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // AUDIO_STREAM_RESAMPLED_H
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "gibberish_stream.h"
|
||||
#include "servers/audio_server.h"
|
||||
|
||||
#if 0
|
||||
|
||||
int AudioStreamGibberish::get_channel_count() const {
|
||||
|
||||
return 1;
|
||||
|
@ -328,3 +330,4 @@ AudioStreamGibberish::AudioStreamGibberish() {
|
|||
paused=false;
|
||||
active_voices=0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#ifndef GIBBERISH_STREAM_H
|
||||
#define GIBBERISH_STREAM_H
|
||||
|
||||
|
||||
#if 0
|
||||
#include "scene/resources/audio_stream.h"
|
||||
#include "scene/resources/sample_library.h"
|
||||
class AudioStreamGibberish : public AudioStream {
|
||||
|
@ -109,4 +109,6 @@ public:
|
|||
AudioStreamGibberish();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // GIBBERISH_STREAM_H
|
||||
|
|
356
servers/audio/audio_rb_resampler.cpp
Normal file
356
servers/audio/audio_rb_resampler.cpp
Normal file
|
@ -0,0 +1,356 @@
|
|||
#include "audio_rb_resampler.h"
|
||||
|
||||
|
||||
int AudioRBResampler::get_channel_count() const {
|
||||
|
||||
if (!rb)
|
||||
return 0;
|
||||
|
||||
return channels;
|
||||
}
|
||||
|
||||
|
||||
template<int C>
|
||||
uint32_t AudioRBResampler::_resample(int32_t *p_dest,int p_todo,int32_t p_increment) {
|
||||
|
||||
uint32_t read=offset&MIX_FRAC_MASK;
|
||||
|
||||
for (int i=0;i<p_todo;i++) {
|
||||
|
||||
offset = (offset + p_increment)&(((1<<(rb_bits+MIX_FRAC_BITS))-1));
|
||||
read+=p_increment;
|
||||
uint32_t pos = offset >> MIX_FRAC_BITS;
|
||||
uint32_t frac = offset & MIX_FRAC_MASK;
|
||||
#ifndef FAST_AUDIO
|
||||
ERR_FAIL_COND_V(pos>=rb_len,0);
|
||||
#endif
|
||||
uint32_t pos_next = (pos+1)&rb_mask;
|
||||
//printf("rb pos %i\n",pos);
|
||||
|
||||
// since this is a template with a known compile time value (C), conditionals go away when compiling.
|
||||
if (C==1) {
|
||||
|
||||
int32_t v0 = rb[pos];
|
||||
int32_t v0n=rb[pos_next];
|
||||
#ifndef FAST_AUDIO
|
||||
v0+=(v0n-v0)*(int32_t)frac >> MIX_FRAC_BITS;
|
||||
#endif
|
||||
v0<<=16;
|
||||
p_dest[i]=v0;
|
||||
|
||||
}
|
||||
if (C==2) {
|
||||
|
||||
int32_t v0 = rb[(pos<<1)+0];
|
||||
int32_t v1 = rb[(pos<<1)+1];
|
||||
int32_t v0n=rb[(pos_next<<1)+0];
|
||||
int32_t v1n=rb[(pos_next<<1)+1];
|
||||
|
||||
#ifndef FAST_AUDIO
|
||||
v0+=(v0n-v0)*(int32_t)frac >> MIX_FRAC_BITS;
|
||||
v1+=(v1n-v1)*(int32_t)frac >> MIX_FRAC_BITS;
|
||||
#endif
|
||||
v0<<=16;
|
||||
v1<<=16;
|
||||
p_dest[(i<<1)+0]=v0;
|
||||
p_dest[(i<<1)+1]=v1;
|
||||
|
||||
}
|
||||
|
||||
if (C==4) {
|
||||
|
||||
int32_t v0 = rb[(pos<<2)+0];
|
||||
int32_t v1 = rb[(pos<<2)+1];
|
||||
int32_t v2 = rb[(pos<<2)+2];
|
||||
int32_t v3 = rb[(pos<<2)+3];
|
||||
int32_t v0n = rb[(pos_next<<2)+0];
|
||||
int32_t v1n=rb[(pos_next<<2)+1];
|
||||
int32_t v2n=rb[(pos_next<<2)+2];
|
||||
int32_t v3n=rb[(pos_next<<2)+3];
|
||||
|
||||
#ifndef FAST_AUDIO
|
||||
v0+=(v0n-v0)*(int32_t)frac >> MIX_FRAC_BITS;
|
||||
v1+=(v1n-v1)*(int32_t)frac >> MIX_FRAC_BITS;
|
||||
v2+=(v2n-v2)*(int32_t)frac >> MIX_FRAC_BITS;
|
||||
v3+=(v3n-v3)*(int32_t)frac >> MIX_FRAC_BITS;
|
||||
#endif
|
||||
v0<<=16;
|
||||
v1<<=16;
|
||||
v2<<=16;
|
||||
v3<<=16;
|
||||
p_dest[(i<<2)+0]=v0;
|
||||
p_dest[(i<<2)+1]=v1;
|
||||
p_dest[(i<<2)+2]=v2;
|
||||
p_dest[(i<<2)+3]=v3;
|
||||
|
||||
}
|
||||
|
||||
if (C==6) {
|
||||
|
||||
int32_t v0 = rb[(pos*6)+0];
|
||||
int32_t v1 = rb[(pos*6)+1];
|
||||
int32_t v2 = rb[(pos*6)+2];
|
||||
int32_t v3 = rb[(pos*6)+3];
|
||||
int32_t v4 = rb[(pos*6)+4];
|
||||
int32_t v5 = rb[(pos*6)+5];
|
||||
int32_t v0n = rb[(pos_next*6)+0];
|
||||
int32_t v1n=rb[(pos_next*6)+1];
|
||||
int32_t v2n=rb[(pos_next*6)+2];
|
||||
int32_t v3n=rb[(pos_next*6)+3];
|
||||
int32_t v4n=rb[(pos_next*6)+4];
|
||||
int32_t v5n=rb[(pos_next*6)+5];
|
||||
|
||||
#ifndef FAST_AUDIO
|
||||
v0+=(v0n-v0)*(int32_t)frac >> MIX_FRAC_BITS;
|
||||
v1+=(v1n-v1)*(int32_t)frac >> MIX_FRAC_BITS;
|
||||
v2+=(v2n-v2)*(int32_t)frac >> MIX_FRAC_BITS;
|
||||
v3+=(v3n-v3)*(int32_t)frac >> MIX_FRAC_BITS;
|
||||
v4+=(v4n-v4)*(int32_t)frac >> MIX_FRAC_BITS;
|
||||
v5+=(v5n-v5)*(int32_t)frac >> MIX_FRAC_BITS;
|
||||
#endif
|
||||
v0<<=16;
|
||||
v1<<=16;
|
||||
v2<<=16;
|
||||
v3<<=16;
|
||||
v4<<=16;
|
||||
v5<<=16;
|
||||
p_dest[(i*6)+0]=v0;
|
||||
p_dest[(i*6)+1]=v1;
|
||||
p_dest[(i*6)+2]=v2;
|
||||
p_dest[(i*6)+3]=v3;
|
||||
p_dest[(i*6)+4]=v4;
|
||||
p_dest[(i*6)+5]=v5;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
return read>>MIX_FRAC_BITS;//rb_read_pos=offset>>MIX_FRAC_BITS;
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool AudioRBResampler::mix(int32_t *p_dest, int p_frames) {
|
||||
|
||||
|
||||
if (!rb)
|
||||
return false;
|
||||
|
||||
int write_pos_cache=rb_write_pos;
|
||||
|
||||
int32_t increment=(src_mix_rate*MIX_FRAC_LEN)/target_mix_rate;
|
||||
|
||||
int rb_todo;
|
||||
|
||||
if (write_pos_cache==rb_read_pos) {
|
||||
return false; //out of buffer
|
||||
|
||||
} else if (rb_read_pos<write_pos_cache) {
|
||||
|
||||
rb_todo=write_pos_cache-rb_read_pos; //-1?
|
||||
} else {
|
||||
|
||||
rb_todo=(rb_len-rb_read_pos)+write_pos_cache; //-1?
|
||||
}
|
||||
|
||||
int todo = MIN( ((int64_t(rb_todo)<<MIX_FRAC_BITS)/increment)+1, p_frames );
|
||||
#if 0
|
||||
if (int(src_mix_rate)==target_mix_rate) {
|
||||
|
||||
|
||||
if (channels==6) {
|
||||
|
||||
for(int i=0;i<p_frames;i++) {
|
||||
|
||||
int from = ((rb_read_pos+i)&rb_mask)*6;
|
||||
int to = i*6;
|
||||
|
||||
p_dest[from+0]=int32_t(rb[to+0])<<16;
|
||||
p_dest[from+1]=int32_t(rb[to+1])<<16;
|
||||
p_dest[from+2]=int32_t(rb[to+2])<<16;
|
||||
p_dest[from+3]=int32_t(rb[to+3])<<16;
|
||||
p_dest[from+4]=int32_t(rb[to+4])<<16;
|
||||
p_dest[from+5]=int32_t(rb[to+5])<<16;
|
||||
}
|
||||
|
||||
} else {
|
||||
int len=p_frames*channels;
|
||||
int from=rb_read_pos*channels;
|
||||
int mask=0;
|
||||
switch(channels) {
|
||||
case 1: mask=rb_len-1; break;
|
||||
case 2: mask=(rb_len*2)-1; break;
|
||||
case 4: mask=(rb_len*4)-1; break;
|
||||
}
|
||||
|
||||
for(int i=0;i<len;i++) {
|
||||
|
||||
p_dest[i]=int32_t(rb[(from+i)&mask])<<16;
|
||||
}
|
||||
}
|
||||
|
||||
rb_read_pos = (rb_read_pos+p_frames)&rb_mask;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
|
||||
uint32_t read=0;
|
||||
switch(channels) {
|
||||
case 1: read=_resample<1>(p_dest,todo,increment); break;
|
||||
case 2: read=_resample<2>(p_dest,todo,increment); break;
|
||||
case 4: read=_resample<4>(p_dest,todo,increment); break;
|
||||
case 6: read=_resample<6>(p_dest,todo,increment); break;
|
||||
}
|
||||
#if 1
|
||||
//end of stream, fadeout
|
||||
int remaining = p_frames-todo;
|
||||
if (remaining && todo>0) {
|
||||
|
||||
//print_line("fadeout");
|
||||
for(int c=0;c<channels;c++) {
|
||||
|
||||
for(int i=0;i<todo;i++) {
|
||||
|
||||
int32_t samp = p_dest[i*channels+c]>>8;
|
||||
uint32_t mul = (todo-i) * 256 /todo;
|
||||
//print_line("mul: "+itos(i)+" "+itos(mul));
|
||||
p_dest[i*channels+c]=samp*mul;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
int remaining = p_frames-todo;
|
||||
if (remaining && todo>0) {
|
||||
|
||||
|
||||
for(int c=0;c<channels;c++) {
|
||||
|
||||
int32_t from = p_dest[(todo-1)*channels+c]>>8;
|
||||
|
||||
for(int i=0;i<remaining;i++) {
|
||||
|
||||
uint32_t mul = (remaining-i) * 256 /remaining;
|
||||
p_dest[(todo+i)*channels+c]=from*mul;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
//zero out what remains there to avoid glitches
|
||||
for(int i=todo*channels;i<int(p_frames)*channels;i++) {
|
||||
|
||||
p_dest[i]=0;
|
||||
}
|
||||
|
||||
if (read>rb_todo)
|
||||
read=rb_todo;
|
||||
|
||||
rb_read_pos = (rb_read_pos+read)&rb_mask;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Error AudioRBResampler::setup(int p_channels,int p_src_mix_rate,int p_target_mix_rate,int p_buffer_msec,int p_minbuff_needed) {
|
||||
|
||||
ERR_FAIL_COND_V(p_channels!=1 && p_channels!=2 && p_channels!=4 && p_channels!=6,ERR_INVALID_PARAMETER);
|
||||
|
||||
|
||||
//float buffering_sec = int(GLOBAL_DEF("audio/stream_buffering_ms",500))/1000.0;
|
||||
int desired_rb_bits =nearest_shift(MAX((p_buffer_msec/1000.0)*p_src_mix_rate,p_minbuff_needed));
|
||||
|
||||
bool recreate=!rb;
|
||||
|
||||
if (rb && (uint32_t(desired_rb_bits)!=rb_bits || channels!=uint32_t(p_channels))) {
|
||||
//recreate
|
||||
|
||||
memdelete_arr(rb);
|
||||
memdelete_arr(read_buf);
|
||||
recreate=true;
|
||||
|
||||
}
|
||||
|
||||
if (recreate) {
|
||||
|
||||
channels=p_channels;
|
||||
rb_bits=desired_rb_bits;
|
||||
rb_len=(1<<rb_bits);
|
||||
rb_mask=rb_len-1;
|
||||
rb = memnew_arr( int16_t, rb_len * p_channels );
|
||||
read_buf = memnew_arr( int16_t, rb_len * p_channels );
|
||||
|
||||
}
|
||||
|
||||
src_mix_rate=p_src_mix_rate;
|
||||
target_mix_rate=p_target_mix_rate;
|
||||
offset=0;
|
||||
rb_read_pos=0;
|
||||
rb_write_pos=0;
|
||||
|
||||
//avoid maybe strange noises upon load
|
||||
for (int i=0;i<(rb_len*channels);i++) {
|
||||
|
||||
rb[i]=0;
|
||||
read_buf[i]=0;
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
||||
}
|
||||
|
||||
void AudioRBResampler::clear() {
|
||||
|
||||
if (!rb)
|
||||
return;
|
||||
|
||||
//should be stopped at this point but just in case
|
||||
if (rb) {
|
||||
memdelete_arr(rb);
|
||||
memdelete_arr(read_buf);
|
||||
}
|
||||
rb=NULL;
|
||||
offset=0;
|
||||
rb_read_pos=0;
|
||||
rb_write_pos=0;
|
||||
read_buf=NULL;
|
||||
}
|
||||
|
||||
AudioRBResampler::AudioRBResampler() {
|
||||
|
||||
rb=NULL;
|
||||
offset=0;
|
||||
read_buf=NULL;
|
||||
rb_read_pos=0;
|
||||
rb_write_pos=0;
|
||||
|
||||
rb_bits=0;
|
||||
rb_len=0;
|
||||
rb_mask=0;
|
||||
read_buff_len=0;
|
||||
channels=0;
|
||||
src_mix_rate=0;
|
||||
target_mix_rate=0;
|
||||
|
||||
}
|
||||
|
||||
AudioRBResampler::~AudioRBResampler() {
|
||||
|
||||
if (rb) {
|
||||
memdelete_arr(rb);
|
||||
memdelete_arr(read_buf);
|
||||
}
|
||||
|
||||
}
|
||||
|
133
servers/audio/audio_rb_resampler.h
Normal file
133
servers/audio/audio_rb_resampler.h
Normal file
|
@ -0,0 +1,133 @@
|
|||
#ifndef AUDIO_RB_RESAMPLER_H
|
||||
#define AUDIO_RB_RESAMPLER_H
|
||||
|
||||
#include "typedefs.h"
|
||||
#include "os/memory.h"
|
||||
|
||||
struct AudioRBResampler {
|
||||
|
||||
uint32_t rb_bits;
|
||||
uint32_t rb_len;
|
||||
uint32_t rb_mask;
|
||||
uint32_t read_buff_len;
|
||||
uint32_t channels;
|
||||
uint32_t src_mix_rate;
|
||||
uint32_t target_mix_rate;
|
||||
|
||||
volatile int rb_read_pos;
|
||||
volatile int rb_write_pos;
|
||||
|
||||
int32_t offset; //contains the fractional remainder of the resampler
|
||||
enum {
|
||||
MIX_FRAC_BITS=13,
|
||||
MIX_FRAC_LEN=(1<<MIX_FRAC_BITS),
|
||||
MIX_FRAC_MASK=MIX_FRAC_LEN-1,
|
||||
};
|
||||
|
||||
int16_t *read_buf;
|
||||
int16_t *rb;
|
||||
|
||||
|
||||
template<int C>
|
||||
uint32_t _resample(int32_t *p_dest,int p_todo,int32_t p_increment);
|
||||
|
||||
|
||||
public:
|
||||
|
||||
_FORCE_INLINE_ void flush() {
|
||||
rb_read_pos=0;
|
||||
rb_write_pos=0;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool is_ready() const{
|
||||
return rb!=NULL;
|
||||
}
|
||||
|
||||
|
||||
_FORCE_INLINE_ int get_total() const {
|
||||
|
||||
return rb_len-1;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int get_todo() const { //return amount of frames to mix
|
||||
|
||||
int todo;
|
||||
int read_pos_cache=rb_read_pos;
|
||||
|
||||
if (read_pos_cache==rb_write_pos) {
|
||||
todo=rb_len-1;
|
||||
} else if (read_pos_cache>rb_write_pos) {
|
||||
|
||||
todo=read_pos_cache-rb_write_pos-1;
|
||||
} else {
|
||||
|
||||
todo=(rb_len-rb_write_pos)+read_pos_cache-1;
|
||||
}
|
||||
|
||||
return todo;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int16_t *get_write_buffer() { return read_buf; }
|
||||
_FORCE_INLINE_ void write(uint32_t p_frames) {
|
||||
|
||||
ERR_FAIL_COND(p_frames >= rb_len);
|
||||
|
||||
switch(channels) {
|
||||
case 1: {
|
||||
|
||||
for(uint32_t i=0;i<p_frames;i++) {
|
||||
|
||||
rb[ rb_write_pos ] = read_buf[i];
|
||||
rb_write_pos=(rb_write_pos+1)&rb_mask;
|
||||
}
|
||||
} break;
|
||||
case 2: {
|
||||
|
||||
for(uint32_t i=0;i<p_frames;i++) {
|
||||
|
||||
rb[ (rb_write_pos<<1)+0 ] = read_buf[(i<<1)+0];
|
||||
rb[ (rb_write_pos<<1)+1 ] = read_buf[(i<<1)+1];
|
||||
rb_write_pos=(rb_write_pos+1)&rb_mask;
|
||||
}
|
||||
} break;
|
||||
case 4: {
|
||||
|
||||
for(uint32_t i=0;i<p_frames;i++) {
|
||||
|
||||
rb[ (rb_write_pos<<2)+0 ] = read_buf[(i<<2)+0];
|
||||
rb[ (rb_write_pos<<2)+1 ] = read_buf[(i<<2)+1];
|
||||
rb[ (rb_write_pos<<2)+2 ] = read_buf[(i<<2)+2];
|
||||
rb[ (rb_write_pos<<2)+3 ] = read_buf[(i<<2)+3];
|
||||
rb_write_pos=(rb_write_pos+1)&rb_mask;
|
||||
}
|
||||
} break;
|
||||
case 6: {
|
||||
|
||||
for(uint32_t i=0;i<p_frames;i++) {
|
||||
|
||||
rb[ (rb_write_pos*6)+0 ] = read_buf[(i*6)+0];
|
||||
rb[ (rb_write_pos*6)+1 ] = read_buf[(i*6)+1];
|
||||
rb[ (rb_write_pos*6)+2 ] = read_buf[(i*6)+2];
|
||||
rb[ (rb_write_pos*6)+3 ] = read_buf[(i*6)+3];
|
||||
rb[ (rb_write_pos*6)+4 ] = read_buf[(i*6)+4];
|
||||
rb[ (rb_write_pos*6)+5 ] = read_buf[(i*6)+5];
|
||||
rb_write_pos=(rb_write_pos+1)&rb_mask;
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int get_channel_count() const;
|
||||
|
||||
Error setup(int p_channels, int p_src_mix_rate, int p_target_mix_rate, int p_buffer_msec, int p_minbuff_needed=-1);
|
||||
void clear();
|
||||
bool mix(int32_t *p_dest, int p_frames);
|
||||
|
||||
AudioRBResampler();
|
||||
~AudioRBResampler();
|
||||
};
|
||||
|
||||
#endif // AUDIO_RB_RESAMPLER_H
|
|
@ -830,10 +830,14 @@ void AudioServerSW::finish() {
|
|||
void AudioServerSW::_update_streams(bool p_thread) {
|
||||
|
||||
_THREAD_SAFE_METHOD_
|
||||
for(List<Stream*>::Element *E=active_audio_streams.front();E;E=E->next()) {
|
||||
for(List<Stream*>::Element *E=active_audio_streams.front();E;) { //stream might be removed durnig this callback
|
||||
|
||||
List<Stream*>::Element *N=E->next();
|
||||
|
||||
if (E->get()->audio_stream && p_thread == E->get()->audio_stream->can_update_mt())
|
||||
E->get()->audio_stream->update();
|
||||
|
||||
E=N;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -201,6 +201,8 @@ public:
|
|||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class SpatialPlayerSpatialGizmo : public SpatialGizmoTool {
|
||||
|
||||
OBJ_TYPE(SpatialPlayerSpatialGizmo,SpatialGizmoTool);
|
||||
|
@ -214,6 +216,8 @@ public:
|
|||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class TestCubeSpatialGizmo : public SpatialGizmoTool {
|
||||
|
||||
OBJ_TYPE(TestCubeSpatialGizmo,SpatialGizmoTool);
|
||||
|
|
Loading…
Reference in a new issue