Skip to content

Commit ece40d6

Browse files
committed
implement godot performer
1 parent 0c8f0ed commit ece40d6

File tree

11 files changed

+382
-2
lines changed

11 files changed

+382
-2
lines changed

platforms/godot/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ add_library(
2020
engine.h
2121
instrument.cpp
2222
instrument.h
23+
performer.cpp
24+
performer.h
2325
godot.cpp
2426
)
2527
target_compile_definitions(

platforms/godot/engine.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ void BarelyEngine::_bind_methods() {
130130

131131
BARELY_GODOT_ENGINE_CONTROLS(BARELY_BIND_GODOT_ENGINE_CONTROL);
132132

133+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tempo"), "set_tempo", "get_tempo");
134+
133135
ADD_PROPERTY(
134136
PropertyInfo(Variant::FLOAT, "comp_mix", PropertyHint::PROPERTY_HINT_RANGE, "0,1,0.01"),
135137
"set_comp_mix", "get_comp_mix");

platforms/godot/godot.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "gdextension_interface.h"
22
#include "godot/engine.h"
33
#include "godot/instrument.h"
4+
#include "godot/performer.h"
45
#include "godot_cpp/classes/engine.hpp"
56
#include "godot_cpp/core/memory.hpp"
67

@@ -18,6 +19,8 @@ void initialize_barelymusiciangodot(ModuleInitializationLevel level) {
1819
ClassDB::register_class<barely::godot::BarelyEngine>();
1920
ClassDB::register_class<barely::godot::BarelySliceResource>();
2021
ClassDB::register_class<barely::godot::BarelyInstrument>();
22+
ClassDB::register_class<barely::godot::BarelyTaskResource>();
23+
ClassDB::register_class<barely::godot::BarelyPerformer>();
2124
::godot::Engine::get_singleton()->register_singleton("BarelyEngine",
2225
memnew(barely::godot::BarelyEngine));
2326
}

platforms/godot/instrument.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ using ::godot::Variant;
3232
ClassDB::bind_method(D_METHOD(BARELY_STR(set_##name), #name), &BarelyInstrument::set_##name); \
3333
ClassDB::bind_method(D_METHOD(BARELY_STR(get_##name)), &BarelyInstrument::get_##name);
3434
#define BARELY_SET_DEFAULT_GODOT_INSTRUMENT_CONTROL(Name, name, type, default) set_##name(default);
35-
#define BARELY_SET_GODOT_INSTRUMENT_CONTROL(Name, name, type, default) set_##name(name##_);
3635

3736
void BarelySliceResource::set_root_pitch(float root_pitch) {
3837
if (root_pitch_ != root_pitch) {

platforms/godot/instrument.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "godot/engine.h"
99
#include "godot_cpp/classes/audio_stream_wav.hpp"
1010
#include "godot_cpp/classes/node.hpp"
11+
#include "godot_cpp/classes/resource.hpp"
1112
#include "godot_cpp/classes/wrapped.hpp"
1213

1314
namespace barely::godot {

platforms/godot/performer.cpp

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
#include "godot/performer.h"
2+
3+
#include <algorithm>
4+
5+
#include "godot/engine.h"
6+
#include "godot_cpp/core/class_db.hpp"
7+
8+
namespace barely::godot {
9+
10+
using ::godot::Callable;
11+
using ::godot::ClassDB;
12+
using ::godot::D_METHOD;
13+
using ::godot::PropertyHint;
14+
using ::godot::PropertyInfo;
15+
using ::godot::Ref;
16+
using ::godot::TypedArray;
17+
using ::godot::Variant;
18+
19+
void BarelyTaskResource::set_position(double position) {
20+
position_ = position;
21+
emit_changed();
22+
}
23+
24+
void BarelyTaskResource::set_duration(double duration) {
25+
static constexpr double kMinTaskDuration = 1e-6;
26+
duration_ = std::max(duration, kMinTaskDuration);
27+
emit_changed();
28+
}
29+
30+
void BarelyTaskResource::set_priority(int32_t priority) {
31+
priority_ = priority;
32+
emit_changed();
33+
}
34+
35+
void BarelyTaskResource::set_begin_callback(const Callable& callback) {
36+
begin_callback_ = callback;
37+
emit_changed();
38+
}
39+
40+
void BarelyTaskResource::set_end_callback(const Callable& callback) {
41+
end_callback_ = callback;
42+
emit_changed();
43+
}
44+
45+
void BarelyTaskResource::_bind_methods() {
46+
ClassDB::bind_method(D_METHOD("set_position", "position"), &BarelyTaskResource::set_position);
47+
ClassDB::bind_method(D_METHOD("get_position"), &BarelyTaskResource::get_position);
48+
49+
ClassDB::bind_method(D_METHOD("set_duration", "duration"), &BarelyTaskResource::set_duration);
50+
ClassDB::bind_method(D_METHOD("get_duration"), &BarelyTaskResource::get_duration);
51+
52+
ClassDB::bind_method(D_METHOD("set_priority", "priority"), &BarelyTaskResource::set_priority);
53+
ClassDB::bind_method(D_METHOD("get_priority"), &BarelyTaskResource::get_priority);
54+
55+
ClassDB::bind_method(D_METHOD("set_begin_callback", "callback"),
56+
&BarelyTaskResource::set_begin_callback);
57+
ClassDB::bind_method(D_METHOD("get_begin_callback"), &BarelyTaskResource::get_begin_callback);
58+
59+
ClassDB::bind_method(D_METHOD("set_end_callback", "callback"),
60+
&BarelyTaskResource::set_end_callback);
61+
ClassDB::bind_method(D_METHOD("get_end_callback"), &BarelyTaskResource::get_end_callback);
62+
63+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "position"), "set_position", "get_position");
64+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "duration"), "set_duration", "get_duration");
65+
ADD_PROPERTY(PropertyInfo(Variant::INT, "priority"), "set_priority", "get_priority");
66+
67+
ADD_PROPERTY(PropertyInfo(Variant::CALLABLE, "begin_callback"), "set_begin_callback",
68+
"get_begin_callback");
69+
ADD_PROPERTY(PropertyInfo(Variant::CALLABLE, "end_callback"), "set_end_callback",
70+
"get_end_callback");
71+
}
72+
73+
BarelyPerformer::BarelyPerformer() {
74+
BarelyEngine_CreatePerformer(BarelyEngine::get_singleton()->get(), &performer_id_);
75+
tasks_ = TypedArray<Ref<BarelyTaskResource>>();
76+
}
77+
78+
BarelyPerformer::~BarelyPerformer() {
79+
_clear_tasks();
80+
BarelyEngine_DestroyPerformer(BarelyEngine::get_singleton()->get(), performer_id_);
81+
}
82+
83+
void BarelyPerformer::start() {
84+
BarelyPerformer_Start(BarelyEngine::get_singleton()->get(), performer_id_);
85+
}
86+
87+
void BarelyPerformer::stop() {
88+
BarelyPerformer_Stop(BarelyEngine::get_singleton()->get(), performer_id_);
89+
}
90+
91+
void BarelyPerformer::set_position(double position) {
92+
BarelyPerformer_SetPosition(BarelyEngine::get_singleton()->get(), performer_id_, position);
93+
}
94+
95+
void BarelyPerformer::set_loop_begin_position(double loop_begin_position) {
96+
loop_begin_position_ = loop_begin_position;
97+
98+
BarelyPerformer_SetLoopBeginPosition(BarelyEngine::get_singleton()->get(), performer_id_,
99+
loop_begin_position_);
100+
}
101+
102+
void BarelyPerformer::set_loop_length(double loop_length) {
103+
loop_length_ = loop_length;
104+
105+
BarelyPerformer_SetLoopLength(BarelyEngine::get_singleton()->get(), performer_id_, loop_length_);
106+
}
107+
108+
void BarelyPerformer::set_looping(bool looping) {
109+
looping_ = looping;
110+
111+
BarelyPerformer_SetLooping(BarelyEngine::get_singleton()->get(), performer_id_, looping_);
112+
}
113+
114+
void BarelyPerformer::set_tasks(const TypedArray<Ref<BarelyTaskResource>>& tasks) {
115+
for (int i = 0; i < tasks_.size(); ++i) {
116+
Ref<BarelyTaskResource> task = tasks_[i];
117+
if (task.is_valid()) {
118+
task->disconnect("changed", Callable(this, "_on_task_changed"));
119+
}
120+
}
121+
tasks_ = tasks;
122+
for (int i = 0; i < tasks_.size(); ++i) {
123+
Ref<BarelyTaskResource> task = tasks_[i];
124+
if (task.is_valid()) {
125+
task->connect("changed", Callable(this, "_on_task_changed"));
126+
}
127+
}
128+
_on_task_changed();
129+
}
130+
131+
double BarelyPerformer::get_position() const {
132+
double position = 0.0;
133+
BarelyPerformer_GetPosition(BarelyEngine::get_singleton()->get(), performer_id_, &position);
134+
return position;
135+
}
136+
137+
bool BarelyPerformer::is_playing() const {
138+
bool playing = false;
139+
BarelyPerformer_IsPlaying(BarelyEngine::get_singleton()->get(), performer_id_, &playing);
140+
return playing;
141+
}
142+
143+
void BarelyPerformer::_bind_methods() {
144+
ClassDB::bind_method(D_METHOD("start"), &BarelyPerformer::start);
145+
ClassDB::bind_method(D_METHOD("stop"), &BarelyPerformer::stop);
146+
147+
ClassDB::bind_method(D_METHOD("set_position", "position"), &BarelyPerformer::set_position);
148+
ClassDB::bind_method(D_METHOD("get_position"), &BarelyPerformer::get_position);
149+
150+
ClassDB::bind_method(D_METHOD("is_playing"), &BarelyPerformer::is_playing);
151+
152+
ClassDB::bind_method(D_METHOD("set_loop_begin_position", "position"),
153+
&BarelyPerformer::set_loop_begin_position);
154+
ClassDB::bind_method(D_METHOD("get_loop_begin_position"),
155+
&BarelyPerformer::get_loop_begin_position);
156+
ClassDB::bind_method(D_METHOD("set_loop_length", "length"), &BarelyPerformer::set_loop_length);
157+
ClassDB::bind_method(D_METHOD("get_loop_length"), &BarelyPerformer::get_loop_length);
158+
ClassDB::bind_method(D_METHOD("set_looping", "looping"), &BarelyPerformer::set_looping);
159+
ClassDB::bind_method(D_METHOD("is_looping"), &BarelyPerformer::is_looping);
160+
161+
ClassDB::bind_method(D_METHOD("set_tasks", "tasks"), &BarelyPerformer::set_tasks);
162+
ClassDB::bind_method(D_METHOD("get_tasks"), &BarelyPerformer::get_tasks);
163+
ClassDB::bind_method(D_METHOD("_on_task_changed"), &BarelyPerformer::_on_task_changed);
164+
165+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_begin_position"), "set_loop_begin_position",
166+
"get_loop_begin_position");
167+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_length"), "set_loop_length", "get_loop_length");
168+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "looping"), "set_looping", "is_looping");
169+
170+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "position"), "set_position", "get_position");
171+
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "tasks", PropertyHint::PROPERTY_HINT_ARRAY_TYPE,
172+
"BarelyTaskResource"),
173+
"set_tasks", "get_tasks");
174+
}
175+
176+
void BarelyPerformer::_clear_tasks() {
177+
for (uint32_t task_id : task_ids_) {
178+
BarelyEngine_DestroyTask(BarelyEngine::get_singleton()->get(), task_id);
179+
}
180+
task_ids_.clear();
181+
}
182+
183+
void BarelyPerformer::_on_task_changed() {
184+
_clear_tasks();
185+
186+
for (int i = 0; i < tasks_.size(); ++i) {
187+
Ref<BarelyTaskResource> task = tasks_[i];
188+
if (task.is_null()) {
189+
continue;
190+
}
191+
192+
uint32_t task_id = 0;
193+
BarelyEngine_CreateTask(
194+
BarelyEngine::get_singleton()->get(), performer_id_, task->get_position(),
195+
task->get_duration(), task->get_priority(),
196+
[](BarelyEventType type, void* user_data) {
197+
BarelyTaskResource* task = static_cast<BarelyTaskResource*>(user_data);
198+
199+
if (!task) return;
200+
201+
if (type == BarelyEventType_kBegin) {
202+
const auto& cb = task->get_begin_callback();
203+
if (cb.is_valid()) cb.call();
204+
} else if (type == BarelyEventType_kEnd) {
205+
const auto& cb = task->get_end_callback();
206+
if (cb.is_valid()) cb.call();
207+
}
208+
},
209+
task.ptr(), &task_id);
210+
task_ids_.push_back(task_id);
211+
}
212+
}
213+
214+
} // namespace barely::godot

platforms/godot/performer.h

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#ifndef BARELYMUSICIAN_GODOT_PERFORMER_H_
2+
#define BARELYMUSICIAN_GODOT_PERFORMER_H_
3+
4+
#include <barelymusician.h>
5+
6+
#include <cstdint>
7+
#include <vector>
8+
9+
#include "godot_cpp/classes/node.hpp"
10+
#include "godot_cpp/classes/resource.hpp"
11+
#include "godot_cpp/variant/callable.hpp"
12+
#include "godot_cpp/variant/typed_array.hpp"
13+
14+
namespace barely::godot {
15+
16+
class BarelyTaskResource : public ::godot::Resource {
17+
public:
18+
void set_position(double position);
19+
void set_duration(double duration);
20+
void set_priority(int32_t priority);
21+
void set_begin_callback(const ::godot::Callable& callback);
22+
void set_end_callback(const ::godot::Callable& callback);
23+
24+
double get_position() const { return position_; }
25+
double get_duration() const { return duration_; }
26+
int32_t get_priority() const { return priority_; }
27+
const ::godot::Callable& get_begin_callback() const { return begin_callback_; }
28+
const ::godot::Callable& get_end_callback() const { return end_callback_; }
29+
30+
private:
31+
GDCLASS(BarelyTaskResource, ::godot::Resource);
32+
static void _bind_methods();
33+
34+
double position_ = 0.0;
35+
double duration_ = 1.0;
36+
int32_t priority_ = 0;
37+
38+
::godot::Callable begin_callback_;
39+
::godot::Callable end_callback_;
40+
};
41+
42+
class BarelyPerformer : public ::godot::Node {
43+
public:
44+
BarelyPerformer();
45+
~BarelyPerformer();
46+
47+
void start();
48+
void stop();
49+
50+
void set_position(double position);
51+
void set_loop_begin_position(double position);
52+
void set_loop_length(double length);
53+
void set_looping(bool looping);
54+
55+
void set_tasks(const ::godot::TypedArray<::godot::Ref<BarelyTaskResource>>& tasks);
56+
57+
double get_position() const;
58+
bool is_playing() const;
59+
60+
double get_loop_begin_position() const { return loop_begin_position_; }
61+
double get_loop_length() const { return loop_length_; }
62+
bool is_looping() const { return looping_; }
63+
64+
::godot::TypedArray<::godot::Ref<BarelyTaskResource>> get_tasks() const { return tasks_; }
65+
66+
private:
67+
GDCLASS(BarelyPerformer, ::godot::Node);
68+
static void _bind_methods();
69+
70+
void _clear_tasks();
71+
void _on_task_changed();
72+
73+
double loop_begin_position_ = 0.0;
74+
double loop_length_ = 1.0;
75+
bool looping_ = false;
76+
77+
::godot::TypedArray<::godot::Ref<BarelyTaskResource>> tasks_;
78+
std::vector<uint32_t> task_ids_;
79+
80+
uint32_t performer_id_ = 0;
81+
};
82+
83+
} // namespace barely::godot
84+
85+
#endif

platforms/godot/project/demo/instrument_demo.gd

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ extends Node
22

33
@onready var audioStreamPlayer: AudioStreamPlayer = $AudioStreamPlayer
44
@onready var instrument: BarelyInstrument = $Instrument
5-
@onready var label: Label = $Label
65

76
const OCTAVE_KEYS = ['A', 'W', 'S', 'E', 'D', 'F', 'T', 'G', 'Y', 'H', 'U', 'J', 'K']
87
const ROOT_PITCH = 0.0
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
extends Node
2+
3+
@onready var audioStreamPlayer: AudioStreamPlayer = $AudioStreamPlayer
4+
@onready var instrument: BarelyInstrument = $Instrument
5+
@onready var metronome: BarelyPerformer = $Metronome
6+
@onready var label: Label = $Label
7+
8+
@export_range(30.0, 960.0, 1) var tempo: float = 120.0
9+
@export_range(1.0, 16.0, 1) var beat_count: float = 4
10+
11+
var _beat = -1
12+
13+
func _ready() -> void:
14+
audioStreamPlayer.stream = BarelyAudioStream.new()
15+
audioStreamPlayer.play()
16+
17+
var metronome_task = metronome.tasks[0]
18+
metronome_task.begin_callback = Callable(self , "_on_task_begin")
19+
metronome_task.end_callback = Callable(self , "_on_task_end")
20+
metronome.start()
21+
22+
func _input(event):
23+
if event is InputEventKey and event.pressed:
24+
if event.keycode == KEY_SPACE:
25+
if metronome.is_playing():
26+
metronome.stop()
27+
else:
28+
metronome.start()
29+
30+
func _process(_delta):
31+
BarelyEngine.tempo = tempo
32+
33+
func _on_task_begin():
34+
_beat = (_beat + 1) % int(beat_count)
35+
instrument.set_note_on(1.0 if _beat == 0 else 0.0)
36+
label.text = str(_beat + 1)
37+
38+
func _on_task_end():
39+
instrument.set_note_off(1.0 if _beat == 0 else 0.0)

0 commit comments

Comments
 (0)