Skip to content

Commit 9de873d

Browse files
committed
PureData
1 parent e2d0304 commit 9de873d

38 files changed

+4950
-16
lines changed

examples/examples-pd/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Pure Data (or just "Pd") is an open source visual programming language for multimedia. Further information can be found in the [Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki/Pure-Data)
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
/**
2+
* Copyright (c) 2014-2018 Enzien Audio Ltd.
3+
*
4+
* Permission to use, copy, modify, and/or distribute this software for any
5+
* purpose with or without fee is hereby granted, provided that the above
6+
* copyright notice and this permission notice appear in all copies.
7+
*
8+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9+
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10+
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11+
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12+
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13+
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14+
* PERFORMANCE OF THIS SOFTWARE.
15+
*/
16+
17+
#include "HeavyContext.hpp"
18+
#include "HvTable.h"
19+
20+
void defaultSendHook(HeavyContextInterface *context,
21+
const char *sendName, hv_uint32_t sendHash, const HvMessage *msg) {
22+
HeavyContext *thisContext = reinterpret_cast<HeavyContext *>(context);
23+
const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(msg) - sizeof(HvMessage);
24+
ReceiverMessagePair *p = reinterpret_cast<ReceiverMessagePair *>(hLp_getWriteBuffer(&thisContext->outQueue, numBytes));
25+
if (p != nullptr) {
26+
p->receiverHash = sendHash;
27+
msg_copyToBuffer(msg, (char *) &p->msg, msg_getSize(msg));
28+
hLp_produce(&thisContext->outQueue, numBytes);
29+
} else {
30+
hv_assert(false &&
31+
"::defaultSendHook - The out message queue is full and cannot accept more messages until they "
32+
"have been processed. Try increasing the outQueueKb size in the new_with_options() constructor.");
33+
}
34+
}
35+
36+
HeavyContext::HeavyContext(double sampleRate, int poolKb, int inQueueKb, int outQueueKb) :
37+
sampleRate(sampleRate) {
38+
39+
hv_assert(sampleRate > 0.0); // sample rate must be positive
40+
hv_assert(poolKb > 0);
41+
hv_assert(inQueueKb > 0);
42+
hv_assert(outQueueKb >= 0);
43+
44+
blockStartTimestamp = 0;
45+
printHook = nullptr;
46+
userData = nullptr;
47+
48+
// if outQueueKb is positive, then the outQueue is allocated and the default sendhook is set.
49+
// Otherwise outQueue and the sendhook are set to NULL.
50+
sendHook = (outQueueKb > 0) ? &defaultSendHook : nullptr;
51+
52+
HV_SPINLOCK_RELEASE(inQueueLock);
53+
HV_SPINLOCK_RELEASE(outQueueLock);
54+
55+
numBytes = sizeof(HeavyContext);
56+
57+
numBytes += mq_initWithPoolSize(&mq, poolKb);
58+
numBytes += hLp_init(&inQueue, inQueueKb * 1024);
59+
numBytes += hLp_init(&outQueue, outQueueKb * 1024); // outQueueKb value of 0 sets everything to NULL
60+
}
61+
62+
HeavyContext::~HeavyContext() {
63+
mq_free(&mq);
64+
hLp_free(&inQueue);
65+
hLp_free(&outQueue);
66+
}
67+
68+
bool HeavyContext::sendBangToReceiver(hv_uint32_t receiverHash) {
69+
HvMessage *m = HV_MESSAGE_ON_STACK(1);
70+
msg_initWithBang(m, 0);
71+
bool success = sendMessageToReceiver(receiverHash, 0.0, m);
72+
return success;
73+
}
74+
75+
bool HeavyContext::sendFloatToReceiver(hv_uint32_t receiverHash, float f) {
76+
HvMessage *m = HV_MESSAGE_ON_STACK(1);
77+
msg_initWithFloat(m, 0, f);
78+
bool success = sendMessageToReceiver(receiverHash, 0.0, m);
79+
return success;
80+
}
81+
82+
bool HeavyContext::sendSymbolToReceiver(hv_uint32_t receiverHash, const char *s) {
83+
hv_assert(s != nullptr);
84+
HvMessage *m = HV_MESSAGE_ON_STACK(1);
85+
msg_initWithSymbol(m, 0, (char *) s);
86+
bool success = sendMessageToReceiver(receiverHash, 0.0, m);
87+
return success;
88+
}
89+
90+
bool HeavyContext::sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *format, ...) {
91+
hv_assert(delayMs >= 0.0);
92+
hv_assert(format != nullptr);
93+
94+
va_list ap;
95+
va_start(ap, format);
96+
const int numElem = (int) hv_strlen(format);
97+
HvMessage *m = HV_MESSAGE_ON_STACK(numElem);
98+
msg_init(m, numElem, blockStartTimestamp + (hv_uint32_t) (hv_max_d(0.0, delayMs)*getSampleRate()/1000.0));
99+
for (int i = 0; i < numElem; i++) {
100+
switch (format[i]) {
101+
case 'b': msg_setBang(m, i); break;
102+
case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break;
103+
case 'h': msg_setHash(m, i, (int) va_arg(ap, int)); break;
104+
case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break;
105+
default: break;
106+
}
107+
}
108+
va_end(ap);
109+
110+
bool success = sendMessageToReceiver(receiverHash, delayMs, m);
111+
return success;
112+
}
113+
114+
bool HeavyContext::sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) {
115+
hv_assert(delayMs >= 0.0);
116+
hv_assert(m != nullptr);
117+
118+
const hv_uint32_t timestamp = blockStartTimestamp +
119+
(hv_uint32_t) (hv_max_d(0.0, delayMs)*(getSampleRate()/1000.0));
120+
121+
ReceiverMessagePair *p = nullptr;
122+
HV_SPINLOCK_ACQUIRE(inQueueLock);
123+
const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(m) - sizeof(HvMessage);
124+
p = (ReceiverMessagePair *) hLp_getWriteBuffer(&inQueue, numBytes);
125+
if (p != nullptr) {
126+
p->receiverHash = receiverHash;
127+
msg_copyToBuffer(m, (char *) &p->msg, msg_getSize(m));
128+
msg_setTimestamp(&p->msg, timestamp);
129+
hLp_produce(&inQueue, numBytes);
130+
} else {
131+
hv_assert(false &&
132+
"::sendMessageToReceiver - The input message queue is full and cannot accept more messages until they "
133+
"have been processed. Try increasing the inQueueKb size in the new_with_options() constructor.");
134+
}
135+
HV_SPINLOCK_RELEASE(inQueueLock);
136+
return (p != nullptr);
137+
}
138+
139+
bool HeavyContext::cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) {
140+
return mq_removeMessage(&mq, m, sendMessage);
141+
}
142+
143+
HvMessage *HeavyContext::scheduleMessageForObject(const HvMessage *m,
144+
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
145+
int letIndex) {
146+
HvMessage *n = mq_addMessageByTimestamp(&mq, m, letIndex, sendMessage);
147+
return n;
148+
}
149+
150+
float *HeavyContext::getBufferForTable(hv_uint32_t tableHash) {
151+
HvTable *t = getTableForHash(tableHash);
152+
if (t != nullptr) {
153+
return hTable_getBuffer(t);
154+
} else return nullptr;
155+
}
156+
157+
int HeavyContext::getLengthForTable(hv_uint32_t tableHash) {
158+
HvTable *t = getTableForHash(tableHash);
159+
if (t != nullptr) {
160+
return hTable_getLength(t);
161+
} else return 0;
162+
}
163+
164+
bool HeavyContext::setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) {
165+
HvTable *t = getTableForHash(tableHash);
166+
if (t != nullptr) {
167+
hTable_resize(t, newSampleLength);
168+
return true;
169+
} else return false;
170+
}
171+
172+
void HeavyContext::lockAcquire() {
173+
HV_SPINLOCK_ACQUIRE(inQueueLock);
174+
}
175+
176+
bool HeavyContext::lockTry() {
177+
HV_SPINLOCK_TRY(inQueueLock);
178+
}
179+
180+
void HeavyContext::lockRelease() {
181+
HV_SPINLOCK_RELEASE(inQueueLock);
182+
}
183+
184+
void HeavyContext::setInputMessageQueueSize(int inQueueKb) {
185+
hv_assert(inQueueKb > 0);
186+
hLp_free(&inQueue);
187+
hLp_init(&inQueue, inQueueKb*1024);
188+
}
189+
190+
void HeavyContext::setOutputMessageQueueSize(int outQueueKb) {
191+
hv_assert(outQueueKb > 0);
192+
hLp_free(&outQueue);
193+
hLp_init(&outQueue, outQueueKb*1024);
194+
}
195+
196+
bool HeavyContext::getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLengthBytes) {
197+
*destinationHash = 0;
198+
ReceiverMessagePair *p = nullptr;
199+
hv_assert((sendHook == &defaultSendHook) &&
200+
"::getNextSentMessage - this function won't do anything if the msg outQueue "
201+
"size is 0, or you've overriden the default sendhook.");
202+
if (sendHook == &defaultSendHook) {
203+
HV_SPINLOCK_ACQUIRE(outQueueLock);
204+
if (hLp_hasData(&outQueue)) {
205+
hv_uint32_t numBytes = 0;
206+
p = reinterpret_cast<ReceiverMessagePair *>(hLp_getReadBuffer(&outQueue, &numBytes));
207+
hv_assert((p != nullptr) && "::getNextSentMessage - something bad happened.");
208+
hv_assert(numBytes >= sizeof(ReceiverMessagePair));
209+
hv_assert((numBytes <= msgLengthBytes) &&
210+
"::getNextSentMessage - the sent message is bigger than the message "
211+
"passed to handle it.");
212+
*destinationHash = p->receiverHash;
213+
hv_memcpy(outMsg, &p->msg, numBytes);
214+
hLp_consume(&outQueue);
215+
}
216+
HV_SPINLOCK_RELEASE(outQueueLock);
217+
}
218+
return (p != nullptr);
219+
}
220+
221+
hv_uint32_t HeavyContext::getHashForString(const char *str) {
222+
return hv_string_to_hash(str);
223+
}
224+
225+
HvTable *_hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) {
226+
hv_assert(c != nullptr);
227+
return reinterpret_cast<HeavyContext *>(c)->getTableForHash(tableHash);
228+
}
229+
230+
void _hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) {
231+
hv_assert(c != nullptr);
232+
reinterpret_cast<HeavyContext *>(c)->scheduleMessageForReceiver(receiverHash, m);
233+
}
234+
235+
HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m,
236+
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
237+
int letIndex) {
238+
hv_assert(c != nullptr);
239+
HvMessage *n = reinterpret_cast<HeavyContext *>(c)->scheduleMessageForObject(
240+
m, sendMessage, letIndex);
241+
return n;
242+
}
243+
244+
#ifdef __cplusplus
245+
extern "C" {
246+
#endif
247+
248+
HvTable *hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) {
249+
return _hv_table_get(c, tableHash);
250+
}
251+
252+
void hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) {
253+
_hv_scheduleMessageForReceiver(c, receiverHash, m);
254+
}
255+
256+
HvMessage *hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m,
257+
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
258+
int letIndex) {
259+
return _hv_scheduleMessageForObject(c, m, sendMessage, letIndex);
260+
}
261+
262+
#ifdef __cplusplus
263+
}
264+
#endif
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* Copyright (c) 2014-2018 Enzien Audio Ltd.
3+
*
4+
* Permission to use, copy, modify, and/or distribute this software for any
5+
* purpose with or without fee is hereby granted, provided that the above
6+
* copyright notice and this permission notice appear in all copies.
7+
*
8+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9+
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10+
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11+
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12+
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13+
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14+
* PERFORMANCE OF THIS SOFTWARE.
15+
*/
16+
17+
#ifndef _HEAVY_CONTEXT_H_
18+
#define _HEAVY_CONTEXT_H_
19+
20+
#include "HeavyContextInterface.hpp"
21+
#include "HvLightPipe.h"
22+
#include "HvMessageQueue.h"
23+
#include "HvMath.h"
24+
25+
struct HvTable;
26+
27+
class HeavyContext : public HeavyContextInterface {
28+
29+
public:
30+
HeavyContext(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0);
31+
virtual ~HeavyContext();
32+
33+
int getSize() override { return (int) numBytes; }
34+
35+
double getSampleRate() override { return sampleRate; }
36+
37+
hv_uint32_t getCurrentSample() override { return blockStartTimestamp; }
38+
float samplesToMilliseconds(hv_uint32_t numSamples) override { return (float) (1000.0*numSamples/sampleRate); }
39+
hv_uint32_t millisecondsToSamples(float ms) override { return (hv_uint32_t) (hv_max_f(0.0f,ms)*sampleRate/1000.0); }
40+
41+
void setUserData(void *x) override { userData = x; }
42+
void *getUserData() override { return userData; }
43+
44+
// hook management
45+
void setSendHook(HvSendHook_t *f) override { sendHook = f; }
46+
HvSendHook_t *getSendHook() override { return sendHook; }
47+
48+
void setPrintHook(HvPrintHook_t *f) override { printHook = f; }
49+
HvPrintHook_t *getPrintHook() override { return printHook; }
50+
51+
// message scheduling
52+
bool sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) override;
53+
bool sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *fmt, ...) override;
54+
bool sendFloatToReceiver(hv_uint32_t receiverHash, float f) override;
55+
bool sendBangToReceiver(hv_uint32_t receiverHash) override;
56+
bool sendSymbolToReceiver(hv_uint32_t receiverHash, const char *symbol) override;
57+
bool cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) override;
58+
59+
// table manipulation
60+
float *getBufferForTable(hv_uint32_t tableHash) override;
61+
int getLengthForTable(hv_uint32_t tableHash) override;
62+
bool setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) override;
63+
64+
// lock control
65+
void lockAcquire() override;
66+
bool lockTry() override;
67+
void lockRelease() override;
68+
69+
// message queue management
70+
void setInputMessageQueueSize(int inQueueKb) override;
71+
void setOutputMessageQueueSize(int outQueueKb) override;
72+
bool getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLength) override;
73+
74+
// utility functions
75+
static hv_uint32_t getHashForString(const char *str);
76+
77+
protected:
78+
virtual HvTable *getTableForHash(hv_uint32_t tableHash) = 0;
79+
friend HvTable *_hv_table_get(HeavyContextInterface *, hv_uint32_t);
80+
81+
virtual void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) = 0;
82+
friend void _hv_scheduleMessageForReceiver(HeavyContextInterface *, hv_uint32_t, HvMessage *);
83+
84+
HvMessage *scheduleMessageForObject(const HvMessage *,
85+
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
86+
int);
87+
friend HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *, const HvMessage *,
88+
void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *),
89+
int);
90+
91+
friend void defaultSendHook(HeavyContextInterface *, const char *, hv_uint32_t, const HvMessage *);
92+
93+
// object state
94+
double sampleRate;
95+
hv_uint32_t blockStartTimestamp;
96+
hv_size_t numBytes;
97+
HvMessageQueue mq;
98+
HvSendHook_t *sendHook;
99+
HvPrintHook_t *printHook;
100+
void *userData;
101+
HvLightPipe inQueue;
102+
HvLightPipe outQueue;
103+
hv_atomic_bool inQueueLock;
104+
hv_atomic_bool outQueueLock;
105+
};
106+
107+
#endif // _HEAVY_CONTEXT_H_

0 commit comments

Comments
 (0)