& code);
23 |
24 | //////////////////////////////////
25 |
26 | #pragma mark PIPER SYNTAX
27 |
28 | struct AST : RCObj
29 | {
30 | virtual const char* TypeName() const { return "AST"; }
31 | virtual void dump(std::ostream& ost, int indent) = 0;
32 | virtual void codegen(P& code) = 0;
33 | };
34 |
35 | typedef P ASTPtr;
36 |
37 | ASTPtr parseExpr(const char*& in);
38 |
39 | void printi(std::ostream& ost, int indent, const char* fmt, ...);
40 | void prints(std::ostream& ost, const char* fmt, ...);
41 |
42 | #endif
43 |
--------------------------------------------------------------------------------
/include/Play.hpp:
--------------------------------------------------------------------------------
1 | // SAPF - Sound As Pure Form
2 | // Copyright (C) 2019 James McCartney
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License
15 | // along with this program. If not, see .
16 |
17 | #include "VM.hpp"
18 |
19 | void playWithAudioUnit(Thread& th, V& v);
20 | void recordWithAudioUnit(Thread& th, V& v, Arg filename);
21 |
22 | void stopPlaying();
23 | void stopPlayingIfDone();
24 |
25 |
--------------------------------------------------------------------------------
/include/RCObj.hpp:
--------------------------------------------------------------------------------
1 | // SAPF - Sound As Pure Form
2 | // Copyright (C) 2019 James McCartney
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License
15 | // along with this program. If not, see .
16 |
17 | #ifndef __no_web2__RCObj__
18 | #define __no_web2__RCObj__
19 |
20 | #include
21 | #include
22 | #include "rc_ptr.hpp"
23 |
24 | class RCObj
25 | {
26 | public:
27 | mutable std::atomic refcount;
28 |
29 | public:
30 | RCObj();
31 | RCObj(RCObj const&);
32 | virtual ~RCObj();
33 |
34 | void retain() const;
35 | void release();
36 | virtual void norefs();
37 |
38 | int32_t getRefcount() const { return refcount; }
39 |
40 | void negrefcount();
41 | void alreadyDead();
42 |
43 |
44 | virtual const char* TypeName() const = 0;
45 | };
46 |
47 | inline void retain(RCObj* o) { o->retain(); }
48 | inline void release(RCObj* o) { o->release(); }
49 |
50 | #endif /* defined(__no_web2__RCObj__) */
51 |
--------------------------------------------------------------------------------
/include/SoundFiles.hpp:
--------------------------------------------------------------------------------
1 | // SAPF - Sound As Pure Form
2 | // Copyright (C) 2019 James McCartney
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License
15 | // along with this program. If not, see .
16 |
17 | #ifndef __taggeddoubles__SoundFiles__
18 | #define __taggeddoubles__SoundFiles__
19 |
20 | #include "VM.hpp"
21 | #include
22 |
23 | const int kMaxSFChannels = 1024;
24 | const int kBufSize = 1024;
25 |
26 | void makeRecordingPath(Arg filename, char* path, int len);
27 |
28 | ExtAudioFileRef sfcreate(Thread& th, const char* path, int numChannels, double fileSampleRate, bool interleaved);
29 | void sfwrite(Thread& th, V& v, Arg filename, bool openIt);
30 | void sfread(Thread& th, Arg filename, int64_t offset, int64_t frames);
31 |
32 | #endif /* defined(__taggeddoubles__SoundFiles__) */
33 |
--------------------------------------------------------------------------------
/include/Spectrogram.hpp:
--------------------------------------------------------------------------------
1 | // SAPF - Sound As Pure Form
2 | // Copyright (C) 2019 James McCartney
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License
15 | // along with this program. If not, see .
16 |
17 | #ifndef taggeddoubles_Spectrogram_h
18 | #define taggeddoubles_Spectrogram_h
19 |
20 | void spectrogram(int size, double* data, int width, int log2bins, const char* path, double dBfloor);
21 |
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/include/Types.hpp:
--------------------------------------------------------------------------------
1 | // SAPF - Sound As Pure Form
2 | // Copyright (C) 2019 James McCartney
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License
15 | // along with this program. If not, see .
16 |
17 | #ifndef __boxeddoubles__Types__
18 | #define __boxeddoubles__Types__
19 |
20 | #include "Object.hpp"
21 |
22 | enum {
23 | stackEffectUnknown = -1
24 | };
25 |
26 | enum {
27 | rankUnknown = -2,
28 | rankVariable = -1
29 | };
30 |
31 | enum {
32 | shapeUnknown = -4,
33 | shapeInfinite = -3,
34 | shapeIndefinite = -2,
35 | shapeFinite = -1
36 | };
37 |
38 | class StackEffect : public Object
39 | {
40 | int mTakes;
41 | int mLeaves;
42 |
43 | StackEffect() : mTakes(stackEffectUnknown), mLeaves(stackEffectUnknown) {}
44 | StackEffect(int inTakes, int inLeaves) : mTakes(stackEffectUnknown), mLeaves(stackEffectUnknown) {}
45 | };
46 |
47 | class TypeEnvir;
48 |
49 | struct TypeShape
50 | {
51 | int mRank = rankUnknown;
52 | std::vector mShape;
53 |
54 | TypeShape() {}
55 | TypeShape(TypeShape const& that) : mRank(that.mRank), mShape(that.mShape) {}
56 | TypeShape& operator=(TypeShape const& that) { mRank = that.mRank; mShape = that.mShape; return *this; }
57 |
58 | bool unify(TypeShape const& inThat, TypeShape& outResult)
59 | {
60 | if (mRank == rankUnknown) {
61 | if (inThat.mRank == rankUnknown) {
62 | outResult = *this;
63 | return true;
64 | }
65 | outResult = inThat;
66 | return true;
67 | }
68 | if (mRank != inThat.mRank)
69 | return false;
70 | outResult = inThat;
71 | for (int i = 0; i < mRank; ++i) {
72 | int a = mShape[i];
73 | int b = inThat.mShape[i];
74 | int& c = outResult.mShape[i];
75 | if (a == shapeUnknown) {
76 | c = b;
77 | } else if (b == shapeUnknown) {
78 | c = a;
79 | } else if (a != b) {
80 | return false;
81 | } else {
82 | c = a;
83 | }
84 | }
85 | return true;
86 | }
87 |
88 | // determine shape of auto mapped result
89 | // determing shape of binary operator results
90 | // e.g. [r...] * r -> [r...]
91 | // [[r]] * [r] -> [[r]]
92 | // [r..] * [r] -> [r] maximum rank, minimum shape.
93 | // also with each operators
94 |
95 | };
96 |
97 | class Type : public Object
98 | {
99 | public:
100 | TypeShape mShape;
101 |
102 | Type() {}
103 | Type(TypeShape const& inShape) : mShape(inShape) {}
104 | virtual ~Type() {}
105 |
106 | virtual bool unify(P const& inThat, P& ioEnvir, P& outResult) = 0;
107 |
108 | virtual bool isTypeReal() const { return false; }
109 | virtual bool isTypeSignal() const { return false; }
110 | virtual bool isTypeRef() const { return false; }
111 | virtual bool isTypeFun() const { return false; }
112 | virtual bool isTypeForm() const { return false; }
113 | virtual bool isTypeTuple() const { return false; }
114 | };
115 |
116 | class TypeUnknown : public Type
117 | {
118 | const char* TypeName() const { return "TypeReal"; }
119 | virtual bool unify(P const& inThat, P& ioEnvir, P& outResult)
120 | {
121 | TypeShape shape;
122 | if (!mShape.unify(inThat->mShape, shape))
123 | return false;
124 |
125 | outResult = inThat;
126 | return true;
127 | }
128 | };
129 |
130 | class TypeVar : public Object
131 | {
132 | int32_t mID;
133 | P mType;
134 | };
135 |
136 | class TypeEnvir : public Object
137 | {
138 | std::vector> mTypeVars;
139 | };
140 |
141 | class TypeReal : public Type
142 | {
143 | public:
144 | TypeReal() {}
145 | TypeReal(TypeShape const& inShape) : Type(inShape) {}
146 |
147 |
148 | const char* TypeName() const { return "TypeReal"; }
149 |
150 | virtual bool unify(P const& inThat, P& ioEnvir, P& outResult)
151 | {
152 | TypeShape shape;
153 | if (!mShape.unify(inThat->mShape, shape))
154 | return false;
155 |
156 | if (inThat->isTypeReal() || inThat->isTypeSignal()) {
157 | outResult = new TypeReal(shape);
158 | return true;
159 | }
160 | return false;
161 | }
162 | };
163 |
164 | class TypeSignal : public Type
165 | {
166 | int mSignalShape = shapeUnknown;
167 | public:
168 |
169 | TypeSignal() {}
170 | TypeSignal(TypeShape const& inShape, int inSignalShape) : Type(inShape), mSignalShape(inSignalShape) {}
171 |
172 | const char* TypeName() const { return "TypeSignal"; }
173 |
174 | virtual bool unify(P const& inThat, P& ioEnvir, P& outResult)
175 | {
176 | TypeShape shape;
177 | if (!mShape.unify(inThat->mShape, shape))
178 | return false;
179 |
180 | if (inThat->isTypeReal()) {
181 | outResult = new TypeReal(shape);
182 | return true;
183 | }
184 | if (inThat->isTypeSignal()) {
185 | // unify signal shape
186 | outResult = new TypeSignal(shape, mSignalShape);
187 | return true;
188 | }
189 | return false;
190 | }
191 | };
192 |
193 | class TypeRef : public Type
194 | {
195 | public:
196 | P mRefType;
197 |
198 |
199 | const char* TypeName() const { return "TypeRef"; }
200 | };
201 |
202 | class TypeFun : public Type
203 | {
204 | std::vector> mInTypes;
205 | std::vector
> mOutTypes;
206 |
207 | const char* TypeName() const { return "TypeFun"; }
208 | };
209 |
210 | struct FieldType
211 | {
212 | P mLabel;
213 | P mType;
214 | };
215 |
216 | class TypeForm : public Type
217 | {
218 | std::vector mFieldTypes;
219 |
220 | const char* TypeName() const { return "TypeForm"; }
221 | };
222 |
223 | class TypeTuple : public Type
224 | {
225 | std::vector> mTypes;
226 |
227 | const char* TypeName() const { return "TypeTuple"; }
228 | };
229 |
230 | #endif /* defined(__boxeddoubles__Types__) */
231 |
--------------------------------------------------------------------------------
/include/VM.hpp:
--------------------------------------------------------------------------------
1 | // SAPF - Sound As Pure Form
2 | // Copyright (C) 2019 James McCartney
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU General Public License
15 | // along with this program. If not, see .
16 |
17 | #ifndef __VM_h__
18 | #define __VM_h__
19 |
20 | #include "Object.hpp"
21 | #include "symbol.hpp"
22 | #include "rgen.hpp"
23 | #include
24 | #include
25 | #include
26 |
27 | #define USE_LIBEDIT 1
28 |
29 | #if USE_LIBEDIT
30 | #include
31 | #endif
32 |
33 | extern pthread_mutex_t gHelpMutex;
34 |
35 | class Unlocker
36 | {
37 | pthread_mutex_t* mLock;
38 | public:
39 | Unlocker(pthread_mutex_t* inLock)
40 | {
41 | pthread_mutex_unlock(mLock);
42 | }
43 | ~Unlocker()
44 | {
45 | pthread_mutex_lock(mLock);
46 | }
47 | };
48 |
49 |
50 | class Locker
51 | {
52 | pthread_mutex_t* mLock;
53 | public:
54 | Locker(pthread_mutex_t* inLock) : mLock(inLock)
55 | {
56 | pthread_mutex_lock(mLock);
57 | }
58 | ~Locker()
59 | {
60 | pthread_mutex_unlock(mLock);
61 | }
62 | };
63 |
64 | const double kDefaultSampleRate = 96000.;
65 | const int kDefaultControlBlockSize = 128;
66 | const int kDefaultVBlockSize = 1;
67 | const int kDefaultZBlockSize = 512;
68 |
69 | struct Rate
70 | {
71 | int blockSize;
72 | double sampleRate;
73 | double nyquistRate;
74 | double invSampleRate;
75 | double invNyquistRate;
76 | double radiansPerSample;
77 | double invBlockSize;
78 | double freqLimit;
79 |
80 | Rate(Rate const& inParent, int inDiv)
81 | {
82 | set(inParent.sampleRate, inParent.blockSize, inDiv);
83 | }
84 |
85 | Rate(double inSampleRate, int inBlockSize)
86 | {
87 | set(inSampleRate, inBlockSize, 1);
88 | }
89 |
90 | Rate(const Rate& that)
91 | {
92 | blockSize = that.blockSize;
93 | sampleRate = that.sampleRate;
94 | nyquistRate = that.nyquistRate;
95 | invSampleRate = that.invSampleRate;
96 | invNyquistRate = that.invNyquistRate;
97 | radiansPerSample = that.radiansPerSample;
98 | invBlockSize = that.invBlockSize;
99 | freqLimit = that.freqLimit;
100 | }
101 |
102 | void set(double inSampleRate, int inBlockSize, int inDiv)
103 | {
104 | blockSize = inBlockSize / inDiv;
105 | sampleRate = inSampleRate / inDiv;
106 | nyquistRate = .5 * sampleRate;
107 | invSampleRate = 1. / sampleRate;
108 | invNyquistRate = 2. * invSampleRate;
109 | radiansPerSample = 2. * M_PI * invSampleRate;
110 | invBlockSize = 1. / blockSize;
111 | freqLimit = std::min(24000., nyquistRate);
112 | }
113 |
114 | bool operator==(Rate const& that)
115 | {
116 | return blockSize == that.blockSize && sampleRate == that.sampleRate;
117 | }
118 | };
119 |
120 | const size_t kStackSize = 16384;
121 |
122 | class CompileScope;
123 |
124 | const int kMaxTokenLen = 2048;
125 |
126 | enum {
127 | parsingWords,
128 | parsingString,
129 | parsingParens,
130 | parsingLambda,
131 | parsingArray,
132 | parsingEnvir
133 | };
134 |
135 | class Thread
136 | {
137 | public:
138 | size_t stackBase;
139 | size_t localBase;
140 | std::vector stack;
141 | std::vector local;
142 | P fun;
143 | P mWorkspace;
144 |
145 | P mCompileScope;
146 |
147 | Rate rate;
148 |
149 | RGen rgen;
150 |
151 | // parser
152 | FILE* parserInputFile;
153 | char token[kMaxTokenLen];
154 | int tokenLen;
155 |
156 | int parsingWhat;
157 | bool fromString;
158 |
159 | // edit line
160 | #if USE_LIBEDIT
161 | EditLine *el;
162 | History *myhistory;
163 | HistEvent ev;
164 | char historyfilename[PATH_MAX];
165 | #endif
166 | const char *line;
167 | int linelen;
168 | int linepos;
169 | const char* logfilename;
170 | time_t previousTimeStamp;
171 |
172 | Thread();
173 | Thread(const Thread& parent);
174 | Thread(const Thread& parent, P const& fun);
175 | ~Thread();
176 |
177 |
178 | const char* curline() const { return line + linepos; }
179 | void prevc() {
180 | if (linepos) --linepos;
181 | }
182 | void unget(int n) { linepos -= n; }
183 | void unget(const char* s) { linepos -= curline() - s; }
184 | char c() { return line[linepos]; }
185 | char d() { return line[linepos] ? line[linepos+1] : 0; }
186 | char getc();
187 | void getLine();
188 | void logTimestamp(FILE* logfile);
189 | void toToken(const char* s, int n) {
190 | if (n >= kMaxTokenLen) { post("token too long.\n"); throw errSyntax; }
191 | tokenLen = n;
192 | memcpy(token, s, n);
193 | token[tokenLen] = 0;
194 | }
195 | char* tok() { return token; }
196 | void clearTok() { tokenLen = 0; token[0] = 0; }
197 | void setParseString(const char* inString)
198 | {
199 | if (inString) { line = inString; fromString = true; linepos = 0; }
200 | else fromString = false;
201 | }
202 |
203 | void ApplyIfFun(V& v);
204 |
205 |
206 | V& getLocal(size_t i)
207 | {
208 | return local[localBase + i];
209 | }
210 |
211 |
212 | void popLocals()
213 | {
214 | local.erase(local.end() - fun->NumLocals(), local.end());
215 | }
216 |
217 | // stack ops
218 | void push(Arg v)
219 | {
220 | stack.push_back(v);
221 | }
222 | void push(V && v)
223 | {
224 | stack.push_back(std::move(v));
225 | }
226 |
227 | void push(O o)
228 | {
229 | stack.push_back(o);
230 | }
231 |
232 | void push(double f)
233 | {
234 | stack.push_back(f);
235 | }
236 |
237 | template
238 | void push(P const& p)
239 | {
240 | stack.push_back(p);
241 | }
242 |
243 | void tuck(size_t n, Arg v);
244 |
245 | void pushBool(bool b) { push(b ? 1. : 0.); }
246 |
247 | V pop()
248 | {
249 | if (stackDepth() == 0)
250 | throw errStackUnderflow;
251 |
252 | V v = std::move(stack.back());
253 |
254 | stack.pop_back();
255 | return v;
256 | }
257 |
258 | void popn(size_t n)
259 | {
260 | if (stackDepth() < n)
261 | throw errStackUnderflow;
262 | stack.erase(stack.end() - n, stack.end());
263 | }
264 |
265 | void clearStack()
266 | {
267 | popn(stackDepth());
268 | }
269 |
270 | V& top() {
271 | if (stackDepth() == 0)
272 | throw errStackUnderflow;
273 | return stack.back();
274 | }
275 | size_t stackDepth() const { return stack.size() - stackBase; }
276 | size_t numLocals() const { return local.size() - localBase; }
277 |
278 | void setStackBaseTo(size_t newStackBase) { stackBase = newStackBase; }
279 | void setStackBase(size_t n = 0) { stackBase = stack.size() - n; }
280 | void setLocalBase(size_t newLocalBase) { localBase = newLocalBase; }
281 | void setLocalBase() { localBase = local.size(); }
282 |
283 | bool compile(const char* inString, P& fun, bool inTopLevel);
284 |
285 | V popValue();
286 | int64_t popInt(const char* msg);
287 | double popFloat(const char* msg);
288 | #define POPTYPEDECL(TYPE) P pop##TYPE(const char* msg);
289 |
290 | POPTYPEDECL(Ref);
291 | POPTYPEDECL(ZRef);
292 | POPTYPEDECL(String);
293 | POPTYPEDECL(Fun);
294 | POPTYPEDECL(List);
295 | POPTYPEDECL(Form);
296 |
297 | V popZIn(const char* msg);
298 | V popZInList(const char* msg);
299 | P popVList(const char* msg);
300 | P popZList(const char* msg);
301 |
302 | void printStack();
303 | void printLocals();
304 |
305 | void run(Opcode* c);
306 |
307 | void repl(FILE* infile, const char* logfilename);
308 | };
309 |
310 | class VM
311 | {
312 | public:
313 | const char* prelude_file;
314 | const char* log_file;
315 |
316 | P builtins;
317 |
318 | int printLength;
319 | int printDepth;
320 | int printTotalItems;
321 |
322 | Rate ar;
323 | Rate kr;
324 |
325 | int VblockSize;
326 |
327 | // useful objects
328 | P