l@272
|
1 /***** Scope.cpp *****/
|
l@272
|
2 #include <Scope.h>
|
l@272
|
3
|
l@272
|
4 Scope::Scope():connected(0), triggerPrimed(false), started(false){}
|
l@272
|
5
|
l@272
|
6 // static aux task functions
|
l@272
|
7 void Scope::triggerTask(void *ptr){
|
l@272
|
8 Scope *instance = (Scope*)ptr;
|
l@272
|
9 if (instance->started)
|
l@272
|
10 instance->doTrigger();
|
l@272
|
11 }
|
l@272
|
12 void Scope::sendBufferTask(void *ptr){
|
l@272
|
13 Scope *instance = (Scope*)ptr;
|
l@272
|
14 instance->sendBuffer();
|
l@272
|
15 }
|
l@272
|
16
|
l@272
|
17 void Scope::setup(unsigned int _numChannels, float _sampleRate){
|
l@272
|
18
|
l@272
|
19 numChannels = _numChannels;
|
l@272
|
20 sampleRate = _sampleRate;
|
l@272
|
21
|
l@272
|
22 // setup the OSC server && client
|
l@272
|
23 // used for sending / recieving settings
|
l@272
|
24 // oscClient has it's send task turned off, as we only need to send one handshake message
|
l@272
|
25 oscServer.setup(OSC_RECIEVE_PORT);
|
l@272
|
26 oscClient.setup(OSC_SEND_PORT, "127.0.0.1", false);
|
l@272
|
27
|
l@272
|
28 // setup the udp socket
|
l@272
|
29 // used for sending raw frame data
|
l@272
|
30 socket.setServer("127.0.0.1");
|
l@272
|
31 socket.setPort(SCOPE_UDP_PORT);
|
l@272
|
32
|
l@272
|
33 // setup the auxiliary tasks
|
l@272
|
34 scopeTriggerTask = BeagleRT_createAuxiliaryTask(Scope::triggerTask, BEAGLERT_AUDIO_PRIORITY-2, "scopeTriggerTask", this, true);
|
l@272
|
35 scopeSendBufferTask = BeagleRT_createAuxiliaryTask(Scope::sendBufferTask, BEAGLERT_AUDIO_PRIORITY-1, "scopeSendBufferTask", this);
|
l@272
|
36
|
l@272
|
37 // send an OSC message to address /scope-setup
|
l@272
|
38 // then wait 1 second for a reply on /scope-setup-reply
|
l@272
|
39 bool handshakeRecieved = false;
|
l@272
|
40 oscClient.sendMessageNow(oscClient.newMessage.to("/scope-setup").add((int)numChannels).add(sampleRate).end());
|
l@272
|
41 oscServer.recieveMessageNow(1000);
|
l@272
|
42 while (oscServer.messageWaiting()){
|
l@272
|
43 if (handshakeRecieved){
|
l@272
|
44 parseMessage(oscServer.popMessage());
|
l@272
|
45 } else if (oscServer.popMessage().match("/scope-setup-reply")){
|
l@272
|
46 handshakeRecieved = true;
|
l@272
|
47 }
|
l@272
|
48 }
|
l@272
|
49
|
l@272
|
50 if (handshakeRecieved && connected)
|
l@272
|
51 start();
|
l@272
|
52
|
l@272
|
53 }
|
l@272
|
54
|
l@272
|
55 void Scope::start(){
|
l@272
|
56
|
l@272
|
57 // resize the buffers
|
l@272
|
58 channelWidth = frameWidth * FRAMES_STORED;
|
l@272
|
59 buffer.resize(numChannels*channelWidth);
|
l@272
|
60 outBuffer.resize(numChannels*frameWidth);
|
l@272
|
61
|
l@272
|
62 // reset the pointers
|
l@272
|
63 writePointer = 0;
|
l@272
|
64 readPointer = 0;
|
l@272
|
65 triggerPointer = 0;
|
l@272
|
66
|
l@272
|
67 // reset the trigger
|
l@272
|
68 triggerPrimed = true;
|
l@272
|
69 triggerCollecting = false;
|
l@272
|
70 triggerWaiting = false;
|
l@272
|
71 triggerCount = 0;
|
l@272
|
72 downSampleCount = 1;
|
l@272
|
73 autoTriggerCount = 0;
|
l@272
|
74 customTriggered = false;
|
l@272
|
75
|
l@272
|
76 started = true;
|
l@272
|
77 }
|
l@272
|
78
|
l@272
|
79 void Scope::stop(){
|
l@272
|
80 started = false;
|
l@272
|
81 }
|
l@272
|
82
|
l@272
|
83 void Scope::log(float chn1, ...){
|
l@272
|
84
|
l@272
|
85 // check for any recieved OSC messages
|
l@272
|
86 while (oscServer.messageWaiting()){
|
l@272
|
87 parseMessage(oscServer.popMessage());
|
l@272
|
88 }
|
l@272
|
89
|
l@272
|
90 if (!started) return;
|
l@272
|
91
|
l@272
|
92 if (downSampling > 1){
|
l@272
|
93 if (downSampleCount < downSampling){
|
l@272
|
94 downSampleCount++;
|
l@272
|
95 return;
|
l@272
|
96 }
|
l@272
|
97 downSampleCount = 1;
|
l@272
|
98 }
|
l@272
|
99
|
l@272
|
100 va_list args;
|
l@272
|
101 va_start (args, chn1);
|
l@272
|
102
|
l@272
|
103 int startingWritePointer = writePointer;
|
l@272
|
104
|
l@272
|
105 // save the logged samples into the buffer
|
l@272
|
106 buffer[writePointer] = chn1;
|
l@272
|
107
|
l@272
|
108 for (int i=1; i<numChannels; i++) {
|
l@272
|
109 // iterate over the function arguments, store them in the relevant part of the buffer
|
l@272
|
110 // channels are stored sequentially in the buffer i.e [[channel1], [channel2], etc...]
|
l@272
|
111 buffer[i*channelWidth + writePointer] = (float)va_arg(args, double);
|
l@272
|
112 }
|
l@272
|
113
|
l@272
|
114 writePointer = (writePointer+1)%channelWidth;
|
l@272
|
115
|
l@272
|
116 // if upSampling > 1, save repeated samples into the buffer
|
l@272
|
117 for (int j=1; j<upSampling; j++){
|
l@272
|
118
|
l@272
|
119 buffer[writePointer] = buffer[startingWritePointer];
|
l@272
|
120
|
l@272
|
121 for (int i=1; i<numChannels; i++) {
|
l@272
|
122 buffer[i*channelWidth + writePointer] = buffer[i*channelWidth + startingWritePointer];
|
l@272
|
123 }
|
l@272
|
124
|
l@272
|
125 writePointer = (writePointer+1)%channelWidth;
|
l@272
|
126
|
l@272
|
127 }
|
l@272
|
128
|
l@272
|
129 va_end (args);
|
l@272
|
130
|
l@272
|
131 }
|
l@272
|
132
|
l@272
|
133 bool Scope::trigger(){
|
l@272
|
134 if (triggerMode == 2 && !customTriggered && triggerPrimed && started){
|
l@272
|
135 customTriggerPointer = (writePointer-xOffset+channelWidth)%channelWidth;
|
l@272
|
136 customTriggered = true;
|
l@272
|
137 return true;
|
l@272
|
138 }
|
l@272
|
139 return false;
|
l@272
|
140 }
|
l@272
|
141
|
l@272
|
142 void Scope::scheduleSendBufferTask(){
|
l@272
|
143 BeagleRT_scheduleAuxiliaryTask(scopeSendBufferTask);
|
l@272
|
144 }
|
l@272
|
145
|
l@272
|
146 bool Scope::triggered(){
|
l@272
|
147 if (triggerMode == 0 || triggerMode == 1){ // normal or auto trigger
|
l@272
|
148 return (!triggerDir && buffer[channelWidth*triggerChannel+((readPointer-1+channelWidth)%channelWidth)] < triggerLevel // positive trigger direction
|
l@272
|
149 && buffer[channelWidth*triggerChannel+readPointer] >= triggerLevel) ||
|
l@272
|
150 (triggerDir && buffer[channelWidth*triggerChannel+((readPointer-1+channelWidth)%channelWidth)] > triggerLevel // negative trigger direciton
|
l@272
|
151 && buffer[channelWidth*triggerChannel+readPointer] <= triggerLevel);
|
l@272
|
152 } else if (triggerMode == 2){ // custom trigger
|
l@272
|
153 return (customTriggered && readPointer == customTriggerPointer);
|
l@272
|
154 }
|
l@272
|
155 return false;
|
l@272
|
156 }
|
l@272
|
157
|
l@272
|
158 void Scope::doTrigger(){
|
l@272
|
159 // iterate over the samples between the read and write pointers and check for / deal with triggers
|
l@272
|
160 while (readPointer != writePointer){
|
l@272
|
161
|
l@272
|
162 // if we are currently listening for a trigger
|
l@272
|
163 if (triggerPrimed){
|
l@272
|
164
|
l@272
|
165 // if we crossed the trigger threshold
|
l@272
|
166 if (triggered()){
|
l@272
|
167
|
l@272
|
168 // stop listening for a trigger
|
l@272
|
169 triggerPrimed = false;
|
l@272
|
170 triggerCollecting = true;
|
l@272
|
171
|
l@272
|
172 // save the readpointer at the trigger point
|
l@272
|
173 triggerPointer = (readPointer-xOffset+channelWidth)%channelWidth;
|
l@272
|
174
|
l@272
|
175 triggerCount = frameWidth/2 - xOffset;
|
l@272
|
176 autoTriggerCount = 0;
|
l@272
|
177
|
l@272
|
178 } else {
|
l@272
|
179 // auto triggering
|
l@272
|
180 if (triggerMode == 0 && (autoTriggerCount++ > (frameWidth+holdOff))){
|
l@272
|
181 // it's been a whole frameWidth since we've found a trigger, so auto-trigger anyway
|
l@272
|
182 triggerPrimed = false;
|
l@272
|
183 triggerCollecting = true;
|
l@272
|
184
|
l@272
|
185 // save the readpointer at the trigger point
|
l@272
|
186 triggerPointer = (readPointer-xOffset+channelWidth)%channelWidth;
|
l@272
|
187
|
l@272
|
188 triggerCount = frameWidth/2 - xOffset;
|
l@272
|
189 autoTriggerCount = 0;
|
l@272
|
190 }
|
l@272
|
191 }
|
l@272
|
192
|
l@272
|
193 } else if (triggerCollecting){
|
l@272
|
194
|
l@272
|
195 // a trigger has been detected, and we are collecting the second half of the triggered frame
|
l@272
|
196 if (--triggerCount > 0){
|
l@272
|
197
|
l@272
|
198 } else {
|
l@272
|
199 triggerCollecting = false;
|
l@272
|
200 triggerWaiting = true;
|
l@272
|
201 triggerCount = frameWidth/2 + holdOffSamples;
|
l@272
|
202
|
l@272
|
203 // copy the previous to next frameWidth/2 samples into the outBuffer
|
l@272
|
204 int startptr = (triggerPointer-frameWidth/2 + channelWidth)%channelWidth;
|
l@272
|
205 int endptr = (triggerPointer+frameWidth/2 + channelWidth)%channelWidth;
|
l@272
|
206
|
l@272
|
207 if (endptr > startptr){
|
l@272
|
208 for (int i=0; i<numChannels; i++){
|
l@272
|
209 std::copy(&buffer[channelWidth*i+startptr], &buffer[channelWidth*i+endptr], outBuffer.begin()+(i*frameWidth));
|
l@272
|
210 }
|
l@272
|
211 } else {
|
l@272
|
212 for (int i=0; i<numChannels; i++){
|
l@272
|
213 std::copy(&buffer[channelWidth*i+startptr], &buffer[channelWidth*(i+1)], outBuffer.begin()+(i*frameWidth));
|
l@272
|
214 std::copy(&buffer[channelWidth*i], &buffer[channelWidth*i+endptr], outBuffer.begin()+((i+1)*frameWidth-endptr));
|
l@272
|
215 }
|
l@272
|
216 }
|
l@272
|
217
|
l@272
|
218 // the whole frame has been saved in outBuffer, so send it
|
l@272
|
219 scheduleSendBufferTask();
|
l@272
|
220
|
l@272
|
221 }
|
l@272
|
222
|
l@272
|
223 } else if (triggerWaiting){
|
l@272
|
224
|
l@272
|
225 // a trigger has completed, so wait half a framewidth before looking for another
|
l@272
|
226 if (--triggerCount > 0){
|
l@272
|
227
|
l@272
|
228 } else {
|
l@272
|
229 triggerWaiting = false;
|
l@272
|
230 triggerPrimed = true;
|
l@272
|
231 customTriggered = false;
|
l@272
|
232 }
|
l@272
|
233
|
l@272
|
234 }
|
l@272
|
235
|
l@272
|
236 // increment the read pointer
|
l@272
|
237 readPointer = (readPointer+1)%channelWidth;
|
l@272
|
238 }
|
l@272
|
239
|
l@272
|
240 }
|
l@272
|
241
|
l@272
|
242 void Scope::sendBuffer(){
|
l@272
|
243 socket.send(&(outBuffer[0]), outBuffer.size()*sizeof(float));
|
l@272
|
244 }
|
l@272
|
245
|
l@272
|
246 void Scope::parseMessage(oscpkt::Message msg){
|
l@272
|
247 if (msg.partialMatch("/scope-settings/")){
|
l@272
|
248 int intArg;
|
l@272
|
249 float floatArg;
|
l@272
|
250 if (msg.match("/scope-settings/connected").popInt32(intArg).isOkNoMoreArgs()){
|
l@272
|
251 if (connected == 0 && intArg == 1){
|
l@272
|
252 start();
|
l@272
|
253 } else if (connected == 1 && intArg == 0){
|
l@272
|
254 stop();
|
l@272
|
255 }
|
l@272
|
256 connected = intArg;
|
l@272
|
257 } else if (msg.match("/scope-settings/frameWidth").popInt32(intArg).isOkNoMoreArgs()){
|
l@272
|
258 stop();
|
l@272
|
259 frameWidth = intArg;
|
l@272
|
260 start();
|
l@272
|
261 } else if (msg.match("/scope-settings/triggerMode").popInt32(intArg).isOkNoMoreArgs()){
|
l@272
|
262 triggerMode = intArg;
|
l@272
|
263 } else if (msg.match("/scope-settings/triggerChannel").popInt32(intArg).isOkNoMoreArgs()){
|
l@272
|
264 triggerChannel = intArg;
|
l@272
|
265 } else if (msg.match("/scope-settings/triggerDir").popInt32(intArg).isOkNoMoreArgs()){
|
l@272
|
266 triggerDir = intArg;
|
l@272
|
267 } else if (msg.match("/scope-settings/triggerLevel").popFloat(floatArg).isOkNoMoreArgs()){
|
l@272
|
268 triggerLevel = floatArg;
|
l@272
|
269 } else if (msg.match("/scope-settings/xOffset").popInt32(intArg).isOkNoMoreArgs()){
|
l@272
|
270 xOffset = intArg;
|
l@272
|
271 } else if (msg.match("/scope-settings/upSampling").popInt32(intArg).isOkNoMoreArgs()){
|
l@272
|
272 upSampling = intArg;
|
l@272
|
273 holdOffSamples = (int)(sampleRate*0.001*holdOff*upSampling/downSampling);
|
l@272
|
274 } else if (msg.match("/scope-settings/downSampling").popInt32(intArg).isOkNoMoreArgs()){
|
l@272
|
275 downSampling = intArg;
|
l@272
|
276 holdOffSamples = (int)(sampleRate*0.001*holdOff*upSampling/downSampling);
|
l@272
|
277 } else if (msg.match("/scope-settings/holdOff").popFloat(floatArg).isOkNoMoreArgs()){
|
l@272
|
278 holdOff = floatArg;
|
l@272
|
279 holdOffSamples = (int)(sampleRate*0.001*holdOff*upSampling/downSampling);
|
l@272
|
280 }
|
l@272
|
281 }
|
l@272
|
282 }
|