-
Notifications
You must be signed in to change notification settings - Fork 0
/
ybot_audio_stream.xtm
499 lines (436 loc) · 22.1 KB
/
ybot_audio_stream.xtm
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
;; Include guard
(if (and (defined? '*xtmlib-ybot-audio-stream-loaded*) *xtmlib-ybot-audio-stream-loaded*)
(sys:load-escape "ybot_audio_stream library already loaded"))
(define *xtmlib-ybot-audio-stream-loaded* #f)
(define *ybot-lib-dir* "/Users/s2805534/Dropbox/ybot/src/extempore_libs")
(if (not (defined? '*ybot-lib-dir*))
(sys:load-escape "Set the *ybot-lib-dir* variable before loading this library"))
(sys:load (string-append *ybot-lib-dir* "/samplerate.xtm"))
;; Dependencies
(sys:load "libs/aot-cache/audio_dsp.xtm")
(sys:load "libs/aot-cache/sndfile.xtm")
(sys:load "libs/core/xthread.xtm")
(bind-alias mutex_t i8*)
(bind-func ybot_trylock:[bool,mutex_t]*
(lambda (mutex)
(= 0 (i32toi64 (mutex_trylock mutex)))))
;; An audio source is a closure that returns a window of frames on request
;; params: startFrame numFrames numChannels outBuffer
;; returns: numFrames written to output (should equal numFrames unless there is a problem, or end of source)
(bind-alias YbotAudioSource_t [i64,i64,i64,i64,SAMPLE*]*)
(bind-func silent_source:YbotAudioSource_t
(lambda (start:i64 frames:i64 channels:i64 output:SAMPLE*)
(let ((f:i64 0) (c:i64 0))
(dotimes (f frames)
(dotimes (c channels)
(pset! output (+ (* f channels) c) (convert 0.0))))
frames)))
;;;;;;;;;;;;;;;;;;;;;;
;; YbotAudioSourceBuffer type ;;
;;;;;;;;;;;;;;;;;;;;;;
;; 0 SAMPLE* (data)
;; 1 number of frames in buffer
;; 2 channels
;; 3 samplerate
;; 4 which frame of the parent audio source corresponds to the start of the buffer
;; 5 current playhead frame (relative to start of buffer)
;; 6 current writehead frame (relative to the start of buffer)
;; 7 the AudioSource from which it draws
;; 8 an integer ID
;; 9 A Mutex for thread synchronisation
;; 10 last_frame of source (or -1 if unknown)
(bind-type YbotAudioSourceBuffer <SAMPLE*,i64,i64,SAMPLE,i64,i64,i64,YbotAudioSource_t,i64,mutex_t*,i64>)
(bind-func YbotAudioSourceBuffer_c:[YbotAudioSourceBuffer*,YbotAudioSource_t,i64,i64,SAMPLE]*
(lambda (source frames channels rate)
(let ((output:YbotAudioSourceBuffer* (zalloc))
(mutex_ptr:mutex_t* (zalloc))
(data:SAMPLE* (halloc (* frames channels))))
(pset! mutex_ptr 0 (mutex_create))
(tset! output 0 data)
(tset! output 1 frames)
(tset! output 2 channels)
(tset! output 3 rate)
(tset! output 7 source)
(tset! output 9 mutex_ptr)
(tset! output 10 -1)
output)))
(bind-func YbotAudioSourceBuffer_d:[void,YbotAudioSourceBuffer*]*
(lambda (buffer)
(mutex_destroy (pref (tref buffer 9) 0))
(free (tref buffer 0))
void))
(bind-func YASB_lock:[void,YbotAudioSourceBuffer*]*
(lambda (buffer)
(mutex_lock (pref (tref buffer 9) 0))
void))
(bind-func YASB_unlock:[void,YbotAudioSourceBuffer*]*
(lambda (buffer)
(mutex_unlock (pref (tref buffer 9) 0))
void))
(bind-func YASB_trylock:[bool,YbotAudioSourceBuffer*]*
(lambda (buffer)
(= 0 (i32toi64 (mutex_trylock (pref (tref buffer 9) 0))))))
(bind-func YASB_ready:[bool,YbotAudioSourceBuffer*]*
(lambda (buffer)
(if (YASB_trylock buffer) (begin (YASB_unlock buffer) #t) #f)))
(bind-func YASB_load_from_offset:[bool,YbotAudioSourceBuffer*,i64]*
(lambda (buffer start_frame)
(YASB_lock buffer)
(printf "Loading into buffer %p from offset %lld on thread %p\n" buffer start_frame (thread_self))
(let*
((data (tref buffer 0))
(frames (tref buffer 1))
(channels (tref buffer 2))
(source (tref buffer 7))
(frames_loaded (source start_frame frames channels data)))
(tset! buffer 4 start_frame)
(tset! buffer 5 start_frame)
(if (< frames_loaded frames) (tset! buffer 10 (+ start_frame frames_loaded)))
(YASB_unlock buffer)
(= frames_loaded frames))))
(bind-val YASB_not_ready i64 -3)
(bind-val YASB_buffer_too_small i64 -2)
(bind-val YASB_before i64 -1)
(bind-val YASB_well_inside i64 0)
(bind-val YASB_near_end i64 1)
(bind-val YASB_just_past i64 2)
(bind-val YASB_well_past i64 3)
(bind-func YASB_contains:[i64,YbotAudioSourceBuffer*,i64,i64]*
(lambda (buffer windowStart windowSize)
;(printf "\n\n~~~\nTesting if buffer %p contains window from %lld of size %lld\n" buffer windowStart windowSize)
(if (YASB_trylock buffer)
(let* ((bufStart:i64 (tref buffer 4))
(bufSize:i64 (tref buffer 1))
(bufEnd:i64 (+ bufStart bufSize))
(windowEnd:i64 (+ windowStart windowSize))
(output:i64 0))
(set! output
(cond
((< (/ bufSize 2) windowSize) YASB_buffer_too_small)
((< windowStart bufStart) YASB_before)
((< windowEnd (+ bufStart (/ bufSize 2))) YASB_well_inside)
((< windowEnd bufEnd) YASB_near_end)
((< windowEnd (+ bufEnd (/ bufSize 2))) YASB_just_past)
(else YASB_well_past)))
(YASB_unlock buffer)
;(printf "Answer: %lld\n~~~\n" output)
output)
(begin
;(printout "buffer is locked\n~~~\n")
YASB_not_ready))))
(bind-func YASB_fetch_window:[bool,YbotAudioSourceBuffer*,i64,i64,SAMPLE*]*
(lambda (buffer windowStart windowSize output)
(if (YASB_trylock buffer)
(let*
((data (tref buffer 0))
(bufFrames (tref buffer 1))
(channels (tref buffer 2))
(bufStart (tref buffer 4))
(bufEnd (+ bufStart bufFrames))
(windowEnd (+ windowStart windowSize))
(offset (- windowStart bufStart))
(i:i64 0) (c:i64 0))
(cond
((and (<= bufStart windowStart) (< windowEnd bufEnd))
(dotimes (i windowSize)
(dotimes (c channels)
(pset! output (+ c (* i channels)) (pref data (+ c (* (+ offset i) channels))))))
(YASB_unlock buffer) #t)
(else
(YASB_unlock buffer)
;(printf "For buffer %p Requested window starting at %lld and length %lld is out of range\n" buffer startFrame windowSize)
#f)))
(begin
;(println "Buffer is locked!")
#f))))
(bind-func YASB_fetch_sample:[bool,YbotAudioSourceBuffer*,i64,i64,SAMPLE*]*
(lambda (buffer frame channel output)
(if (YASB_trylock buffer)
(let*
((data (tref buffer 0))
(bufFrames (tref buffer 1))
(channels (tref buffer 2))
(bufStart (tref buffer 4))
(bufEnd (+ bufStart bufFrames))
(offset (- frame bufStart))
(i:i64 0) (c:i64 0))
(cond
((and (<= bufStart frame) (< frame bufEnd))
(pset! output 0 (pref data (+ channel (* offset channels))))
(YASB_unlock buffer) #t)
(else
(YASB_unlock buffer)
;(printf "For buffer %p Requested window starting at %lld and length %lld is out of range\n" buffer startFrame windowSize)
#f)))
(begin
;(println "Buffer is locked!")
#f))))
;; (bind-func sauce
;; (lambda (time:i64 size:i64 channels:i64 output:SAMPLE*)
;; (let ((f:i64 0) (c:i64 0))
;; (dotimes (f size)
;; (pset! output (* f channels) (convert 0.0))
;; (pset! output (+ (* f channels) 1) (convert 0.0)))
;; #t)))
;; (bind-func chutney
;; (lambda (time:i64 size:i64 channels:i64 output:SAMPLE*)
;; (let ((f:i64 0) (c:i64 0))
;; (dotimes (f size)
;; (pset! output (* f channels) (dtof (* 0.1 (random))))
;; (pset! output (+ (* f channels) 1) (dtof (* 0.1 (random)))))
;; #t)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Sample Rate Conversion
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; State data to pass into SRC provider callback
;; source, offset, block_size, channels, error
;; (bind-type YbotSRC_data <YbotAudioSource_t,i64,i64,i64,i32*>)
;; (bind-func print:[void,YbotSRC_data*]*
;; (lambda (data)
;; (printf "------------------------------\nConverter data at address %p
;; audio source: \t\t%p
;; offset: \t\t%lld
;; block_size: \t\t%lld
;; channels: \t\t%lld
;; error: \t\t%p\t\t%lld\n"
;; (tref data 0) (tref data 1) (tref data 2) (tref data 3) (tref data 4) (pref (tref data 4) 0))
;; void))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; YbotAudioStream type
;;
;; 0 the audio source
;; 1 channels
;; 2 nominal sample rate
;; 3 read_head
;; 4 block_size (for sample rate conversion)
;; 5 error code (for sample rate conversion)
;; 6 conversion buffer (for sample rate conversion)
;; 7 EOS?
(bind-type YbotAudioStream <YbotAudioSource_t,i64,SAMPLE,i64,i64,i32*,SAMPLE*,bool>)
;; Global stateless provider function
(bind-func YbotSRC_provider:src_callback_t
(lambda (userData:i8* pBuf:float**)
;(println "Calling global provider function")
(let* ((stream:YbotAudioStream* (cast userData YbotAudioStream*))
(source (tref stream 0))
(channels (tref stream 1))
(read_head (tref stream 3))
(block_size (tref stream 4))
(conversion_buffer (tref stream 6))
(eos (tref stream 7)))
;(printf "---- provider ----\n readhead: %lld\n size: %lld\n channels %lld\n into buffer %p\n" read_head block_size channels conversion_buffer)
;(print stream)
;(println "\n-------------------------")
(let ((frames_read:i64 (source read_head block_size channels conversion_buffer)))
;(println "Read in " frames_read " frames")
(tset! stream 3 (+ read_head frames_read))
(pset! pBuf 0 conversion_buffer)
;(println "Finished providing data")
frames_read))))
;; (bind-func YbotSRC_provider:src_callback_t
;; (lambda (userData:i8* pBuf:float**)
;; (println "Dummy provider function")
;; (let ((i:i64 0))
;; (dotimes (i 32)
;; (pset! (pref pBuf 0) i (dtof 0.0))))
;; (println "Finished copying dummy provider data")
;; 32))
(bind-func YbotAudioConvertedStream_c:[YbotAudioStream*,YbotAudioStream*,SAMPLE]*
(lambda (input_stream output_rate)
(let* ((input_source:YbotAudioSource_t (tref input_stream 0))
(input_channels:i64 (tref input_stream 1))
(output_channels:i64 input_channels)
(input_rate:SAMPLE (tref input_stream 2))
(input_block_size:i64 (tref input_stream 4))
(output_block_size:i64 input_block_size)
(input_conversion_buffer:SAMPLE* (tref input_stream 6))
(output_conversion_buffer:SAMPLE* (zalloc (* output_block_size output_channels)))
(input_err:i32* (tref input_stream 5))
(output_err:i32* (zalloc)))
(pset! output_err 0 (i64toi32 0))
(let* ((output_stream (YbotAudioStream null output_channels output_rate 0 output_block_size output_err output_conversion_buffer #f))
(converter:SRC_STATE*
(src_callback_new (cast (get_native_fptr YbotSRC_provider) src_callback_t)
SRC_SINC_BEST_QUALITY (i64toi32 input_channels) input_err (cast input_stream i8*)))
(output_source:YbotAudioSource_t
(lambda (windowStart:i64 windowSize:i64 channels:i64 output:SAMPLE*)
;(println "About to call src_callback_read with rate: " (/ (ftod output_rate) (ftod input_rate)) " size: " windowSize " output: " output)
(let ((frames_converted:i64
(src_callback_read converter (/ (ftod output_rate) (ftod input_rate)) windowSize output)))
;(println "Finished converting " frames_converted " frames")
frames_converted))))
(tset! output_stream 0 output_source)
;(println "\nOutput stream: " output_stream)
output_stream))))
;; (bind-func YbotAudioConvertedStream_c:[YbotAudioStream*,YbotAudioStream*,SAMPLE]*
;; (lambda (input_stream output_rate)
;; (let* ((input_source:YbotAudioSource_t (tref input_stream 0))
;; (input_channels:i64 (tref input_stream 1))
;; (output_channels:i64 input_channels)
;; (input_rate (tref input_stream 2))
;; (converter_block_size 32)
;; (converter_err:i32* (zalloc)))
;; (pset! converter_err 0 (i64toi32 0))
;; (let* ((output_stream (YbotAudioStream null output_channels output_rate 0 converter_block_size converter_err))
;; (converter:SRC_STATE*
;; (src_callback_new (cast (get_native_fptr YbotSRC_provider) src_callback_t)
;; SRC_SINC_BEST_QUALITY (i64toi32 input_channels) converter_err (convert 0)))
;; (output_source:YbotAudioSource_t
;; (lambda (windowStart:i64 windowSize:i64 channels:i64 output:SAMPLE*)
;; (println "About to call src_callback_read with rate: " (/ (ftod output_rate) (ftod input_rate)) " size: " windowSize " output: " output)
;; (let ((frames_converted:i64
;; (src_callback_read converter (/ (ftod output_rate) (ftod input_rate)) windowSize output)))
;; (println "Finished converting " frames_converted " frames")
;; frames_converted))))
;; (tset! output_stream 0 output_source)
;; (println "\nOutput stream: " output_stream)
;; output_stream))))
(bind-func YbotAudioBufferedStream_c:[YbotAudioStream*,YbotAudioSource_t,i64,i64,SAMPLE]*
(lambda (source bufferSize outputChannels nominalSampleRate)
(let* ((buffer1:YbotAudioSourceBuffer* (YbotAudioSourceBuffer_c source bufferSize outputChannels nominalSampleRate))
(buffer2:YbotAudioSourceBuffer* (YbotAudioSourceBuffer_c source bufferSize outputChannels nominalSampleRate))
(loadFrame1:i64 0) (loadFrame2:i64 0) (busyLoading1:bool #f) (busyLoading2:bool #f)
(conch1:mutex_t (mutex_create)) (conch2:mutex_t (mutex_create))
(loader1:[void]* (lambda () (mutex_lock conch1) (YASB_load_from_offset buffer1 loadFrame1) (mutex_unlock conch1) void))
(loader2:[void]* (lambda () (mutex_lock conch2) (YASB_load_from_offset buffer2 loadFrame2) (mutex_unlock conch2) void))
(converter_block_size 32)
(conversion_buffer:SAMPLE* (zalloc (* converter_block_size outputChannels)))
(converter_err:i32* (zalloc)))
(pset! converter_err 0 0)
(loader1)
(loader2)
(let* ((toggle:bool #t)
(bufferedSource:YbotAudioSource_t
(lambda (windowStart:i64 windowSize:i64 channels:i64 output:SAMPLE*)
(if (and (<= 0 (tref buffer1 10)) (< (tref buffer1 10) (+ windowStart windowSize)))
-1
(if toggle
(let ((activeStatus:i64 (YASB_contains buffer1 windowStart windowSize))
(idleStatus:i64 (YASB_contains buffer2 windowStart windowSize)))
(cond
((= activeStatus YASB_not_ready)
;(println "Active Buffer is not ready!")
0)
((= activeStatus YASB_buffer_too_small) (println "Streaming buffer too small for requested audio window") 0)
((or (= activeStatus YASB_before) (= activeStatus YASB_just_past) (= activeStatus YASB_well_past))
(cond
((or (= idleStatus YASB_well_inside) (= idleStatus YASB_near_end))
(printf "Buffer 2 is finished loading from %lld to %lld and contains offset %lld\n" (tref buffer2 4) (+ (tref buffer2 4) (tref buffer2 1)) windowStart)
(set! toggle #f)
(set! busyLoading2 #f)
windowSize)
((= idleStatus YASB_not_ready) 0)
(else
(cond
((not busyLoading2)
(cond
((ybot_trylock conch2)
(set! busyLoading2 #t)
(set! loadFrame2 windowStart)
(printf "Emergency Loading into buffer2 at offset %lld\n" windowStart)
(mutex_unlock conch2)
(spawn loader2)
windowSize)
(else 0))
windowSize)
(else 0)))))
((= activeStatus YASB_near_end)
(cond
((not (or (= idleStatus YASB_well_inside) (= idleStatus YASB_near_end) (= idleStatus YASB_not_ready) busyLoading2))
(cond
((ybot_trylock conch2)
(println "\n\n --------- Initiating Loading into Buffer2 -----")
(set! busyLoading2 #t)
(printf "Standard Loading into buffer2 at offset %lld on thread %p\n" windowStart (thread_self))
(set! loadFrame2 windowStart)
(mutex_unlock conch2)
(spawn loader2)
windowSize)
(else 0)))
(else 0)))
(else windowSize))
(if (ybot_trylock conch1)
(let ((success:bool (YASB_fetch_window buffer1 windowStart windowSize output)))
(mutex_unlock conch1)
(if success windowSize 0))
0))
(let ((activeStatus:i64 (YASB_contains buffer2 windowStart windowSize))
(idleStatus:i64 (YASB_contains buffer1 windowStart windowSize)))
(cond
((= activeStatus YASB_not_ready)
;(println "Active Buffer is not ready!")
0)
((= activeStatus YASB_buffer_too_small) (println "Streaming buffer too small for requested audio window") 0)
((or (= activeStatus YASB_before) (= activeStatus YASB_just_past) (= activeStatus YASB_well_past))
(cond
((or (= idleStatus YASB_well_inside) (= idleStatus YASB_near_end))
;(println "Toggling: setting buffer 1 active, and buffer 2 is ready")
(printf "Buffer 1 is finished loading from %lld to %lld and contains offset %lld\n" (tref buffer1 4) (+ (tref buffer1 4) (tref buffer1 1)) windowStart)
(set! toggle #t)
(set! busyLoading1 #f)
windowSize)
((= idleStatus YASB_not_ready) 0)
(else
(cond
((not busyLoading1)
(cond
((ybot_trylock conch1)
(set! busyLoading1 #t)
(printf "Emergency Loading into buffer1 at offset %lld\n" windowStart)
(set! loadFrame1 windowStart)
(mutex_unlock conch1)
(spawn loader1)
windowSize)
(else 0))
windowSize)
(else 0)))))
((= activeStatus YASB_near_end)
(cond
((not (or (= idleStatus YASB_well_inside) (= idleStatus YASB_near_end) (= idleStatus YASB_not_ready) busyLoading2))
(cond
((ybot_trylock conch1)
(set! busyLoading1 #t)
(printf "Standard Loading into buffer1 at offset %lld on thread %p\n" windowStart (thread_self))
(set! loadFrame1 windowStart)
(mutex_unlock conch1)
(spawn loader1)
windowSize)
(else 0)))
(else 0)))
(else windowSize))
(if (ybot_trylock conch2)
(let ((success:bool (YASB_fetch_window buffer2 windowStart windowSize output)))
(mutex_unlock conch2)
(if success windowSize 0))
0)))))))
(YbotAudioStream bufferedSource outputChannels nominalSampleRate 0 32 converter_err conversion_buffer #f)))))
(bind-func YbotAudioBufferedStream_d:[void,YbotAudioStream*]*
(lambda (stream:YbotAudioStream*)
(let ((source:YbotAudioSource_t (tref stream 0)))
(mutex_destroy (pref (source.conch:mutex_t*) 0))
(YbotAudioSourceBuffer_d (source.buffer1:YbotAudioSourceBuffer*))
(YbotAudioSourceBuffer_d (source.buffer2:YbotAudioSourceBuffer*))
void)))
;; (bind-func YbotAudioTranscode:[bool,YbotAudioStream*,i64,i64,i8*,i64,audiofile_t,SAMPLE*]*
;; (lambda (stream windowStart windowSize stage path handle scratch)
;; (if (= stage 0)
;; (let ((info:SF_INFO* (zalloc))
;; (channels (tref stream 1))
;; (block_size (tref stream 4)))
;; (set! scratch (zalloc (* block_size channels)))
;; (set! handle (sf_open path SFM_WRITE info))
;; (set! stage 1)))
;; (let* ((source:YbotAudioSource_t (tref stream 0))
;; (frames_read:i64 (source window windowSize scratch)
;; (bind-func anduin (YbotAudioStream_c chutney 100000 2))
;; (bind-func dsp:DSP
;; (let ((output:SAMPLE* (zalloc CHANNELS)))
;; (lambda (in:SAMPLE time:i64 channel:i64 userData:SAMPLE*)
;; (if (= channel 0) (anduin time 1 CHANNELS output))
;; (pref output channel))))
;; (bind-func dsp:DSPMC
;; (lambda (in:float* out:float* time:i64 userData:i8*)
;; (anduin time FRAMES CHANNELS out)
;; void)))
;; (dsp:set! dsp)
(set! *xtmlib-ybot-audio-stream-loaded* #t)