Loading...
Searching...
No Matches
Queue.hpp
1#pragma once
2#include <ossia/network/value/value.hpp>
3
4#include <boost/circular_buffer.hpp>
5
6#include <halp/controls.hpp>
7#include <halp/meta.hpp>
8#include <halp/value_types.hpp>
9
10#include <optional>
11#include <string_view>
12#include <utility>
13
14namespace avnd_tools
15{
16
17struct Queue
18{
19 halp_meta(name, "Buffer queue")
20 halp_meta(author, "ossia team")
21 halp_meta(category, "Control/Data Processing")
22 halp_meta(description, "Queue input messages and output them as a buffer")
23 halp_meta(c_name, "avnd_buffer_queue")
24 halp_meta(uuid, "8f68b81e-e5ba-4a10-a888-6581a5d770fe")
25 halp_meta(manual_url, "https://ossia.io/score-docs/processes/buffer-queue.html")
26
27 // The combo boxes of Mode and Data list their labels in enumerator order.
28 // When the output is sent; Bang sends in every mode.
29 // OnInput*: the ticks a message arrives, even if Lock or Clear drops it.
30 // OnChange*: the ticks the queue's contents change (a message queued, a
31 // clear); the removal done by Pop does not count.
32 // OnBangPopOldest: each output then removes the oldest value, whatever
33 // Data is (with Pop, what it sent).
34 enum OutputMode
35 {
36 EveryTick,
37 OnInput,
38 OnChange,
39 WhenFull,
40 OnInputWhenFull,
41 OnChangeWhenFull,
42 OnBang,
43 OnBangPopOldest,
44 };
45 enum OutputData
46 {
47 Oldest,
48 Newest,
49 WholeBuffer,
50 };
51 struct
52 {
53 // An event port: only the messages received this tick are queued.
54 halp::val_port<"Input", std::optional<ossia::value>> input;
55 struct : halp::spinbox_i32<"Max length", halp::range{0, 100000, 100}>
56 {
57 void update(Queue& self)
58 {
59 if(this->value < 0)
60 return;
61 self.buffer.set_capacity(this->value);
62 }
63 } length;
64 // Held: the queue is cleared and the input dropped. An impulse (a
65 // message, a cable) is a press lasting one tick: it clears once.
66 halp::maintained_button<"Clear"> clear;
67 halp::maintained_button<"Lock"> lock;
68 struct : halp::combobox_t<"Mode", OutputMode>
69 {
70 struct range
71 {
72 std::string_view values[8]{
73 "Every tick",
74 "On input",
75 "On change",
76 "When full",
77 "On input, when full",
78 "On change, when full",
79 "On bang",
80 "On bang, pop oldest"};
81 OutputMode init{EveryTick};
82 };
83 } mode;
84 struct : halp::combobox_t<"Data", OutputData>
85 {
86 struct range
87 {
88 std::string_view values[3]{"Oldest", "Newest", "Whole buffer"};
89 OutputData init{Oldest};
90 };
91 } data;
92 // Like Counter's "Output": sends the output now, whatever the mode.
93 struct : halp::impulse_button<"Bang">
94 {
95 void update(Queue& self) { self.banged = true; }
96 } bang;
97 // Each output removes what it sent: the oldest value, the newest, or the
98 // whole queue (with WhenFull or OnChangeWhenFull: chunks of Max length).
99 halp::toggle<"Pop"> pop;
100 } inputs;
101
102 struct
103 {
104 halp::val_port<"Output", std::optional<ossia::value>> output;
105 } outputs;
106
107 boost::circular_buffer<ossia::value> buffer;
108 bool banged{};
109
110 bool triggered(bool received, bool changed) const noexcept
111 {
112 const bool full = buffer.full();
113 switch(inputs.mode)
114 {
115 case OutputMode::EveryTick:
116 return true;
117 case OutputMode::OnInput:
118 return received;
119 case OutputMode::OnChange:
120 return changed;
121 case OutputMode::WhenFull:
122 return full;
123 case OutputMode::OnInputWhenFull:
124 return received && full;
125 case OutputMode::OnChangeWhenFull:
126 return changed && full;
127 case OutputMode::OnBang:
128 case OutputMode::OnBangPopOldest:
129 return false;
130 }
131 return false;
132 }
133
134 void send()
135 {
136 if(buffer.empty())
137 {
138 if(inputs.data == OutputData::WholeBuffer)
139 outputs.output.value = std::vector<ossia::value>{};
140 else
141 outputs.output.value = ossia::value{};
142 return;
143 }
144
145 switch(inputs.data)
146 {
147 case OutputData::Oldest:
148 outputs.output.value = buffer.front();
149 if(inputs.pop || inputs.mode == OutputMode::OnBangPopOldest)
150 buffer.pop_front();
151 break;
152 case OutputData::WholeBuffer:
153 outputs.output.value = std::vector<ossia::value>(buffer.begin(), buffer.end());
154 if(inputs.pop)
155 buffer.clear();
156 else if(inputs.mode == OutputMode::OnBangPopOldest)
157 buffer.pop_front();
158 break;
159 case OutputData::Newest:
160 outputs.output.value = buffer.back();
161 if(inputs.pop)
162 buffer.pop_back();
163 else if(inputs.mode == OutputMode::OnBangPopOldest)
164 buffer.pop_front();
165 break;
166 }
167 }
168
169 void operator()()
170 {
171 const bool bang = std::exchange(banged, false);
172 outputs.output.value.reset();
173
174 const bool received = bool(inputs.input.value);
175 bool changed = false;
176 if(received)
177 {
178 if(!inputs.lock && !inputs.clear && buffer.capacity() > 0)
179 {
180 buffer.push_back(std::move(*inputs.input.value));
181 changed = true;
182 }
183 inputs.input.value.reset();
184 }
185
186 if(inputs.clear && !buffer.empty())
187 {
188 buffer.clear();
189 changed = true;
190 }
191
192 if(bang || triggered(received, changed))
193 send();
194 }
195};
196}
Definition Queue.hpp:18
Definition MIDISync.hpp:149