-
Notifications
You must be signed in to change notification settings - Fork 0
/
LabSoundTemplateNode.cpp
135 lines (107 loc) · 3.29 KB
/
LabSoundTemplateNode.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include "LabSoundTemplateNode.h"
#include <LabSound/core/AudioBus.h>
#include <LabSound/core/AudioContext.h>
#include <LabSound/core/AudioNodeOutput.h>
#include <LabSound/extended/AudioContextLock.h>
#include <LabSound/extended/Registry.h>
#include "concurrentqueue.h"
#include <algorithm>
#include <queue>
using namespace lab;
struct LabSoundTemplateNodeEvent
{
double when;
int id;
bool operator<(const LabSoundTemplateNodeEvent& rhs) const
{
if (when > rhs.when)
return true;
if (when < rhs.when)
return false;
return id > rhs.id;
}
};
struct LabSoundTemplateNode::Detail
{
std::priority_queue<LabSoundTemplateNodeEvent> queue;
moodycamel::ConcurrentQueue<LabSoundTemplateNodeEvent> incoming;
lab::AudioContext* ac = nullptr;
Detail() = default;
~Detail() = default;
void clearSchedules()
{
LabSoundTemplateNodeEvent s;
while (incoming.try_dequeue(s)) {}
while (!queue.empty())
queue.pop();
}
};
LabSoundTemplateNode::LabSoundTemplateNode(AudioContext& ac)
: AudioNode(ac)
, _detail(new Detail())
{
_detail->ac = ∾
addOutput(std::unique_ptr<AudioNodeOutput>(new AudioNodeOutput(this, 1)));
if (s_registered)
initialize();
}
bool LabSoundTemplateNode::s_registered = NodeRegistry::Register(LabSoundTemplateNode::static_name(),
[](AudioContext& ac)->AudioNode* { return new LabSoundTemplateNode(ac); },
[](AudioNode* n) { delete n; });
LabSoundTemplateNode::~LabSoundTemplateNode()
{
_detail->clearSchedules();
uninitialize();
delete _detail;
}
void LabSoundTemplateNode::realtimeEvent(float when, int identifier)
{
double now = _detail->ac->currentTime();
_detail->incoming.enqueue({when + now, identifier});
}
void LabSoundTemplateNode::process(ContextRenderLock &r, int bufferSize)
{
AudioBus * outputBus = output(0)->bus(r);
if (!isInitialized())
{
if (outputBus)
outputBus->zero();
_detail->clearSchedules();
return;
}
// move incoming commands to the internal schedule
{
LabSoundTemplateNodeEvent s;
while (_detail->incoming.try_dequeue(s))
{
_detail->queue.push(s);
}
}
auto& ac = *r.context();
double quantumStart = ac.currentTime();
double quantumEnd = quantumStart + (double) bufferSize / ac.sampleRate();
// any events to service now?
int events = 0;
outputBus->zero();
while (!_detail->queue.empty() && _detail->queue.top().when < quantumEnd)
{
auto& top = _detail->queue.top();
// compute the exact sample the event occurs at
int offset = (top.when < quantumStart) ? 0 : static_cast<int>((top.when - quantumStart) * ac.sampleRate());
// sanity to guard against rounding problems
if (offset > bufferSize - 1)
offset = bufferSize - 1;
// make a little popping sound
float* data = outputBus->channel(0)->mutableData();
for (int i = std::max(0, offset - 4); i < std::min(offset + 4, bufferSize); ++i)
data[i] = 1;
_detail->queue.pop();
++events;
}
if (events > 0)
outputBus->clearSilentFlag();
}
void LabSoundTemplateNode::reset(ContextRenderLock&)
{
_detail->clearSchedules();
}