-
Notifications
You must be signed in to change notification settings - Fork 0
/
ybot_audio_file_writer.xtm
166 lines (145 loc) · 6.32 KB
/
ybot_audio_file_writer.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
;; Include guard
(if (and (defined? '*xtmlib-ybot-audio-file-writer-loaded*) *xtmlib-ybot-audio-file-writer-loaded*)
(sys:load-escape "ybot_audio_file_writer library already loaded"))
(define *xtmlib-ybot-audio-file-writer-loaded* #f)
;; ybot-lib dependencies
(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* "/ybot_audio_file_reader.xtm"))
;; Other dependencies
;;;;;;;;;;;;;;;;;;;;;;
;; YbotAudioFileBuffer type ;;
;;;;;;;;;;;;;;;;;;;;;;
;; 0 SAMPLE* (data)
;; 1 number of frames in buffer
;; 2 channels
;; 3 samplerate
;; 4 which frame of the parent audiofile 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 AudioFile from which it is drawn
;; 8 an integer ID
;; 9 A Mutex for thread synchronisation
;; 10 status flag
(bind-func YAFB_write_to_file_offset:[i64,YbotAudioFileBuffer*,i64]*
(lambda (buffer start_frame)
(YAFB_lock buffer)
(let*
((data (tref buffer 0))
(frames (tref buffer 1))
(audiofile (tref buffer 7)))
(sf_seek audiofile start_frame SEEK_SET)
(let ((frames_writ (sf_writef audiofile data frames)))
(tset! buffer 4 start_frame)
(tset! buffer 5 start_frame)
(tset! buffer 10 #t)
(YAFB_unlock buffer)
frames_writ))))
(bind-alias YbotAudioFileWriter_t [bool,i64,i64,SAMPLE]*)
(bind-func silent_writer:YbotAudioFileWriter_t
(lambda (frame channel value)
#f))
(bind-func YbotAudioFileWriter_c:[YbotAudioFileWriter_t,i8*,i64]*
(lambda (fname write_buffer_width)
(let*
((playing_buffer:i64 1)
(buffering:bool #f)
(info:SF_INFO* (salloc))
(audiofile:audiofile_t (sf_open fname SFM_READ info)))
(if (null? audiofile)
(begin
(println "soundfile error:" (sf_strerror audiofile))
silent_reader)
;; when sf_open has read the file without error
(let ((frames (sf_frames info))
(channels (i32toi64 (sf_channels info)))
(samplerate (i32tof (sf_samplerate info))))
(if (= read_buffer_width 0)
(set! read_buffer_width frames) 0)
(if (<> samplerate (convert SAMPLERATE))
(println "File samplerate" samplerate "doesn't match the current audio samplerate" SR ) void)
(if (> read_buffer_width frames)
(set! read_buffer_width frames) 0)
;; initialise and allocate memory for the AudioBuffer
(let*
((frames_read 0) (current_file_read_frame 0)
(buffer1:YAFB* (YbotAudioFileBuffer_c frames channels (convert samplerate) audiofile 1))
(buffer2:YAFB* (YbotAudioFileBuffer_c frames channels (convert samplerate) audiofile 2)))
;; read the audio data from the file into buffer1
(println "About to read into buffer1")
(set! frames_read (YAFB_load_from_file_offset buffer1 0))
(println "Read in " frames_read " frames")
(lambda (frame channel)
(let*
((f (modulo frame frames))
(c (modulo channel channels))
(playbuf (if (= playing_buffer 1) buffer1 buffer2))
(idlebuf (if (= playing_buffer 1) buffer2 buffer1))
(p (YAFB_contains playbuf f))
(success:bool* (salloc))
(value:SAMPLE* (salloc)))
(cond
((= p YAFB_near_end)
(cond
((and (not buffering) (YAFB_ready playbuf) (YAFB_ready idlebuf)) ;; time to load some more data into the idle buffer
(spawn (let ((new_start_frame (+ read_buffer_width (tref playbuf 1))))
(lambda () (YAFB_load_from_file_offset idlebuf new_start_frame) void)))
(set! buffering #t)
void)
(else void)))
((or (= p YAFB_past) (= p YAFB_before))
(if (YAFB_ready idlebuf) (set! buffering #f) #f)
(let ((q (YAFB_contains idlebuf f)))
(cond
((= q 0)
(if (= playing_buffer 1)
(begin
(set! playing_buffer 2)
(set! playbuf buffer2)
(set! idlebuf buffer1)
void)
(begin
(set! playing_buffer 1)
(set! playbuf buffer1)
(set! idlebuf buffer2)
void)))
(else void))))
((= p YAFB_well_inside) ;; All is well :)
void)
(else ;; Something weird is happening - generate noise so we can notice it
(pset! value 0 (convert (* 0.1 (random))))
void
))
(YAFB_fetch_sample playbuf f c success value)
(pref value 0)))))))))
(bind-func YbotAudioFileReader_d:[i32,YbotAudioFileReader_t]*
(lambda (yafr)
(let*
((af:i8* (yafr.audiofile))
(buffer1:YAFB* (yafr.buffer1))
(buffer2:YAFB* (yafr.buffer2)))
(YbotAudioFileBuffer_d buffer2)
(YbotAudioFileBuffer_d buffer1)
(sf_close af))))
;;;;; Example usage ;;;;;;
(bind-func dsp:DSP
(let* ((playhead:i64 0)
(playing:i64 1)
(master_gain:float 2.0)
(reader (YbotAudioFileReader_c "/Volumes/Cybermat/Ultrasoundscapes/Tewantin_River_House/T64.WAV" (* 44100 10)))
(val:float 0.0))
(lambda (in:SAMPLE time:i64 channel:i64 data:SAMPLE*)
(if (< 0 playing)
(set! val (reader playhead channel))
(set! val (dtof 0.0)))
(cond ((= channel 0)
(set! playhead (+ playhead 1))
(cond ((> playhead (reader.frames))
(println "Looping")
(set! playhead 0)))))
(* master_gain val))))
(dsp:set! dsp)
(dsp.master_gain 0.5)
(dsp.playhead 0)
(dsp.playing 0)
(set! *xtmlib-ybot-audio-file-reader-loaded* #t)