Skip to content
Snippets Groups Projects
Commit 860e4c61 authored by Florent Berthaut's avatar Florent Berthaut
Browse files

Got non rt audio working

parent 0bad1963
Branches
No related tags found
No related merge requests found
......@@ -4,3 +4,4 @@
[submodule "src/godot-cpp"]
path = src/godot-cpp
url = https://github.com/GodotNativeTools/godot-cpp
branch = 4.2
......@@ -29,7 +29,10 @@ env = SConscript("src/godot-cpp/SConstruct")
# - CPPDEFINES are for pre-processor defines
# - LINKFLAGS are for linking flags
env.Append(CPPPATH=['.', 'src/libpd/libpd_wrapper', 'src/libpd/libpd_wrapper/util', 'src/libpd/cpp','src/libpd/pure-data/src', 'src/rtaudio'])
env.Append(CPPPATH=['.', 'src/libpd/libpd_wrapper',
'src/libpd/libpd_wrapper/util',
'src/libpd/cpp','src/libpd/pure-data/src', 'src/rtaudio'],
CCFLAGS=["-fexceptions"])
env.Append(CFLAGS=['-DUSEAPI_DUMMY', '-DPD', '-DHAVE_UNISTD_H',\
'-D_GNU_SOURCE','-DLIBPD_EXTRA'])
......
......@@ -6,6 +6,7 @@ script/source = "extends Control
@onready var _gdpd = GdPd.new()
func _ready():
add_child(_gdpd)
pass
func _process(delta):
......@@ -42,6 +43,23 @@ func _on_Start_pressed() :
# listen to messages sent with [send to_godot]
_gdpd.subscribe(\"to_godot\")
func _on_start_non_rt_pressed():
# initialise without realtime
_gdpd.init_nort()
# the patch path should be absolute
_load_patch(ProjectSettings.globalize_path(\"res://patch1.pd\"))
_load_patch(ProjectSettings.globalize_path(\"res://patch2.pd\"))
# send message to [receive from_godot] with one symbol
_gdpd.start_message(1)
_gdpd.add_symbol(\"hello\")
_gdpd.finish_list(\"from_godot\")
# listen to messages sent with [send to_godot]
_gdpd.subscribe(\"to_godot\")
func _on_Stop_pressed():
_gdpd.closefile(\"patch1.pd\")
......@@ -62,6 +80,8 @@ func _on_HSlider2_value_changed(value):
_gdpd.add_symbol(\"pitch\")
_gdpd.add_float(value)
_gdpd.finish_list(\"from_godot\")
"
[node name="Control" type="Control"]
......@@ -73,21 +93,29 @@ grow_horizontal = 2
grow_vertical = 2
script = SubResource("1")
[node name="Start" type="Button" parent="."]
layout_mode = 0
offset_left = 72.0
offset_top = 80.0
offset_right = 176.0
offset_bottom = 128.0
text = "Start"
[node name="Stop" type="Button" parent="."]
layout_mode = 0
offset_left = 213.0
offset_top = 83.0
offset_right = 316.0
offset_bottom = 129.0
offset_left = 296.0
offset_top = 80.0
offset_right = 399.0
offset_bottom = 128.0
text = "Stop"
[node name="Start" type="Button" parent="."]
[node name="StartNonRT" type="Button" parent="."]
layout_mode = 0
offset_left = 73.0
offset_top = 83.0
offset_right = 177.0
offset_bottom = 134.0
text = "Start"
offset_left = 184.0
offset_top = 80.0
offset_right = 290.0
offset_bottom = 128.0
text = "Start Non RT"
[node name="HSlider" type="HSlider" parent="."]
layout_mode = 0
......@@ -121,7 +149,8 @@ offset_right = 204.0
offset_bottom = 260.0
text = "Send pitch to patch2"
[connection signal="pressed" from="Stop" to="." method="_on_Stop_pressed"]
[connection signal="pressed" from="Start" to="." method="_on_Start_pressed"]
[connection signal="pressed" from="Stop" to="." method="_on_Stop_pressed"]
[connection signal="pressed" from="StartNonRT" to="." method="_on_start_non_rt_pressed"]
[connection signal="value_changed" from="HSlider" to="." method="_on_HSlider_value_changed"]
[connection signal="value_changed" from="HSlider2" to="." method="_on_HSlider2_value_changed"]
......@@ -2,7 +2,7 @@
* gdpd.cpp
* Part of GdPd
* 2023- see README for AUTHORS
* https://gitlab.univ-lille.fr/immersync
* https://gitlab.univ-lille.fr/ivmi
****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
......@@ -24,14 +24,16 @@
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/classes/os.hpp>
#include <godot_cpp/classes/global_constants.hpp>
#include <godot_cpp/classes/label.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/classes/audio_server.hpp>
using namespace godot;
const int PCM_BUFFER_SIZE = 4096 * 4;
void GdPd::_bind_methods() {
ADD_GROUP("GdPd", "gdpd_");
......@@ -40,7 +42,8 @@ void GdPd::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_available_output_devices"),
&GdPd::get_available_output_devices);
ClassDB::bind_method(D_METHOD("init_devices"), &GdPd::init_devices);
ClassDB::bind_method(D_METHOD("init"), &GdPd::init);
ClassDB::bind_method(D_METHOD("init_parameters"), &GdPd::init_parameters);
ClassDB::bind_method(D_METHOD("init_nort"), &GdPd::init_nort);
ClassDB::bind_method(D_METHOD("stop"), &GdPd::stop);
ClassDB::bind_method(D_METHOD("openfile"), &GdPd::openfile);
ClassDB::bind_method(D_METHOD("closefile"), &GdPd::closefile);
......@@ -52,6 +55,7 @@ void GdPd::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_float"), &GdPd::add_float);
ClassDB::bind_method(D_METHOD("finish_list"), &GdPd::finish_list);
ClassDB::bind_method(D_METHOD("set_volume"), &GdPd::set_volume);
}
GdPd::GdPd() : m_vol(1) {
......@@ -62,12 +66,13 @@ GdPd::GdPd() : m_vol(1) {
GdPd::~GdPd() {}
int GdPd::audioCallback(void *outputBuffer, void *inputBuffer,
unsigned int nBufferFrames, double streamTime,
RtAudioStreamStatus status, void *userData){
GdPd* gdpd = static_cast<GdPd*>(userData);
gdpd->processAudio(outputBuffer, inputBuffer, nBufferFrames, streamTime,
status, userData);
gdpd->processAudio(outputBuffer, inputBuffer, nBufferFrames);
return 0;
}
......@@ -113,10 +118,13 @@ int GdPd::init_devices(String inputDevice, String outputDevice) {
m_nbOutputs = outInfo.outputChannels;
m_sampleRate = outInfo.preferredSampleRate;
m_bufferFrames = 128;
m_realtime=true;
return start();
}
int GdPd::init(int nbInputs, int nbOutputs, int sampleRate, int bufferSize) {
int GdPd::init_parameters(int nbInputs, int nbOutputs, int sampleRate, int bufferSize) {
m_inputDevice = m_audio.getDefaultInputDevice();
m_outputDevice = m_audio.getDefaultOutputDevice();
RtAudio::DeviceInfo inpInfo = m_audio.getDeviceInfo(m_inputDevice);
......@@ -125,17 +133,54 @@ int GdPd::init(int nbInputs, int nbOutputs, int sampleRate, int bufferSize) {
m_nbOutputs = std::min<int>(nbOutputs, outInfo.outputChannels);
m_sampleRate = sampleRate;
m_bufferFrames = std::max<int>(64, bufferSize);
m_realtime=true;
return start();
}
int GdPd::init_nort() {
m_nbInputs=0;
m_nbOutputs=2;
m_sampleRate=44100;
m_realtime=false;
if(!m_pd.init(m_nbInputs, m_nbOutputs, m_sampleRate, true)) {
print("GDPD : Error starting libpd");
return 1;
}
// Create message hook
m_pd.subscribe("to_gdpd");
m_pd.setReceiver(this);
m_init=true;
// Create stream player
AudioStreamPlayer* p = new AudioStreamPlayer();
add_child(p);
Ref<GdPdStream> s;
s.instantiate();
s->setGdPd(this);
p->set_stream(s);
p->play();
print("Initialized");
return 0;
}
int GdPd::start() {
RtAudio::StreamParameters outParams, inpParams;
inpParams.deviceId = m_inputDevice;
inpParams.nChannels = m_nbInputs;
outParams.deviceId = m_outputDevice;
outParams.nChannels = m_nbOutputs;
print("Output channels = "+std::to_string(outParams.nChannels));
print("Input channels = "+std::to_string(inpParams.nChannels));
RtAudio::StreamParameters outParams, inpParams;
inpParams.deviceId = m_inputDevice;
inpParams.nChannels = m_nbInputs;
outParams.deviceId = m_outputDevice;
outParams.nChannels = m_nbOutputs;
print("Output channels = "+std::to_string(outParams.nChannels));
print("Input channels = "+std::to_string(inpParams.nChannels));
if(!m_pd.init(m_nbInputs, m_nbOutputs, m_sampleRate, true)) {
......@@ -148,27 +193,32 @@ int GdPd::start() {
//start dsp
m_pd.computeAudio(true);
//intialize rtaudio
if(m_audio.getDeviceCount()==0){
UtilityFunctions::print("There are no available sound devices.");
}
RtAudio::StreamOptions options;
options.streamName = "gdpd";
options.flags = RTAUDIO_SCHEDULE_REALTIME;
if(m_audio.getCurrentApi() != RtAudio::MACOSX_CORE) {
options.flags |= RTAUDIO_MINIMIZE_LATENCY;
}
try {
m_audio.openStream(&outParams, &inpParams, RTAUDIO_FLOAT32,
m_sampleRate, &m_bufferFrames, &audioCallback,
this, &options);
m_audio.startStream();
print("Stream started");
}
catch(RtAudioError& e) {
UtilityFunctions::print(e.getMessage().c_str());
}
if(m_realtime) {
//intialize rtaudio
if(m_audio.getDeviceCount()==0){
UtilityFunctions::print("There are no available sound devices.");
return 1;
}
RtAudio::StreamOptions options;
options.streamName = "gdpd";
options.flags = RTAUDIO_SCHEDULE_REALTIME;
if(m_audio.getCurrentApi() != RtAudio::MACOSX_CORE) {
options.flags |= RTAUDIO_MINIMIZE_LATENCY;
}
try {
m_audio.openStream(&outParams, &inpParams, RTAUDIO_FLOAT32,
m_sampleRate, &m_bufferFrames, &audioCallback,
this, &options);
m_audio.startStream();
print("Stream started");
}
catch(RtAudioError& e) {
UtilityFunctions::print(e.getMessage().c_str());
}
}
else {
}
//create message hook
m_pd.subscribe("to_gdpd");
......@@ -182,15 +232,18 @@ int GdPd::start() {
void GdPd::stop() {
m_init=false;
m_audio.stopStream();
m_audio.closeStream();
if(m_realtime) {
m_audio.stopStream();
m_audio.closeStream();
}
else {
}
m_pd.computeAudio(false);
print("Stopped");
}
void GdPd::processAudio(void *outputBuffer, void *inputBuffer,
unsigned int nBufferFrames, double streamTime,
RtAudioStreamStatus status, void *userData) {
unsigned int nBufferFrames) {
int ticks = nBufferFrames / libpd_blocksize();
m_pd.processFloat(ticks, (float*)inputBuffer, (float*)outputBuffer);
......@@ -301,4 +354,44 @@ void GdPd::set_volume(float vol) {
m_vol=vol;
}
/* GdPdStream */
GdPdStream::GdPdStream() {
}
Ref<AudioStreamPlayback> GdPdStream::_instantiate_playback() const {
Ref<GdPdStreamPlayback> gdPlayback;
gdPlayback.instantiate();
gdPlayback->m_base = Ref<GdPdStream>(this);
return gdPlayback;
}
#define zeromem(to, count) memset(to, 0, count)
/* GdPdStreamplayback */
GdPdStreamPlayback::GdPdStreamPlayback() {
m_buffer.resize(4096*2,0.0);
AudioServer::get_singleton()->lock();
pcm_buffer = memalloc(PCM_BUFFER_SIZE);
zeromem(pcm_buffer, PCM_BUFFER_SIZE);
AudioServer::get_singleton()->unlock();
}
GdPdStreamPlayback::~GdPdStreamPlayback() {
}
int32_t GdPdStreamPlayback::_mix_resampled(AudioFrame *buffer,
int32_t nbFrames) {
m_buffer.resize(4096*2, 0.0);
m_base->getGdPd()->processAudio(m_buffer.data(), NULL, nbFrames);
for(int i = 0; i < nbFrames; i++) {
buffer[i].left = m_buffer[i*2];
buffer[i].right = m_buffer[i*2+1];
}
return nbFrames;
}
double GdPdStreamPlayback::_get_stream_sampling_rate() const {
return AudioServer::get_singleton()->get_mix_rate();
}
/***************************************************************************
* gd4pd.h
* gdpd.h
* Part of GdPd
* 2023- see README for AUTHORS
* https://gitlab.univ-lille.fr/ivmi
****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
......@@ -34,19 +35,26 @@
#include <godot_cpp/core/binder_common.hpp>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/audio_stream.hpp>
#include <godot_cpp/classes/audio_stream_playback_resampled.hpp>
#include <godot_cpp/classes/audio_stream_player.hpp>
#include <godot_cpp/classes/audio_frame.hpp>
#include "PdBase.hpp"
#include "RtAudio.h"
using namespace godot;
class GdPd : public AudioStream, public pd::PdReceiver {
GDCLASS(GdPd, AudioStream);
class GdPdStreamPlayback;
class GdPd : public Node, public pd::PdReceiver {
GDCLASS(GdPd, Node);
protected:
static void _bind_methods();
private:
bool m_realtime;
float *m_inBuf;
float *m_outBuf;
Array* m_messages;
......@@ -64,6 +72,8 @@ private:
bool m_init;
int start();
public:
GdPd();
......@@ -72,9 +82,9 @@ public:
//libpd functions
Array get_available_input_devices();
Array get_available_output_devices();
int init_nort();
int init_devices(String inputDevice, String outputDevice);
int init(int nbInputs, int nbOutputs, int sampleRate, int bufferSize);
int start();
int init_parameters(int nbInputs, int nbOutputs, int sampleRate, int bufferSize);
void stop();
bool openfile(String basename, String dirname);
void closefile(String basename);
......@@ -101,9 +111,56 @@ public:
unsigned int nBufferFrames, double streamTime,
RtAudioStreamStatus status, void *userData);
void processAudio(void *outputBuffer, void *inputBuffer,
unsigned int nBufferFrames, double streamTime,
RtAudioStreamStatus status, void *userData);
unsigned int nBufferFrames);
};
class GdPdStream : public AudioStream {
GDCLASS(GdPdStream, AudioStream);
friend class GdPdStreamPlayback;
private:
GdPd* m_gdpd;
protected:
static void _bind_methods(){}
public:
GdPdStream();
Ref<AudioStreamPlayback> _instantiate_playback() const override;
virtual String _get_stream_name() const {return "GdPd";}
virtual double _get_length() const { return 0; }
void setGdPd(GdPd* gdpd){m_gdpd=gdpd;}
GdPd* getGdPd(){return m_gdpd;}
};
class GdPdStreamPlayback: public AudioStreamPlaybackResampled {
GDCLASS(GdPdStreamPlayback, AudioStreamPlaybackResampled);
friend class GdPdStream;
protected:
Ref<GdPdStream> m_base;
std::vector<float> m_buffer;
bool active;
void *pcm_buffer;
static void _bind_methods(){}
public:
GdPdStreamPlayback();
~GdPdStreamPlayback();
int32_t _mix_resampled(AudioFrame *dst_buffer, int32_t frame_count) override;
double _get_stream_sampling_rate() const override;
bool _is_playing() const override {return active;}
void _start(double from_pos) override {active=true;}
void _seek(double position) override {}
void _stop() override {active=false;}
};
#endif
Subproject commit 28494f0bd59f9c35289524e503648ed8b4baaa59
Subproject commit 98c143a48365f3f3bf5f99d6289a2cb25e6472d1
......@@ -15,6 +15,8 @@ void initialize_gdpd_module(ModuleInitializationLevel p_level) {
return;
}
ClassDB::register_class<GdPd>();
ClassDB::register_class<GdPdStream>();
ClassDB::register_class<GdPdStreamPlayback>();
}
void uninitialize_gdpd_module(ModuleInitializationLevel p_level) {
......
No preview for this file type
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment