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:
Juan Linietsky 2015-09-09 18:50:52 -03:00
parent b0aa49accb
commit 9741374617
21 changed files with 1433 additions and 506 deletions

View file

@ -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();

View file

@ -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:

View file

@ -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;
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) {

View file

@ -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);

View file

@ -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, &current_section);
long ret=ov_read(&vf,(char*)p_bufer,todo*stream_channels*sizeof(int16_t), 1, 2, 1, &current_section);
#else
long ret=ov_read(&vf,(char*)get_write_buffer(),todo*stream_channels*sizeof(int16_t), 0, 2, 1, &current_section);
long ret=ov_read(&vf,(char*)p_bufer,todo*stream_channels*sizeof(int16_t), 0, 2, 1, &current_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;; // :(
}
frames_mixed=0;
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;
repeats=0;
setting_up=false;
stream_valid=false;
repeats=0;
paused=true;
stream_channels=0;
stream_srate=0;
current_section=0;
length=0;
loop_restart_time=0;
}
AudioStreamOGGVorbis::~AudioStreamOGGVorbis() {
AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
_clear_stream();

View file

@ -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 {

View file

@ -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();
}

View file

@ -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();

View file

@ -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);
AudioServer::get_singleton()->free(stream_rid);
resampler.clear();
}

View file

@ -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();

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View 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);
}
}

View 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

View file

@ -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;
}
}

View file

@ -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);