Skip to content

Commit c8e8e6b

Browse files
committed
inplement true zero duration task
1 parent f669f23 commit c8e8e6b

8 files changed

Lines changed: 120 additions & 100 deletions

examples/demo/metronome_demo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ int main(void) {
6868
auto metronome = engine.CreatePerformer();
6969
metronome.SetLooping(true);
7070
int beat = 0;
71-
metronome.CreateTask(0.0, 1e-6, 0, [&](TaskEventType type) {
71+
metronome.CreateTask(0.0, 0.0, 0, [&](TaskEventType type) {
7272
if (type != TaskEventType::kBegin) {
7373
return;
7474
}

src/barelymusician.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ uint32_t BarelyPerformer_CreateTask(BarelyEngine* engine, uint32_t performer_id,
201201
void* user_data) {
202202
if (engine != nullptr && engine->IsValidPerformer(performer_id)) {
203203
const uint32_t task_index = engine->controller.performer_controller().AcquireTask(
204-
engine->state.GetIdIndex(performer_id), position, duration, priority, callback, user_data);
204+
engine->state.GetIdIndex(performer_id), position, std::max(duration, 0.0), priority,
205+
callback, user_data);
205206
if (task_index != barely::kInvalidIndex) {
206207
return engine->state.BuildId(task_index, engine->state.task_generations[task_index]);
207208
}
@@ -293,7 +294,7 @@ void BarelyTask_SetCallback(BarelyEngine* engine, uint32_t task_id, BarelyTaskCa
293294
void BarelyTask_SetDuration(BarelyEngine* engine, uint32_t task_id, double duration) {
294295
if (engine != nullptr && engine->IsValidTask(task_id)) {
295296
engine->controller.performer_controller().SetTaskDuration(engine->state.GetIdIndex(task_id),
296-
duration);
297+
std::max(duration, 0.0));
297298
}
298299
}
299300

src/engine/engine_controller.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <cassert>
55
#include <cstdint>
6+
#include <optional>
67

78
#include "core/time.h"
89
#include "engine/engine_state.h"
@@ -21,21 +22,24 @@ class EngineController {
2122
}
2223

2324
void Update(double timestamp) noexcept {
25+
std::optional<int32_t> min_priority = std::nullopt;
2426
while (engine_.timestamp < timestamp) {
2527
if (engine_.tempo > 0.0) {
2628
const double max_update_duration =
2729
SecondsToBeats(engine_.tempo, timestamp - engine_.timestamp);
2830

2931
double update_duration = max_update_duration;
3032
int32_t max_priority = INT32_MIN;
31-
performer_controller_.GetNextTaskEvent(update_duration, max_priority);
33+
performer_controller_.GetNextTaskEvent(min_priority, update_duration, max_priority);
3234

33-
if (update_duration > 0) {
34-
performer_controller_.Update(update_duration);
35+
if (update_duration > 0.0) {
36+
performer_controller_.UpdatePosition(update_duration);
3537
engine_.timestamp += BeatsToSeconds(engine_.tempo, update_duration);
38+
min_priority = std::nullopt;
3639
}
3740
if (update_duration < max_update_duration) {
38-
performer_controller_.ProcessAllTasksAtPosition(max_priority);
41+
performer_controller_.ProcessAllTasksAtPosition(min_priority, max_priority);
42+
min_priority = max_priority;
3943
}
4044
} else if (engine_.timestamp < timestamp) {
4145
engine_.timestamp = timestamp;

src/engine/engine_controller_test.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <cstdint>
77
#include <functional>
88
#include <memory>
9+
#include <optional>
910

1011
#include "core/arena.h"
1112
#include "engine/engine_state.h"
@@ -52,19 +53,20 @@ TEST(EngineControllerTest, AcquireReleasePerformer) {
5253
EXPECT_TRUE(performer.is_playing);
5354
EXPECT_FALSE(task.is_active);
5455

56+
std::optional<int32_t> min_priority = std::nullopt;
5557
double duration = 10.0;
5658
int32_t max_priority = INT32_MIN;
5759

5860
// Update the timestamp just before the task, which should not be triggered.
59-
controller.performer_controller().GetNextTaskEvent(duration, max_priority);
61+
controller.performer_controller().GetNextTaskEvent(min_priority, duration, max_priority);
6062
EXPECT_DOUBLE_EQ(duration, 1.0);
6163
EXPECT_EQ(max_priority, 0);
6264

6365
controller.Update(1.0);
6466

6567
duration = 10.0;
6668
max_priority = INT32_MIN;
67-
controller.performer_controller().GetNextTaskEvent(duration, max_priority);
69+
controller.performer_controller().GetNextTaskEvent(min_priority, duration, max_priority);
6870
EXPECT_DOUBLE_EQ(duration, 0.0);
6971
EXPECT_EQ(max_priority, 0);
7072

@@ -76,15 +78,15 @@ TEST(EngineControllerTest, AcquireReleasePerformer) {
7678
// Update the timestamp inside the task, which should be triggered now.
7779
duration = 10.0;
7880
max_priority = INT32_MIN;
79-
controller.performer_controller().GetNextTaskEvent(duration, max_priority);
81+
controller.performer_controller().GetNextTaskEvent(min_priority, duration, max_priority);
8082
EXPECT_DOUBLE_EQ(duration, 0.0);
8183
EXPECT_EQ(max_priority, 0);
8284

8385
controller.Update(2.5);
8486

8587
duration = 10.0;
8688
max_priority = INT32_MIN;
87-
controller.performer_controller().GetNextTaskEvent(duration, max_priority);
89+
controller.performer_controller().GetNextTaskEvent(min_priority, duration, max_priority);
8890
EXPECT_DOUBLE_EQ(duration, 0.5);
8991
EXPECT_EQ(max_priority, 0);
9092

@@ -96,15 +98,15 @@ TEST(EngineControllerTest, AcquireReleasePerformer) {
9698
// Update the timestamp just past the task, which should not be active anymore.
9799
duration = 10.0;
98100
max_priority = INT32_MIN;
99-
controller.performer_controller().GetNextTaskEvent(duration, max_priority);
101+
controller.performer_controller().GetNextTaskEvent(min_priority, duration, max_priority);
100102
EXPECT_DOUBLE_EQ(duration, 0.5);
101103
EXPECT_EQ(max_priority, 0);
102104

103105
controller.Update(3.0);
104106

105107
duration = 10.0;
106108
max_priority = INT32_MIN;
107-
controller.performer_controller().GetNextTaskEvent(duration, max_priority);
109+
controller.performer_controller().GetNextTaskEvent(min_priority, duration, max_priority);
108110
EXPECT_DOUBLE_EQ(duration, 10.0);
109111
EXPECT_EQ(max_priority, INT32_MIN);
110112

src/engine/performer_controller.cpp

Lines changed: 48 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,11 @@ void PerformerController::Release(uint32_t performer_index) noexcept {
4848
uint32_t PerformerController::AcquireTask(uint32_t performer_index, double position,
4949
double duration, int32_t priority,
5050
BarelyTaskCallback callback, void* user_data) noexcept {
51+
assert(duration >= 0.0);
5152
const uint32_t task_index = engine_.task_pool.Acquire();
5253
if (task_index != kInvalidIndex) {
5354
TaskState& task = engine_.GetTask(task_index);
54-
task = {
55-
{callback, user_data}, position, std::max(duration, kMinTaskDuration), priority,
56-
performer_index,
57-
};
55+
task = {{callback, user_data}, position, duration, priority, performer_index};
5856
InsertInactiveTask(engine_.GetPerformer(performer_index), task_index);
5957
}
6058
return task_index;
@@ -116,17 +114,7 @@ void PerformerController::SetPosition(uint32_t performer_index, double position)
116114
}
117115
} else {
118116
performer.position = position;
119-
uint32_t task_index = performer.first_active_task_index;
120-
while (task_index != kInvalidIndex) {
121-
auto& task = engine_.GetTask(task_index);
122-
if (task.IsInside(performer.position)) {
123-
task_index = task.next_task_index;
124-
} else {
125-
SetTaskActive(performer, task_index, false);
126-
// Restart the iteration since links can get invalidated after a callback.
127-
task_index = performer.first_active_task_index;
128-
}
129-
}
117+
UpdateActiveTasks(performer);
130118
}
131119
}
132120

@@ -143,9 +131,9 @@ void PerformerController::Stop(uint32_t performer_index) noexcept {
143131
}
144132

145133
void PerformerController::SetTaskDuration(uint32_t task_index, double duration) noexcept {
134+
assert(duration >= 0.0);
146135
auto& task = engine_.GetTask(task_index);
147136
auto& performer = engine_.GetPerformer(task.performer_index);
148-
duration = std::max(duration, kMinTaskDuration);
149137
if (task.duration == duration) return;
150138
task.duration = duration;
151139
if (task.is_active) {
@@ -195,7 +183,8 @@ void PerformerController::SetTaskPriority(uint32_t task_index, int32_t priority)
195183
}
196184
}
197185

198-
void PerformerController::ProcessAllTasksAtPosition(int32_t max_priority) noexcept {
186+
void PerformerController::ProcessAllTasksAtPosition(const std::optional<int32_t>& min_priority,
187+
int32_t max_priority) noexcept {
199188
for (uint32_t i = 0; i < engine_.performer_pool.ActiveCount(); ++i) {
200189
auto& performer = engine_.GetPerformer(engine_.performer_pool.GetActive(i));
201190
if (!performer.is_playing) {
@@ -204,23 +193,16 @@ void PerformerController::ProcessAllTasksAtPosition(int32_t max_priority) noexce
204193
// Active tasks get processed in `SetPosition`, so we only need to process inactive tasks.
205194
for (uint32_t task_index = GetNextInactiveTask(performer); task_index != kInvalidIndex;
206195
task_index = GetNextInactiveTask(performer)) {
207-
const auto& task = engine_.GetTask(task_index);
208-
if (!task.IsInside(performer.position) ||
209-
(task.position >= performer.position && task.priority > max_priority)) {
196+
auto& task = engine_.GetTask(task_index);
197+
if (task.IsInside(performer.position) ||
198+
(task.position == performer.position && task.priority <= max_priority &&
199+
(task.duration > 0.0 || !min_priority.has_value() || task.priority > *min_priority))) {
200+
SetTaskActive(performer, task_index, true);
201+
} else {
210202
break;
211203
}
212-
SetTaskActive(performer, task_index, true);
213-
}
214-
}
215-
}
216-
217-
void PerformerController::Update(double duration) noexcept {
218-
assert(duration > 0.0);
219-
for (uint32_t i = 0; i < engine_.performer_pool.ActiveCount(); ++i) {
220-
const uint32_t performer_index = engine_.performer_pool.GetActive(i);
221-
if (const auto& performer = engine_.GetPerformer(performer_index); performer.is_playing) {
222-
SetPosition(performer_index, performer.position + duration);
223204
}
205+
UpdateActiveTasks(performer);
224206
}
225207
}
226208

@@ -320,6 +302,20 @@ void PerformerController::SetTaskActive(PerformerState& performer, uint32_t task
320302
}
321303
}
322304

305+
void PerformerController::UpdateActiveTasks(PerformerState& performer) noexcept {
306+
uint32_t task_index = performer.first_active_task_index;
307+
while (task_index != kInvalidIndex) {
308+
auto& task = engine_.GetTask(task_index);
309+
if (task.IsInside(performer.position)) {
310+
task_index = task.next_task_index;
311+
} else {
312+
SetTaskActive(performer, task_index, false);
313+
// Restart the iteration since links can get invalidated after a callback.
314+
task_index = performer.first_active_task_index;
315+
}
316+
}
317+
}
318+
323319
uint32_t PerformerController::GetNextInactiveTask(const PerformerState& performer) const noexcept {
324320
if (!performer.is_playing) {
325321
return kInvalidIndex;
@@ -335,8 +331,9 @@ uint32_t PerformerController::GetNextInactiveTask(const PerformerState& performe
335331
return kInvalidIndex;
336332
}
337333

338-
void PerformerController::GetNextTaskEvent(const PerformerState& performer, double& duration,
339-
int32_t& priority) const noexcept {
334+
void PerformerController::GetNextTaskEvent(const PerformerState& performer,
335+
const std::optional<int32_t>& min_priority,
336+
double& duration, int32_t& priority) const noexcept {
340337
if (!performer.is_playing) {
341338
return;
342339
}
@@ -347,30 +344,33 @@ void PerformerController::GetNextTaskEvent(const PerformerState& performer, doub
347344
uint32_t task_index = performer.first_inactive_task_index;
348345
while (task_index != kInvalidIndex) {
349346
const auto& task = engine_.GetTask(task_index);
350-
if (task.position < performer.position ||
351-
(task.position == performer.position && task.priority <= priority)) {
347+
if (task.position <= performer.position) {
352348
// If the performer position is inside an inactive task, we can return immediately.
353-
if (task.GetEndPosition() > performer.position) {
349+
if (task.GetEndPosition() > performer.position ||
350+
(task.position == performer.position &&
351+
(!min_priority.has_value() || *min_priority < task.priority))) {
352+
priority = (duration > 0.0) ? task.priority : std::min(task.priority, priority);
354353
duration = 0.0;
355-
priority = std::min(task.priority, priority);
356354
return;
357355
}
358356
if (performer.is_looping && task.position >= performer.loop_begin_position &&
359357
task.position < loop_end_position) {
360-
const double looped_inactive_duration =
361-
task.position - performer.position + performer.loop_length;
362-
if (looped_inactive_duration < duration ||
363-
(looped_inactive_duration == duration && task.priority < priority)) {
358+
if (const double looped_inactive_duration =
359+
task.position - performer.position + performer.loop_length;
360+
looped_inactive_duration < duration) {
364361
duration = looped_inactive_duration;
365362
priority = task.priority;
363+
} else if (looped_inactive_duration == duration && task.priority < priority) {
364+
priority = task.priority;
366365
}
367366
}
368367
} else {
369-
const double inactive_duration = task.position - performer.position;
370-
if (inactive_duration < duration ||
371-
(inactive_duration == duration && task.priority < priority)) {
368+
if (const double inactive_duration = task.position - performer.position;
369+
inactive_duration < duration) {
372370
duration = inactive_duration;
373371
priority = task.priority;
372+
} else if (inactive_duration == duration && task.priority < priority) {
373+
priority = task.priority;
374374
}
375375
break;
376376
}
@@ -383,11 +383,12 @@ void PerformerController::GetNextTaskEvent(const PerformerState& performer, doub
383383
const double end_position = performer.is_looping
384384
? std::min(active_task.GetEndPosition(), loop_end_position)
385385
: active_task.GetEndPosition();
386-
const double active_duration = end_position - performer.position;
387-
if (active_duration < duration ||
388-
(active_duration == duration && active_task.priority < priority)) {
386+
if (const double active_duration = end_position - performer.position;
387+
active_duration < duration) {
389388
duration = active_duration;
390389
priority = active_task.priority;
390+
} else if (active_duration == duration && active_task.priority < priority) {
391+
priority = active_task.priority;
391392
}
392393
}
393394
}

src/engine/performer_controller.h

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <barelymusician.h>
55

66
#include <cstdint>
7+
#include <optional>
78

89
#include "engine/engine_state.h"
910
#include "engine/performer_state.h"
@@ -34,13 +35,23 @@ class PerformerController {
3435
void SetTaskPosition(uint32_t task_index, double position) noexcept;
3536
void SetTaskPriority(uint32_t task_index, int32_t priority) noexcept;
3637

37-
void ProcessAllTasksAtPosition(int32_t max_priority) noexcept;
38-
void Update(double duration) noexcept;
38+
void ProcessAllTasksAtPosition(const std::optional<int32_t>& min_priority,
39+
int32_t max_priority) noexcept;
3940

40-
void GetNextTaskEvent(double& duration, int32_t& priority) const noexcept {
41+
void UpdatePosition(double duration) noexcept {
4142
for (uint32_t i = 0; i < engine_.performer_pool.ActiveCount(); ++i) {
42-
GetNextTaskEvent(engine_.GetPerformer(engine_.performer_pool.GetActive(i)), duration,
43-
priority);
43+
const uint32_t performer_index = engine_.performer_pool.GetActive(i);
44+
if (const auto& performer = engine_.GetPerformer(performer_index); performer.is_playing) {
45+
SetPosition(performer_index, performer.position + duration);
46+
}
47+
}
48+
}
49+
50+
void GetNextTaskEvent(const std::optional<int32_t>& min_priority, double& duration,
51+
int32_t& priority) const noexcept {
52+
for (uint32_t i = 0; i < engine_.performer_pool.ActiveCount(); ++i) {
53+
GetNextTaskEvent(engine_.GetPerformer(engine_.performer_pool.GetActive(i)), min_priority,
54+
duration, priority);
4455
}
4556
}
4657

@@ -49,10 +60,11 @@ class PerformerController {
4960
void InsertInactiveTask(PerformerState& performer, uint32_t task_index) noexcept;
5061
void RemoveTask(PerformerState& performer, uint32_t task_index) noexcept;
5162
void SetTaskActive(PerformerState& performer, uint32_t task_index, bool is_active) noexcept;
63+
void UpdateActiveTasks(PerformerState& performer) noexcept;
5264

5365
[[nodiscard]] uint32_t GetNextInactiveTask(const PerformerState& performer) const noexcept;
54-
void GetNextTaskEvent(const PerformerState& performer, double& duration,
55-
int32_t& priority) const noexcept;
66+
void GetNextTaskEvent(const PerformerState& performer, const std::optional<int32_t>& min_priority,
67+
double& duration, int32_t& priority) const noexcept;
5668

5769
EngineState& engine_;
5870
};

0 commit comments

Comments
 (0)