├── .gitignore ├── Makefile ├── README.md ├── by-sa.eps ├── data.w ├── debug.w ├── glottis.w ├── header.w ├── macros.tex ├── plots ├── nose.plt ├── tongueshape1.plt ├── tongueshape2.plt ├── tongueshape3.plt ├── tongueshape4.plt └── tract.plt ├── ref.bib ├── sp.w ├── sp ├── chant.sp ├── rant.sp ├── test.sp └── unya.sp ├── top.w ├── tract.w ├── ugen.w ├── voc.lua └── voc.w /.gitignore: -------------------------------------------------------------------------------- 1 | cwebmac.tex 2 | btxmac.tex 3 | epsf.tex 4 | *.dat 5 | *.eps 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: pdf 2 | 3 | CFLAGS=-fPIC -Wall -ansi -g -pedantic -O3 4 | SP_LDFLAGS = -lsoundpipe -lsndfile -lm 5 | LDFLAGS=-lsporth $(SP_LDFLAGS) -lpthread -ldl 6 | # for more readable C output: 7 | #CTANGLE=ctanglex +c -l 8 | 9 | SPORTH_FILES= sp/test.tex sp/chant.tex sp/unya.tex sp/rant.tex 10 | 11 | PLOTS=plots/tract.eps plots/nose.eps \ 12 | plots/tongueshape1.eps\ 13 | plots/tongueshape2.eps\ 14 | plots/tongueshape3.eps\ 15 | plots/tongueshape4.eps\ 16 | 17 | WEB=data.w top.w ugen.w glottis.w header.w debug.w tract.w sp.w 18 | 19 | CONFIG?= 20 | 21 | include $(CONFIG) 22 | 23 | default: libvoc.a 24 | 25 | pdf: voc.pdf 26 | 27 | plugin: voc.so 28 | 29 | library: libvoc.a 30 | 31 | version: 32 | git rev-parse HEAD > version 33 | 34 | voc.tex: voc.w macros.tex $(WEB) $(PLOTS) $(SPORTH_FILES) version 35 | $(CWEAVE) -x voc.w 36 | 37 | voc.dvi: voc.tex 38 | tex "\let\pdf+ \input voc" 39 | bibtex voc 40 | 41 | voc.pdf: voc.dvi 42 | dvipdfm $< 43 | 44 | voc.c: voc.w $(WEB) 45 | $(CTANGLE) $< 46 | 47 | debug.c: voc.c 48 | 49 | plot.c: voc.c 50 | 51 | %.o: %.c 52 | $(CC) $(CFLAGS) -c $< -o $@ 53 | 54 | sp/%.tex: sp/%.sp 55 | sporth_tex $< > $@ 56 | 57 | voc.so: ugen.c voc.o 58 | $(CC) $(CFLAGS) -DBUILD_SPORTH_PLUGIN -shared voc.o $< -o $@ $(LDFLAGS) 59 | 60 | debug: debug.o voc.c 61 | $(CC) $(CFLAGS) debug.o voc.c -o $@ 62 | 63 | plot: plot.o voc.c 64 | $(CC) $(CFLAGS) plot.o voc.c -o $@ $(SP_LDFLAGS) 65 | 66 | plots/%.dat: plot 67 | ./plot $@ > $@ 68 | 69 | plots/%.eps: plots/%.plt plots/%.dat 70 | gnuplot $< 71 | 72 | libvoc.a: voc.o 73 | $(AR) rcs $@ voc.o 74 | 75 | clean: 76 | rm -rf voc.tex *.dvi *.idx *.log *.pdf *.sc *.toc *.scn 77 | rm -rf *.c 78 | rm -rf $(SP) 79 | rm -rf voc.so 80 | rm -rf *.aux *.bbl *.blg 81 | rm -rf voc.h 82 | rm -rf debug 83 | rm -rf *.o 84 | rm -rf plot 85 | rm -rf plots/*.eps 86 | rm -rf plots/*.dat 87 | rm -rf version 88 | rm -rf sp/*.tex 89 | rm -rf libvoc.a 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Voc 2 | 3 | A physical model of the vocal tract, based on 4 | [pink trombone](https://dood.al/pinktrombone/) 5 | 6 | More information can be found on the main 7 | [Voc webpage](http://pbat.ch/proj/voc). 8 | 9 | Things you'll need: 10 | 11 | - CWEB (this often comes with TeX) 12 | - [Soundpipe (dev branch)](http://www.github.com/paulbatchelor/soundpipe.git) 13 | - [Sporth](http://www.github.com/paulbatchelor/sporth.git) (somewhat optional) 14 | - sporth_tex (can be installed with Sporth by running "make sporth_tex" in the 15 | sporth codebase) 16 | - GNUplot (needed to generate visuals) 17 | 18 | Running "make" with no arguments will run ctangle. 19 | The core files generated are 20 | *voc.c* and *voc.h*. These can more or less be dropped into a working project 21 | and it will behave like any other soundpipe module. The exception to this is 22 | that you will need to use setter and getter functions to set and retrieve 23 | parameters in Voc. Some of these examples can be found inside the generated 24 | Voc document. 25 | 26 | ## Sound examples 27 | 28 | The videos below showcase Voc being used in a musical context. The sounds 29 | were synthesized using Sporth and the Sporth plugin implementation of Voc. 30 | 31 | - [Babble](https://vimeo.com/220091107): The first real patch created in Sporth 32 | using Voc was *Babble*. *Babble* makes good use of interpolated random number 33 | generators and jitter to produce sounds that mimic a babbling person. 34 | 35 | - [Chant](https://vimeo.com/220091290): The patch *Chant* was an attempt to 36 | build a constrasting patch to *Babble*, this time focusing on slow moving periodic 37 | modulations instead of fast and 38 | random ones. Voc is able to turn into something resembling a chanting 39 | monk by picking a low fundamental frequency, a high velum, low frequency 40 | sinusoidal modulation of the position, as well as a high diameter parameter. 41 | A lowend boost and reverb is added to taste. 42 | 43 | - [Unya](https://vimeo.com/220091487): 44 | This was an attempt to find interesting values from the tongue control 45 | parameters, as well as build something tonal. Both *diameter* and *position* 46 | are mapped to clocked envelope generators, which have the effect of going 47 | between two vowel states. In this case, they make a sound which approximates 48 | the nonsensical word "unya". 49 | 50 | ## Realtime Demo 51 | 52 | For those wishing to try out Voc with minimum hassle, there is a 53 | realtime [voc demo](https://www.github.com/paulbatchelor/voc_demo) which 54 | is able to compile for both Mac OSX and Linux systems running JACK. This 55 | demo uses the version of Voc contained inside of 56 | [Soundpipe](https://www.github.com/paulbatchelor/soundpipe). 57 | 58 | ## Licensing 59 | 60 | Voc has a multi-licensing scheme for the different components: 61 | 62 | - The core CWEB code uses the MIT license. 63 | - The code generated by ctangle (voc.c, voc.h, etc...) is released under 64 | the public domain. 65 | - The TeX code generated by cweave, and any resulting documents generated 66 | using that code such as the PDF, are released on the Creative Commons 67 | Attribution ShareAlike license. 68 | - The included Sporth code examples are all public domain 69 | -------------------------------------------------------------------------------- /by-sa.eps: -------------------------------------------------------------------------------- 1 | %!PS-Adobe-3.0 EPSF-3.0 2 | %%Creator: (ImageMagick) 3 | %%Title: (by-sa.eps) 4 | %%CreationDate: (2017-06-02T10:57:50-07:00) 5 | %%BoundingBox: -0 -0 96 34 6 | %%HiResBoundingBox: 0 0 96.0107 34 7 | %%DocumentData: Clean7Bit 8 | %%LanguageLevel: 1 9 | %%Pages: 1 10 | %%EndComments 11 | 12 | %%BeginDefaults 13 | %%EndDefaults 14 | 15 | %%BeginProlog 16 | % 17 | % Display a color image. The image is displayed in color on 18 | % Postscript viewers or printers that support color, otherwise 19 | % it is displayed as grayscale. 20 | % 21 | /DirectClassPacket 22 | { 23 | % 24 | % Get a DirectClass packet. 25 | % 26 | % Parameters: 27 | % red. 28 | % green. 29 | % blue. 30 | % length: number of pixels minus one of this color (optional). 31 | % 32 | currentfile color_packet readhexstring pop pop 33 | compression 0 eq 34 | { 35 | /number_pixels 3 def 36 | } 37 | { 38 | currentfile byte readhexstring pop 0 get 39 | /number_pixels exch 1 add 3 mul def 40 | } ifelse 41 | 0 3 number_pixels 1 sub 42 | { 43 | pixels exch color_packet putinterval 44 | } for 45 | pixels 0 number_pixels getinterval 46 | } bind def 47 | 48 | /DirectClassImage 49 | { 50 | % 51 | % Display a DirectClass image. 52 | % 53 | systemdict /colorimage known 54 | { 55 | columns rows 8 56 | [ 57 | columns 0 0 58 | rows neg 0 rows 59 | ] 60 | { DirectClassPacket } false 3 colorimage 61 | } 62 | { 63 | % 64 | % No colorimage operator; convert to grayscale. 65 | % 66 | columns rows 8 67 | [ 68 | columns 0 0 69 | rows neg 0 rows 70 | ] 71 | { GrayDirectClassPacket } image 72 | } ifelse 73 | } bind def 74 | 75 | /GrayDirectClassPacket 76 | { 77 | % 78 | % Get a DirectClass packet; convert to grayscale. 79 | % 80 | % Parameters: 81 | % red 82 | % green 83 | % blue 84 | % length: number of pixels minus one of this color (optional). 85 | % 86 | currentfile color_packet readhexstring pop pop 87 | color_packet 0 get 0.299 mul 88 | color_packet 1 get 0.587 mul add 89 | color_packet 2 get 0.114 mul add 90 | cvi 91 | /gray_packet exch def 92 | compression 0 eq 93 | { 94 | /number_pixels 1 def 95 | } 96 | { 97 | currentfile byte readhexstring pop 0 get 98 | /number_pixels exch 1 add def 99 | } ifelse 100 | 0 1 number_pixels 1 sub 101 | { 102 | pixels exch gray_packet put 103 | } for 104 | pixels 0 number_pixels getinterval 105 | } bind def 106 | 107 | /GrayPseudoClassPacket 108 | { 109 | % 110 | % Get a PseudoClass packet; convert to grayscale. 111 | % 112 | % Parameters: 113 | % index: index into the colormap. 114 | % length: number of pixels minus one of this color (optional). 115 | % 116 | currentfile byte readhexstring pop 0 get 117 | /offset exch 3 mul def 118 | /color_packet colormap offset 3 getinterval def 119 | color_packet 0 get 0.299 mul 120 | color_packet 1 get 0.587 mul add 121 | color_packet 2 get 0.114 mul add 122 | cvi 123 | /gray_packet exch def 124 | compression 0 eq 125 | { 126 | /number_pixels 1 def 127 | } 128 | { 129 | currentfile byte readhexstring pop 0 get 130 | /number_pixels exch 1 add def 131 | } ifelse 132 | 0 1 number_pixels 1 sub 133 | { 134 | pixels exch gray_packet put 135 | } for 136 | pixels 0 number_pixels getinterval 137 | } bind def 138 | 139 | /PseudoClassPacket 140 | { 141 | % 142 | % Get a PseudoClass packet. 143 | % 144 | % Parameters: 145 | % index: index into the colormap. 146 | % length: number of pixels minus one of this color (optional). 147 | % 148 | currentfile byte readhexstring pop 0 get 149 | /offset exch 3 mul def 150 | /color_packet colormap offset 3 getinterval def 151 | compression 0 eq 152 | { 153 | /number_pixels 3 def 154 | } 155 | { 156 | currentfile byte readhexstring pop 0 get 157 | /number_pixels exch 1 add 3 mul def 158 | } ifelse 159 | 0 3 number_pixels 1 sub 160 | { 161 | pixels exch color_packet putinterval 162 | } for 163 | pixels 0 number_pixels getinterval 164 | } bind def 165 | 166 | /PseudoClassImage 167 | { 168 | % 169 | % Display a PseudoClass image. 170 | % 171 | % Parameters: 172 | % class: 0-PseudoClass or 1-Grayscale. 173 | % 174 | currentfile buffer readline pop 175 | token pop /class exch def pop 176 | class 0 gt 177 | { 178 | currentfile buffer readline pop 179 | token pop /depth exch def pop 180 | /grays columns 8 add depth sub depth mul 8 idiv string def 181 | columns rows depth 182 | [ 183 | columns 0 0 184 | rows neg 0 rows 185 | ] 186 | { currentfile grays readhexstring pop } image 187 | } 188 | { 189 | % 190 | % Parameters: 191 | % colors: number of colors in the colormap. 192 | % colormap: red, green, blue color packets. 193 | % 194 | currentfile buffer readline pop 195 | token pop /colors exch def pop 196 | /colors colors 3 mul def 197 | /colormap colors string def 198 | currentfile colormap readhexstring pop pop 199 | systemdict /colorimage known 200 | { 201 | columns rows 8 202 | [ 203 | columns 0 0 204 | rows neg 0 rows 205 | ] 206 | { PseudoClassPacket } false 3 colorimage 207 | } 208 | { 209 | % 210 | % No colorimage operator; convert to grayscale. 211 | % 212 | columns rows 8 213 | [ 214 | columns 0 0 215 | rows neg 0 rows 216 | ] 217 | { GrayPseudoClassPacket } image 218 | } ifelse 219 | } ifelse 220 | } bind def 221 | 222 | /DisplayImage 223 | { 224 | % 225 | % Display a DirectClass or PseudoClass image. 226 | % 227 | % Parameters: 228 | % x & y translation. 229 | % x & y scale. 230 | % label pointsize. 231 | % image label. 232 | % image columns & rows. 233 | % class: 0-DirectClass or 1-PseudoClass. 234 | % compression: 0-none or 1-RunlengthEncoded. 235 | % hex color packets. 236 | % 237 | gsave 238 | /buffer 512 string def 239 | /byte 1 string def 240 | /color_packet 3 string def 241 | /pixels 768 string def 242 | 243 | currentfile buffer readline pop 244 | token pop /x exch def 245 | token pop /y exch def pop 246 | x y translate 247 | currentfile buffer readline pop 248 | token pop /x exch def 249 | token pop /y exch def pop 250 | currentfile buffer readline pop 251 | token pop /pointsize exch def pop 252 | /Times-Roman findfont pointsize scalefont setfont 253 | x y scale 254 | currentfile buffer readline pop 255 | token pop /columns exch def 256 | token pop /rows exch def pop 257 | currentfile buffer readline pop 258 | token pop /class exch def pop 259 | currentfile buffer readline pop 260 | token pop /compression exch def pop 261 | class 0 gt { PseudoClassImage } { DirectClassImage } ifelse 262 | grestore 263 | } bind def 264 | %%EndProlog 265 | %%Page: 1 1 266 | %%PageBoundingBox: 0 0 96 34 267 | userdict begin 268 | DisplayImage 269 | 0 0 270 | 96.0107 33.6037 271 | 12 272 | 120 42 273 | 0 274 | 0 275 | C9C9C92C2C2C030303000000000000000000000000000000000000000000000000000000000000 276 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 277 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 278 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 279 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 280 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 281 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 282 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 283 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 284 | 0404042C2C2CCBCBCB2C2C2C707571A7AFA8AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 285 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 286 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 287 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 288 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 289 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 290 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 291 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 292 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 293 | AAB2ABAAB2ABAAB2ABA7AFA86C716D2B2B2B010101A7AFA8AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 294 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 295 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 296 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 297 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 298 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 299 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 300 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 301 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 302 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA6AEA7020202000000A7AFA8AAB2ABAAB2AB 303 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 304 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 305 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 306 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 307 | AAB2ABAAB2ABAAB2ABA7AFA87C827D595D594A4D4A4447444F53506065618C938DAAB2ABAAB2AB 308 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 309 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB919892626763515552444744494D4A575B58777C77 310 | A4ACA5AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 311 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000000000 312 | A7AFA8AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 313 | AAB2AB8C938D585C593134321D1E1D0F0F0F0101010404041112111F21203A3D3A626763979E98 314 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 315 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 316 | AAB2ABAAB2ABAAB2ABAAB2AB9DA59E4B4F4C0B0B0B000000000000000000000000000000000000 317 | 000000171817656A66A7AFA8AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 318 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA9B1AA7177721E1F1E000000000000000000000000 319 | 000000000000000000070707414442979E98AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 320 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 321 | A7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 322 | AAB2ABAAB2AB7C827D252726000000000000000000000000000000000000000000000000000000 323 | 0000000000000101013639368C938DAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 324 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 325 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB7075710B0B0B0000000000001515155A5A5A707070 326 | 7777776B6B6B4A4A4A0808080000000000001B1D1B8D938DAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 327 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB99A09A272928000000000000040404 328 | 4141416969697777777272725E5E5E1D1D1D0000000000000506055E625FAAB2ABAAB2ABAAB2AB 329 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 330 | AAB2ABAAB2ABAAB2ABA7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 331 | AAB2ABAAB2ABAAB2AB9FA7A03B3D3B0000000000000000000000000000000A0A0A1E1E1E323232 332 | 2F2F2F1A1A1A070707000000000000000000000000030303515451A8B0A9AAB2ABAAB2ABAAB2AB 333 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 334 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB5559550101010000002E2E2EB5B5B5 335 | FCFCFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F4F4909090141414000000090909787E79AAB2AB 336 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB89908A121312000000 337 | 0B0B0B7E7E7EEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4C4C43F3F3F000000000000 338 | 3F423FA7AFA8AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 339 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2AB 340 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB8F9690171817000000000000000000141414757575C4C4C4 341 | FDFDFDFFFFFFFFFFFFFFFFFFFFFFFFF8F8F8B6B6B66666660A0A0A000000000000000000313331 342 | A0A8A1AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 343 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB5F6460000000000000 344 | 737373FCFCFCFFFFFFFFFFFFFFFFFFC7C7C78F8F8FE7E7E7FFFFFFFFFFFFFFFFFFEBEBEB404040 345 | 000000050605838A84AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB969D97 346 | 0F100F0000002A2A2ADCDCDCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 347 | FFFFFF919191040404000000434644AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 348 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000000000A7AFA8 349 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB8D938D0E0F0E0000000000000B0C0B828282 350 | F3F3F3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7E7E76D6D6D 351 | 0404040000000000001C1D1C9BA29CAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 352 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB7E847F 353 | 020202000000868686FFFFFFFFFFFFFFFFFFFFFFFFE1E1E1060606000000383838FFFFFFFFFFFF 354 | FFFFFFFFFFFFFAFAFA4B4B4B0000000F0F0F9BA29CAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 355 | AAB2ABA7AFA81F21200000002C2C2CF0F0F0FFFFFFFFFFFFFFFFFFFFFFFFFBFBFBEEEEEEEEEEEE 356 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFADADAD020202000000646965AAB2ABAAB2ABAAB2ABAAB2AB 357 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8 358 | 000000000000A7AFA8AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB979E98111211000000000000 359 | 242524DADADAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 360 | FFFFFFFFFFFFFFFFFFC4C4C4141414000000000000212221A2AAA3AAB2ABAAB2ABAAB2ABAAB2AB 361 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 362 | AAB2ABA9B1AA1F201F000000575757FEFEFEFFFFFFFFFFFFFFFFFFFFFFFFD0D0D0000000000000 363 | 212121FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F1F1282828000000494D4AAAB2ABAAB2ABAAB2AB 364 | AAB2ABAAB2ABAAB2ABAAB2AB636864000000111111DDDDDDFFFFFFFFFFFFFFFFFFDCDCDC525252 365 | 0A0A0A000000000000232323898989F6F6F6FFFFFFFFFFFFFFFFFF8383830000000D0D0DA1A8A2 366 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 367 | AAB2ABAAB2ABA7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA5ACA6262826 368 | 000000000000262626E4E4E4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 369 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5D5D5161616000000000000393C3AAAB2AB 370 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 371 | AAB2ABAAB2ABAAB2ABAAB2AB7176710000000E0E0EE9E9E9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 372 | FFFFFF9090904B4B4BC5C5C5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7B7B7000000050505 373 | 9AA19BAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA6AEA71011100000008F8F8FFFFFFFFFFFFFFFFFFF 374 | D2D2D2121212000000000000000000000000000000000000333333EDEDEDFFFFFFFFFFFFFAFAFA 375 | 252525000000595D59AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 376 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2ABAAB2AB 377 | AAB2AB515552000000000000151515DFDFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 378 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCECECE0A0A0A 379 | 0000000000006D726EAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 380 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB3739370000007A7A7AFFFFFFFFFFFFFFFFFF 381 | FFFFFFFFFFFFA9A9A95555555555555555555555555C5C5CD7D7D7FFFFFFFFFFFFFFFFFFFFFFFF 382 | FFFFFF343434000000676C68AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB7D837D000000171717F9F9F9 383 | FFFFFFFFFFFFFFFFFF363636000000000000545454A8A8A89D9D9D3D3D3D000000000000565656 384 | FFFFFFFFFFFFFFFFFF9C9C9C000000212322AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 385 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000000000A7AFA8AAB2AB 386 | AAB2ABAAB2ABAAB2AB99A19A060606000000000000BABABAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 387 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 388 | FFFFFFFFFFFF9A9A9A000000000000171817A7AFA8AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 389 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8080908000000CDCDCD 390 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A3A3A000000000000000000000000000000888888FFFFFF 391 | FFFFFFFFFFFFFFFFFFFFFFFF848484000000373A38AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB4B4E4B 392 | 000000646464FFFFFFFFFFFFFFFFFFCECECE000000000000404040FEFEFEFFFFFFFFFFFFF8F8F8 393 | 232323000000010101D5D5D5FFFFFFFFFFFFE9E9E90202020101019BA29CAAB2ABAAB2ABAAB2AB 394 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000 395 | 000000A7AFA8AAB2ABAAB2ABAAB2ABAAB2AB4F524F000000000000505050FFFFFFFFFFFFFFFFFF 396 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 397 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFCFC2B2B2B0000000000006E736FAAB2ABAAB2ABAAB2AB 398 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB979E98 399 | 000000070707FEFEFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A3A3A000000000000000000000000 400 | 000000878787FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8B8B8000000212221AAB2ABAAB2ABAAB2AB 401 | AAB2ABAAB2AB3234320000009C9C9CFFFFFFFFFFFFF0F0F04444440000000000003B3B3BF2F2F2 402 | FFFFFFFFFFFFFFFFFF8080800000000000008B8B8BFFFFFFFFFFFFFFFFFF202020000000858C86 403 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 404 | AAB2ABA7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2ABA9B1AA111211000000010101D7D7D7 405 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDEDEDEBDBDBDC9C9C9FAFAFAFFFFFFFFFFFFFFFFFFFFFFFF 406 | FFFFFFDFDFDFBDBDBDC8C8C8F8F8F8FFFFFFFFFFFFFFFFFFFFFFFFAEAEAE0000000000002F3230 407 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 408 | AAB2ABAAB2AB878D870000001E1E1EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A3A3A000000 409 | 000000000000000000000000878787FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFCFCF000000121212 410 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB222422000000B4B4B4FFFFFFFFFFFFFFFFFFCECECE131313 411 | 151515D0D0D0FFFFFFFFFFFFFFFFFFFFFFFFA5A5A5000000000000666666FFFFFFFFFFFFFFFFFF 412 | 373737000000767C77AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 413 | AAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2AB878D87000000 414 | 000000363636FFFFFFFFFFFFFFFFFFFFFFFFD0D0D02E2E2E000000000000000000191919A7A7A7 415 | FFFFFFFFFFFFD3D3D3303030000000000000000000161616A1A1A1FFFFFFFFFFFFFFFFFFF9F9F9 416 | 0D0D0D000000050505A5ADA6AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 417 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB888E890000001C1C1CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 418 | FFFFFF3A3A3A000000000000000000000000000000878787FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 419 | CDCDCD000000131413AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB242624000000B2B2B2FFFFFFFFFFFF 420 | FFFFFFFFFFFFC9C9C9CDCDCDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4A4A4000000000000626262 421 | FFFFFFFFFFFFFFFFFF353535000000777D78AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 422 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000000000A7AFA8AAB2ABAAB2AB 423 | AAB2AB616561000000000000858585FFFFFFFFFFFFFFFFFFE1E1E1121212000000000000242424 424 | 1C1C1C000000000000A7A7A7E5E5E51616160000000000002424241C1C1C0000000000009F9F9F 425 | FFFFFFFFFFFFFFFFFF545454000000000000868C87AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 426 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB979E98000000060606FDFDFDFFFFFF 427 | FFFFFFFFFFFFFFFFFFFFFFFF6161611C1C1C0000000000000000002C2C2C9F9F9FFFFFFFFFFFFF 428 | FFFFFFFFFFFFFFFFFFB6B6B6000000222422AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB333634000000 429 | 9A9A9AFFFFFFFFFFFFFFFFFFE7E7E7CCCCCCCCCCCCE8E8E8FFFFFFFFFFFFFFFFFFFFFFFF7F7F7F 430 | 000000000000828282FFFFFFFFFFFFFFFFFF1F1F1F000000868C87AAB2ABAAB2ABAAB2ABAAB2AB 431 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000000000 432 | A7AFA8AAB2ABAAB2ABAAB2AB4D514E000000000000BABABAFFFFFFFFFFFFFFFFFF626262000000 433 | 000000929292FFFFFFF9F9F94D4D4D686868E4E4E46C6C6C000000000000929292FFFFFFF9F9F9 434 | 4C4C4C686868E2E2E2FFFFFFFFFFFFFFFFFF848484000000000000757A75AAB2ABAAB2ABAAB2AB 435 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA80B0B0B 436 | 000000C8C8C8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C8C8C000000000000000000DADADA 437 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F7F7F000000393C3AAAB2ABAAB2ABAAB2ABAAB2AB 438 | AAB2AB4D514E0000005F5F5FFFFFFFFFFFFFFFFFFFA8A8A8000000000000636363FFFFFFFFFFFF 439 | FFFFFFF9F9F91F1F1F000000000000C8C8C8FFFFFFFFFFFFE7E7E70101010101019DA49EAAB2AB 440 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 441 | A7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2AB3E413E000000000000D1D1D1FFFFFFFFFFFF 442 | FFFFFF1B1B1B0000000F0F0FFBFBFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF262626000000121212 443 | FCFCFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B9B9B000000000000656965 444 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 445 | AAB2ABAAB2AB3A3D3A000000747474FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C8C8C000000 446 | 000000000000DADADAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF313131000000696E6AAAB2AB 447 | AAB2ABAAB2ABAAB2ABAAB2AB808681000000141414F7F7F7FFFFFFFFFFFFF2F2F2111111000000 448 | 040404939393DDDDDDC5C5C5474747000000000000424242FFFFFFFFFFFFFFFFFF999999000000 449 | 232524AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 450 | AAB2ABAAB2ABAAB2ABA7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2AB313331000000000000 451 | E5E5E5FFFFFFFFFFFFFFFFFF050505000000272727FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 452 | 1111110000002B2B2BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAEAEAE 453 | 000000000000585C59AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 454 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB757B760000000B0B0BE6E6E6FFFFFFFFFFFFFFFFFFFFFFFF 455 | FFFFFF8C8C8C000000000000000000DADADAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4B4B4000000 456 | 0607079BA39CAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8141514000000898989FFFFFFFFFFFF 457 | FFFFFFA2A2A20101010000000000000000000000000000000000001D1D1DE1E1E1FFFFFFFFFFFF 458 | FAFAFA2323230000005B605CAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 459 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2AB 460 | 3B3E3C000000000000D4D4D4FFFFFFFFFFFFFFFFFF141414000000131313FEFEFEFFFFFFFFFFFF 461 | FFFFFFFFFFFFFFFFFF202020000000171717FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 462 | FFFFFFFFFFFF9E9E9E000000000000626763AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 463 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA9B1AA2526250000004E4E4EFDFDFD 464 | FFFFFFFFFFFFFFFFFFFFFFFF8C8C8C000000000000000000DADADAFFFFFFFFFFFFFFFFFFFFFFFF 465 | EEEEEE2525250000004E524EAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB696E6A000000 466 | 0D0D0DD4D4D4FFFFFFFFFFFFFFFFFFA5A5A51E1E1E000000000000000000070707606060E7E7E7 467 | FFFFFFFFFFFFFFFFFF7D7D7D000000101110A3AAA4AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 468 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000000000A7AFA8 469 | AAB2ABAAB2ABAAB2AB4B4F4C000000000000BDBDBDFFFFFFFFFFFFFFFFFF4D4D4D000000000000 470 | ACACACFFFFFFFFFFFF7F7F7F565656DDDDDD595959000000000000ADADADFFFFFFFFFFFF818181 471 | 555555DBDBDBFFFFFFFFFFFFFFFFFF878787000000000000717772AAB2ABAAB2ABAAB2ABAAB2AB 472 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB848A85 473 | 050505000000707070FEFEFEFFFFFFFFFFFFFFFFFF8C8C8C000000000000000000DADADAFFFFFF 474 | FFFFFFFFFFFFF3F3F34141410000001617169FA6A0AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 475 | AAB2ABA8B0A9262826000000212121E5E5E5FFFFFFFFFFFFFFFFFFFCFCFCD7D7D7AFAFAFB8B8B8 476 | EFEFEFFFFFFFFFFFFFFFFFFFFFFFFF9D9D9D0101010000006D726EAAB2ABAAB2ABAAB2ABAAB2AB 477 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8 478 | 000000000000A7AFA8AAB2ABAAB2ABAAB2AB5D615D0000000000008F8F8FFFFFFFFFFFFFFFFFFF 479 | C5C5C50303030000000505054646463838380101010000009C9C9CCFCFCF060606000000050505 480 | 464646393939010101000000949494FFFFFFFFFFFFFFFFFF5E5E5E000000000000828883AAB2AB 481 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 482 | AAB2ABAAB2ABAAB2AB696E690000000000005D5D5DF8F8F8FFFFFFFFFFFFCACACA888888888888 483 | 888888EEEEEEFFFFFFFFFFFFDADADA2D2D2D0000000909098B928CAAB2ABAAB2ABAAB2ABAAB2AB 484 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB9BA29C1415140000001D1D1DD0D0D0FFFFFFFFFFFFFFFFFF 485 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAFAFA7474740101010000004E524EAAB2ABAAB2AB 486 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 487 | AAB2ABAAB2ABA7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2AB818782000000000000414141 488 | FFFFFFFFFFFFFFFFFFFFFFFFA8A8A81313130000000000000000000707078F8F8FFFFFFFFFFFFF 489 | B1B1B1161616000000000000000000060606888888FFFFFFFFFFFFFFFFFFFDFDFD151515000000 490 | 030303A2AAA3AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 491 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB616662030303000000202020A1A1A1F6F6F6 492 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8E8E87979790808080000000F100F828883AAB2ABAAB2AB 493 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB909791191B19000000050505 494 | 6B6B6BDEDEDEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAFAFAB0B0B02828280000000000004D504D 495 | A9B1AAAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 496 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2ABA6AEA7 497 | 0C0D0C000000040404E4E4E4FFFFFFFFFFFFFFFFFFFFFFFFF4F4F4AFAFAF999999A6A6A6E9E9E9 498 | FFFFFFFFFFFFFFFFFFFFFFFFF6F6F6B1B1B1999999A5A5A5E7E7E7FFFFFFFFFFFFFFFFFFFFFFFF 499 | C0C0C0000000000000292B29AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 500 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB7D837D141515 501 | 0000000000000A0A0A4848485F5F5F6666665A5A5A383838020202000000000000272827959C96 502 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 503 | 9FA7A03639360000000000000000002F2F2F5858586666666161614D4D4D101010000000000000 504 | 0B0B0B696E6AAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 505 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000000000A7AFA8AAB2AB 506 | AAB2ABAAB2ABAAB2AB454946000000000000616161FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 507 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 508 | FFFFFFFFFFFFFFFFFF3D3E3D000000000000626763AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 509 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 510 | AAB2ABAAB2ABA4ACA55A5E5B171717000000000000000000000000000000000000010101262726 511 | 737974A9B1AAAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 512 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB8086812C2E2C020202000000000000000000000000000000 513 | 0000000F100F4F524F9EA59FAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 514 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000 515 | 000000A7AFA8AAB2ABAAB2ABAAB2ABAAB2AB919892020202000000020202C9C9C9FFFFFFFFFFFF 516 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 517 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAEAEAE0000000000000C0D0CA2AAA3AAB2ABAAB2ABAAB2AB 518 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 519 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB898F89656965565A574F53505B5F5B 520 | 6C716D989F99AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 521 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB9DA49E6E736F5D615D 522 | 4F5350555955636763838A84A9B1AAAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 523 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 524 | AAB2ABA7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB4E524E000000000000 525 | 181818E2E2E2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 526 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2D2D20C0C0C000000000000636864AAB2AB 527 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 528 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 529 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 530 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 531 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 532 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 533 | AAB2ABAAB2ABAAB2ABAAB2ABA7AFA8000000000000A7AFA8AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 534 | A3AAA41F201F000000000000353535EFEFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 535 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5E5E5252525000000000000 536 | 262826A7AFA8AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 537 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 538 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 539 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 540 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 541 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 542 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA7AFA80000000000004E524E4F53504F5350 543 | 4F53507E847FAAB2ABAAB2AB959C960D0E0D000000000000282828DBDBDBFFFFFFFFFFFFFFFFFF 544 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9C9C9191919 545 | 000000000000111211979E98AAB2ABAAB2AB686D694F53504F53504F53504F53504F53504F5350 546 | 4F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F5350 547 | 4F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F5350 548 | 4F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F5350 549 | 4F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F5350 550 | 4F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504F5350 551 | 4F53504F53504F53504F53504F53504F53504F53504F53504F53504F53504E524E000000000000 552 | 0000000000000000000000000D0D0D9BA29CAAB2ABAAB2AB818781070807000000000000121212 553 | 969696FBFBFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F4F4 554 | 8181810808080000000000001111118C938DAAB2ABAAB2AB7C827D010101000000000000000000 555 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 556 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 557 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 558 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 559 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 560 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 561 | 000000000000000000000000000000000000000000000000393C3AA9B1AAAAB2ABAAB2AB8A908B 562 | 131413000000000000000000282828919191DADADAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFE 563 | CDCDCD848484181818000000000000000000252625989F99AAB2ABAAB2ABA0A8A1191A19000000 564 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 565 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 566 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 567 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 568 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 569 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 570 | 000000000000000000000000000000000000000000000000000000000000000000000000535754 571 | AAB2ABAAB2ABAAB2AB9AA19B2B2D2B000000000000000000000000000000191919393939505050 572 | 4D4D4D333333131313000000000000000000000000010101464946A5ACA6AAB2ABAAB2ABA6AEA7 573 | 292B2A000000000000000000000000000000000000000000000000000000000000000000000000 574 | 000000000000000000000000000000000000000000000000000000090909444444444444444444 575 | 3131310404041111114444441A1A1A0000000000002C2C2C424242010101000000000000000000 576 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 577 | 3E3E3E6565653C3C3C000000000000000000040404444444222222000000000000000000000000 578 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 579 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 580 | 000000000000010101676C68AAB2ABAAB2ABAAB2ABA9B1AA6B706C1B1D1B000000000000000000 581 | 0000000000000000000000000000000000000000000000000000002D2F2E858C86AAB2ABAAB2AB 582 | AAB2ABA7AFA83F423F000000000000000000000000000000000000000000000000000000000000 583 | 000000000000000000000000000000000000000000000000000000000000000000000000202020 584 | FFFFFFE8E8E8DDDDDDFAFAFACCCCCC050505D0D0D0C6C6C60101011A1A1AF2F2F2929292000000 585 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 586 | 0000000000009A9A9AF1F1F1BEBEBEF4F4F49A9A9A000000000000515151FFFFFFC0C0C0000000 587 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 588 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 589 | 0000000000000000000000000000000000000101015B605CAAB2ABAAB2ABAAB2ABAAB2ABA9B1AA 590 | 7F85804447441C1D1C0202020000000000000000000000000505052527264F524F8C938DAAB2AB 591 | AAB2ABAAB2ABAAB2ABA1A8A2303230000000000000000000000000000000000000000000000000 592 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 593 | 000000000000202020FFFFFF565656000000606060FEFEFE0D0D0D3C3C3CFEFEFE606060A1A1A1 594 | E6E6E60E0E0E000000000000000000000000000000000000000000000000000000000000000000 595 | 000000000000000000000000020202F9F9F97474740000004D4D4DA6A6A6030303000000AEAEAE 596 | D9D9D9FDFDFD202020000000000000000000000000000000000000000000000000000000000000 597 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 598 | 000000000000000000000000000000000000000000000000000000000000000000282A28919892 599 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA4ACA58B928C888E89888E89909791A7AFA8AAB2AB 600 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2AB787E79101110000000000000000000000000000000000000 601 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 602 | 000000000000000000000000000000202020FFFFFFBBBBBB999999D7D7D79C9C9C000000000000 603 | A6A6A6ECECECFDFDFD626262000000000000000000000000000000000000000000000000000000 604 | 000000000000000000000000000000000000000000000000B7B7B7F9F9F9B9B9B97575751B1B1B 605 | 000000191919FBFBFB4B4B4BE0E0E0808080000000000000000000000000000000000000000000 606 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 607 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 608 | 000000000000090A09595D59A3ABA4AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB 609 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2AB979E98414442020202000000000000000000000000 610 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 611 | 000000000000000000000000000000000000000000000000202020FFFFFF999999676767A2A2A2 612 | F6F6F62C2C2C000000191919F5F5F5C8C8C8010101000000000000000000000000000000000000 613 | 000000000000000000000000000000000000000000000000000000000000000000040404565656 614 | 9D9D9DEBEBEBE9E9E9111111717171F6F6F6393939ACACACDCDCDC010101000000000000000000 615 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 616 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 617 | 0000000000000000000000000000000000000000001111115256528E958FAAB2ABAAB2ABAAB2AB 618 | AAB2ABAAB2ABAAB2ABAAB2ABAAB2ABAAB2ABA9B1AA7F8580424542060606000000000000000000 619 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 620 | 000000000000000000000000000000000000000000000000000000000000000000202020FFFFFF 621 | 5656560000001A1A1AFFFFFF626262000000000000E0E0E0949494000000000000000000000000 622 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 623 | 2A2A2ADDDDDD3A3A3A000000343434FFFFFF3E3E3ED1D1D1ECECECDDDDDDDFDFDFFFFFFF3E3E3E 624 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 625 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 626 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 627 | 1516153739375559566368647176716E736F6165614F53502E302E0D0D0D000000000000000000 628 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 629 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 630 | 000000202020FFFFFFDDDDDDCCCCCCE7E7E7E9E9E91D1D1D000000000000E0E0E0949494000000 631 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 632 | 000000000000000000050505C8C8C8EAEAEAABABABD5D5D5D9D9D93F3F3FFFFFFF3B3B3B000000 633 | 000000D9D9D9A0A0A0000000000000000000000000000000000000000000000000000000000000 634 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 635 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 636 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 637 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 638 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 639 | 0000000000000000000000000B0B0B5555555555555555554C4C4C0D0D0D000000000000000000 640 | 4B4B4B313131000000000000000000000000000000000000000000000000000000000000000000 641 | 0000000000000000000000000000000000000000000707075454547777776262620C0C0C272727 642 | 5454540202020000000000003636364A4A4A000000000000000000000000000000000000000000 643 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 644 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 645 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 646 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 647 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 648 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 649 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 650 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 651 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 652 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 653 | 000000000000000000000000000000000000121212000000000000000000000000000000000000 654 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 655 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 656 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 657 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 658 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 659 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 660 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 661 | 000000000000000000000000000000000000000000000000000000000000000000000000000000 662 | 000000000000000000000000000000000000000000000000121312 663 | 664 | end 665 | %%PageTrailer 666 | %%Trailer 667 | %%EOF 668 | -------------------------------------------------------------------------------- /data.w: -------------------------------------------------------------------------------- 1 | @* Header Inclusion, Structs, and Macros. 2 | 3 | @ \subsec{Header of File} 4 | The header section consists of header inclusion, and definition of C-structs. 5 | The system-wide header files include |stdlib.h| for things like |malloc()|. 6 | Standard math library functions from |math.h| are used. 7 | Soundpipe/Sporth specific header files are |soundpipe.h| and |sporth.h|. 8 | It should be noted that due to the implementation of Sporth, the Soundpipe 9 | header file {\it must} be included before the Sporth header file. 10 | 11 | ANSI C doesn't have the constant |M_PI|, so it has to be explicitly defined. 12 | 13 | Both |MIN| and |MAX| macros are defined. 14 | 15 | The header file |string.h| is included so that |memset| can be used to 16 | zero arrays. 17 | 18 | There is exactly one local header file called |voc.h|, which 19 | is generated by CTANGLE. For more information about this header file, 20 | see |@| 21 | 22 | The macro |MAX_TRANSIENTS| is the maximum number of transients at a given 23 | time. 24 | 25 | @= 26 | #include 27 | #include 28 | #include 29 | #include "soundpipe.h" 30 | 31 | #ifndef M_PI 32 | #define M_PI 3.14159265358979323846 33 | #endif 34 | 35 | #include "voc.h" 36 | 37 | #ifndef MIN 38 | #define MIN(A,B) ((A) < (B) ? (A) : (B)) 39 | #endif 40 | 41 | #ifndef MAX 42 | #define MAX(A,B) ((A) > (B) ? (A) : (B)) 43 | #endif 44 | 45 | #define EPSILON 1.0e-38 46 | 47 | #define MAX_TRANSIENTS 4 48 | 49 | @ 50 | 51 | @ \subsec{Structs} 52 | This subsection contains all the data structs needed by Voc. 53 | 54 | @= 55 | @@/ 56 | @@/ 57 | @@/ 58 | @@/ 59 | @@/ 60 | 61 | @ The top-most data structure is |sp_voc|, designed to be an opaque 62 | struct containing all the variables needed for {\it Voc} to work. 63 | Like all Soundpipe modules, this struct has the prefix "sp". 64 | 65 | @= 66 | 67 | struct sp_voc { 68 | glottis @, glot; /*The Glottis*/ 69 | tract @, tr; /*The Vocal Tract */ 70 | SPFLOAT @, buf[512]; 71 | int counter; 72 | }; 73 | 74 | @ The glottis data structure contains all the variables used by the glottis. 75 | See |@| to see the implementation of the glottal sound source. 76 | 77 | \item{$\bullet$} |enable| is the on/off state of the glottis 78 | \item{$\bullet$} |freq| is the frequency 79 | \item{$\bullet$} |tenseness| is the tenseness of the glottis (more or less looks 80 | like a cross fade between voiced and unvoiced sound). It is a value in the 81 | range $[0,1]$. 82 | \item{$\bullet$} |intensity| is an internal value used for applying attack and 83 | release on |enable| transitions 84 | \item{$\bullet$} |attack_time| is the time in seconds to reach full amplitude 85 | following glottis on 86 | \item{$\bullet$} |release_time| is the time in seconds to reach 0 amplitude 87 | following glottis off 88 | \item{$\bullet$} |Rd| % is what? 89 | \item{$\bullet$} |waveform_length| provides the period length (in seconds) of 90 | the fundamental frequency, in seconds. 91 | \item{$\bullet$} The waveform position is kept track of in |time_in_waveform|, 92 | in seconds. 93 | 94 | % TODO: describe what these variables are 95 | \item{$\bullet$} |alpha| 96 | \item{$\bullet$} |E0| 97 | \item{$\bullet$} |epsilon| 98 | \item{$\bullet$} |shift| 99 | \item{$\bullet$} |delta| 100 | \item{$\bullet$} |Te| 101 | \item{$\bullet$} |omega| 102 | \item{$\bullet$} |T| 103 | 104 | @= 105 | 106 | typedef struct { 107 | int @, enable; 108 | SPFLOAT @, freq; 109 | SPFLOAT @, tenseness; 110 | SPFLOAT @, intensity; 111 | SPFLOAT @, attack_time; 112 | SPFLOAT @, release_time; 113 | SPFLOAT @, Rd; 114 | SPFLOAT @, waveform_length; 115 | SPFLOAT @, time_in_waveform; 116 | 117 | SPFLOAT @, alpha; 118 | SPFLOAT @, E0; 119 | SPFLOAT @, epsilon; 120 | SPFLOAT @, shift; 121 | SPFLOAT @, delta; 122 | SPFLOAT @, Te; 123 | SPFLOAT @, omega; 124 | 125 | SPFLOAT @, T; 126 | } glottis; 127 | 128 | @ 129 | @= 130 | @@/ 131 | @ 132 | 133 | @ This data struct outlines the data for a single transient. A transient 134 | will act as a single entry in a linked list implementation, so there exists 135 | a |next| pointer along with the |SPFLOAT| parameters. 136 | 137 | @= 138 | typedef struct transient { 139 | int @, position; 140 | SPFLOAT @, time_alive; 141 | SPFLOAT @, lifetime; 142 | SPFLOAT @, strength; 143 | SPFLOAT @, exponent; 144 | char is_free; 145 | unsigned int id; 146 | struct transient *next; 147 | } transient; 148 | 149 | @ A pre-allocated set of transients and other parameters are used in what 150 | will be known as a {\it transient pool}. A memory pool is an ideal choice for 151 | realtime systems instead of dynamic memory. Calls to |malloc| are discouraged 152 | because it adds performance overhead and possible blocking behavior, and there 153 | is a greater chance of memory leaks or segfaults if not handled properly. 154 | 155 | 156 | @= 157 | typedef struct { 158 | transient pool[MAX_TRANSIENTS]; 159 | transient *root; 160 | int size; 161 | int next_free; 162 | } transient_pool; 163 | 164 | @ The Tract C struct contains all the data needed for the vocal tract filter. 165 | @= 166 | typedef struct { 167 | int n; 168 | @t \indent n is the size, set to 44. @> @/ 169 | SPFLOAT @, diameter[44]; 170 | SPFLOAT @, rest_diameter[44]; 171 | SPFLOAT @, target_diameter[44]; 172 | SPFLOAT @, R[44]; @t \indent component going right @>@/ 173 | SPFLOAT @, L[44]; @t \indent component going left @>@/ 174 | SPFLOAT @, reflection[45]; 175 | SPFLOAT @, new_reflection[45]; 176 | SPFLOAT @, junction_outL[45]; 177 | SPFLOAT @, junction_outR[45]; 178 | SPFLOAT @, A[44]; 179 | 180 | int nose_length; 181 | @t \indent The original code here has it at $floor(28 * n/44)$, and since @> 182 | @t n=44, it should be 28.@>@/ 183 | int nose_start; @t \indent $n - nose\_length + 1$, or 17 @>@/ 184 | @t tip\_start is a constant set to 32 @>@/ 185 | int tip_start; 186 | SPFLOAT @, noseL[28]; 187 | SPFLOAT @, noseR[28]; 188 | SPFLOAT @, nose_junc_outL[29]; 189 | SPFLOAT @, nose_junc_outR[29]; 190 | SPFLOAT @, nose_reflection[29]; 191 | SPFLOAT @, nose_diameter[28]; 192 | SPFLOAT @, noseA[28]; 193 | 194 | SPFLOAT @, reflection_left; 195 | SPFLOAT @, reflection_right; 196 | SPFLOAT @, reflection_nose; 197 | 198 | SPFLOAT @, new_reflection_left; 199 | SPFLOAT @, new_reflection_right; 200 | SPFLOAT @, new_reflection_nose; 201 | 202 | SPFLOAT @, velum_target; 203 | 204 | SPFLOAT @, glottal_reflection; 205 | SPFLOAT @, lip_reflection; 206 | int @, last_obstruction; 207 | SPFLOAT @, fade; 208 | SPFLOAT @, movement_speed; @t 15 cm/s @>@\ 209 | SPFLOAT @, lip_output; 210 | SPFLOAT @, nose_output; 211 | SPFLOAT @, block_time; 212 | 213 | transient_pool tpool; 214 | SPFLOAT @, T; 215 | } tract; 216 | 217 | @ 218 | @= 219 | 220 | static SPFLOAT move_towards(SPFLOAT current, SPFLOAT target, 221 | SPFLOAT amt_up, SPFLOAT amt_down) 222 | { 223 | SPFLOAT tmp; 224 | if(current < target) { 225 | tmp = current + amt_up; 226 | return MIN(tmp, target); 227 | } else { 228 | tmp = current - amt_down; 229 | return MAX(tmp, target); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /debug.w: -------------------------------------------------------------------------------- 1 | @* Small Applications and Examples. 2 | 3 | It has been fruitful investment to write small applications to assist in the 4 | debugging process. Such programs can be used to generate plots or visuals, or 5 | to act as a simple program to be used with GDB. In addition to debugging, 6 | these programs are also used to quickly try out concepts or ideas. 7 | 8 | @ \subsec{A Program for Non-Realtime Processing and Debugging} 9 | The example program below is a C program designed out of necessity to debug 10 | and test Voc. It a program with a simple commandline interface, where 11 | the user gives a "mode" along with set of optional arguments. 12 | 13 | The following modes are as follows: 14 | 15 | \item{$\bullet$} {\bf audio:} writes an audio file called "test.wav". You 16 | must supply a duration (in samples). 17 | \item{$\bullet$} {\bf plot:} Uses sp\_process\_plot to generate a 18 | matlab/octave compatible program that plots the audio output. 19 | \item{$\bullet$} {\bf tongue:} Will be a test program that experiments with 20 | parameters manipulating tongue position. It takes in tongue index and diameter 21 | parameters, to allow for experimentation without needing to recompile. 22 | 23 | The functions needed to call Voc from C in this way are found in the 24 | section |@|. 25 | 26 | @(debug.c@>= 27 | #include 28 | #include 29 | #include 30 | #include "voc.h" 31 | 32 | static void process(sp_data *sp, void *ud) 33 | { 34 | SPFLOAT out; 35 | sp_voc *voc = ud; 36 | 37 | sp_voc_compute(sp, voc, &out); 38 | 39 | sp_out(sp, 0, out); 40 | } 41 | 42 | static void run_voc(long len, int type) 43 | { 44 | sp_voc *voc; 45 | sp_data *sp; 46 | 47 | sp_create(&sp); 48 | sp->len = len; 49 | sp_voc_create(&voc); 50 | sp_voc_init(sp, voc); 51 | 52 | if(type == 0) { 53 | sp_process_plot(sp, voc, process); 54 | } else { 55 | sp_process(sp, voc, process); 56 | } 57 | 58 | sp_voc_destroy(&voc); 59 | sp_destroy(&sp); 60 | } 61 | 62 | static void run_tongue(SPFLOAT tongue_index, SPFLOAT tongue_diameter) 63 | { 64 | sp_voc *voc; 65 | sp_data *sp; 66 | 67 | sp_create(&sp); 68 | sp_voc_create(&voc); 69 | sp_voc_init(sp, voc); 70 | 71 | fprintf(stderr, "Tongue index: %g. Tongue diameter: %g\n", 72 | tongue_index, 73 | tongue_diameter); 74 | sp_voc_set_tongue_shape(voc, tongue_index, tongue_diameter); 75 | sp_process(sp, voc, process); 76 | 77 | sp_voc_destroy(&voc); 78 | sp_destroy(&sp); 79 | } 80 | 81 | int main(int argc, char *argv[]) 82 | { 83 | 84 | if(argc == 1) { 85 | fprintf(stderr, "Pick a mode!\n"); 86 | exit(0); 87 | } 88 | 89 | if(!strcmp(argv[1], "plot")) { 90 | if(argc < 3) { 91 | fprintf(stderr, 92 | "Usage: %s plot duration (samples)\n", 93 | argv[0]); 94 | exit(0); 95 | } 96 | run_voc(atoi(argv[2]), 0); 97 | } else if(!strcmp(argv[1], "audio")) { 98 | if(argc < 3) { 99 | fprintf(stderr, 100 | "Usage: %s audio duration (samples)\n", 101 | argv[0]); 102 | exit(0); 103 | } 104 | run_voc(atoi(argv[2]), 1); 105 | } else if(!strcmp(argv[1], "tongue")) { 106 | if(argc < 4) { 107 | fprintf(stderr, "Usage %s tongue tongue_index tongue_diameter\n", 108 | argv[0]); 109 | exit(0); 110 | } 111 | run_tongue(atof(argv[2]), atof(argv[3])); 112 | } else { 113 | fprintf(stderr, "Error: invalid type %s\n", argv[1]); 114 | } 115 | 116 | return 0; 117 | } 118 | 119 | @ \subsec{A Utility for Plotting Data} 120 | The following program below is used to write data files to be read by 121 | GNUplot. The primary use of this program is for generating use plots 122 | in this document, such as those seen in the section |@|. 123 | 124 | @(plot.c@>= 125 | #include 126 | #include 127 | #include 128 | #include "voc.h" 129 | 130 | static void plot_tract() 131 | { 132 | sp_voc *voc; 133 | sp_data *sp; 134 | SPFLOAT *tract; 135 | int size; 136 | int i; 137 | 138 | sp_create(&sp); 139 | sp_voc_create(&voc); 140 | sp_voc_init(sp, voc); 141 | 142 | tract = sp_voc_get_tract_diameters(voc); 143 | size = sp_voc_get_tract_size(voc); 144 | 145 | for(i = 0; i < size; i++) { 146 | printf("%i\t%g\n", i, tract[i]); 147 | } 148 | 149 | sp_voc_destroy(&voc); 150 | sp_destroy(&sp); 151 | } 152 | 153 | static void plot_nose() 154 | { 155 | sp_voc *voc; 156 | sp_data *sp; 157 | SPFLOAT *nose; 158 | int size; 159 | int i; 160 | 161 | sp_create(&sp); 162 | sp_voc_create(&voc); 163 | sp_voc_init(sp, voc); 164 | 165 | nose = sp_voc_get_nose_diameters(voc); 166 | size = sp_voc_get_nose_size(voc); 167 | 168 | for(i = 0; i < size; i++) { 169 | printf("%i\t%g\n", i, nose[i]); 170 | } 171 | 172 | sp_voc_destroy(&voc); 173 | sp_destroy(&sp); 174 | } 175 | 176 | static void plot_tongue_shape(int num) 177 | { 178 | sp_voc *voc; 179 | sp_data *sp; 180 | SPFLOAT *tract; 181 | int size; 182 | int i; 183 | 184 | sp_create(&sp); 185 | sp_voc_create(&voc); 186 | sp_voc_init(sp, voc); 187 | 188 | tract = sp_voc_get_tract_diameters(voc); 189 | size = sp_voc_get_tract_size(voc); 190 | 191 | switch(num) { 192 | case 1: 193 | sp_voc_set_tongue_shape(voc, 20.5, 3.5); 194 | break; 195 | case 2: 196 | sp_voc_set_tongue_shape(voc, 25.5, 3.5); 197 | break; 198 | case 3: 199 | sp_voc_set_tongue_shape(voc, 20.5, 2.0); 200 | break; 201 | case 4: 202 | sp_voc_set_tongue_shape(voc, 24.8, 1.4); 203 | break; 204 | } 205 | 206 | for(i = 0; i < size; i++) { 207 | printf("%i\t%g\n", i, tract[i]); 208 | } 209 | 210 | sp_voc_destroy(&voc); 211 | sp_destroy(&sp); 212 | } 213 | 214 | int main(int argc, char **argv) 215 | { 216 | if(argc < 2) { 217 | fprintf(stderr, "Usage: %s plots/name.dat\n", argv[0]); 218 | exit(1); 219 | } 220 | if(!strncmp(argv[1], "plots/tract.dat", 100)) { 221 | plot_tract(); 222 | } else if(!strncmp(argv[1], "plots/nose.dat", 100)) { 223 | plot_nose(); 224 | } else if(!strncmp(argv[1], "plots/tongueshape1.dat", 100)) { 225 | plot_tongue_shape(1); 226 | } else if(!strncmp(argv[1], "plots/tongueshape2.dat", 100)) { 227 | plot_tongue_shape(2); 228 | } else if(!strncmp(argv[1], "plots/tongueshape3.dat", 100)) { 229 | plot_tongue_shape(3); 230 | } else if(!strncmp(argv[1], "plots/tongueshape4.dat", 100)) { 231 | plot_tongue_shape(4); 232 | } else { 233 | fprintf(stderr, "Plot: could not find plot %s\n", argv[1]); 234 | exit(1); 235 | } 236 | return 0; 237 | } 238 | -------------------------------------------------------------------------------- /glottis.w: -------------------------------------------------------------------------------- 1 | @* The Glottis. 2 | 3 | This is where the synthesis of the glottal source signal will be created. 4 | 5 | While the implementation comes directly from Pink Trombone's JavaScript code, 6 | it should be noted that the glottal model is based on a modified 7 | LF-model\cite{lu2000glottal}. 8 | 9 | @= 10 | @@/ 11 | @@/ 12 | @@/ 13 | @@/ 14 | 15 | @ Initializiation of the glottis is done inside of |glottis_init|. 16 | 17 | @= 18 | static void glottis_init(glottis *glot, SPFLOAT sr) 19 | { 20 | glot->enable = 1; /* boolean 0 or 1 */ 21 | glot->freq = 140; /* 140Hz frequency by default */ 22 | glot->tenseness = 0.6; /* value between 0 and 1 */ 23 | glot->intensity = 0; /* value between 0 and 1 */ 24 | glot->attack_time = 0.09; 25 | glot->release_time = 0.23; 26 | glot->T = 1.0/sr; /* big T */ 27 | glot->time_in_waveform = 0; 28 | glottis_setup_waveform(glot, 0); 29 | } 30 | 31 | @ This is where glottis parameters are updated per sample block 32 | 33 | @= 34 | static void glottis_update(glottis *glot, SPFLOAT block_time) 35 | { 36 | /* update attack and release envelope */ 37 | SPFLOAT target_intensity = glot->enable == 1 ? 1 : 0; 38 | glot->intensity = move_towards(glot->intensity, 39 | target_intensity, 40 | block_time / glot->attack_time, 41 | block_time / glot->release_time); 42 | } 43 | 44 | @ This is where a single sample of audio is computed for the glottis 45 | 46 | @= 47 | static SPFLOAT glottis_compute(sp_data *sp, glottis *glot, SPFLOAT lambda) 48 | { 49 | SPFLOAT out; 50 | SPFLOAT aspiration; 51 | SPFLOAT noise; 52 | SPFLOAT t; 53 | SPFLOAT voice_loudness; 54 | 55 | out = 0; 56 | glot->time_in_waveform += glot->T; 57 | 58 | if(glot->time_in_waveform > glot->waveform_length) { 59 | glot->time_in_waveform -= glot->waveform_length; 60 | glottis_setup_waveform(glot, lambda); 61 | 62 | } 63 | 64 | t = (glot->time_in_waveform / glot->waveform_length); 65 | @q PT: normalizedLFWaveform @> 66 | if(t > glot->Te) { 67 | out = (-exp(-glot->epsilon * (t-glot->Te)) + glot->shift) / glot->delta; 68 | } else { 69 | out = glot->E0 * exp(glot->alpha * t) * sin(glot->omega * t); 70 | } 71 | voice_loudness = pow(glot->tenseness, 0.25); 72 | out *= voice_loudness; 73 | 74 | @q generate white noise source @> 75 | @q TODO: apply band pass filter @> 76 | noise = 1.0 * ((SPFLOAT) sp_rand(sp) / SP_RANDMAX) - 0.5; 77 | 78 | @q TODO: modulate aspiration amplitude in voiced case @> 79 | aspiration = (1 - sqrt(glot->tenseness)) * 0.2 * noise; 80 | 81 | aspiration *= 0.2; 82 | 83 | out += aspiration; 84 | 85 | return out * glot->intensity; 86 | } 87 | 88 | @ The function |glottis_setup_waveform| is tasked with setting the variables 89 | needed to create the glottis waveform. The glottal model used here is known 90 | as the LF-model, as described in Lu and Smith\cite{lu2000glottal}. 91 | 92 | 93 | @= 94 | static void glottis_setup_waveform(glottis *glot, SPFLOAT lambda) 95 | { 96 | @@/ 97 | @@/ 98 | @@/ 99 | @@/ 100 | 101 | @@/ 102 | 103 | @@/ 104 | @@/ 105 | 106 | @ 107 | } 108 | 109 | @ A number of local variables are used for intermediate 110 | calculations. They are described below. 111 | @= 112 | SPFLOAT Rd; 113 | SPFLOAT Ra; 114 | SPFLOAT Rk; 115 | SPFLOAT Rg; 116 | 117 | SPFLOAT Ta; 118 | SPFLOAT Tp; 119 | SPFLOAT Te; 120 | 121 | SPFLOAT epsilon; 122 | SPFLOAT shift; 123 | SPFLOAT delta; 124 | SPFLOAT rhs_integral; 125 | 126 | SPFLOAT lower_integral; 127 | SPFLOAT upper_integral; 128 | 129 | SPFLOAT omega; 130 | SPFLOAT s; 131 | SPFLOAT y; 132 | SPFLOAT z; 133 | 134 | SPFLOAT alpha; 135 | SPFLOAT E0; 136 | 137 | @ To begin, both |waveform_length| and $R_d$ are calcuated. 138 | 139 | The variable |waveform_length| is the period of the waveform based on the 140 | current frequency, and will be used later on in |@|. 141 | 142 | $R_d$ is part of a set of normalized timing parameters used to calculate 143 | the time coefficients described in the LF model \cite{fant1997voice}. 144 | The other timing parameters $R_a$, $R_g$, and $R_k$ can 145 | be computed in terms of $R_d$, which is why this gets computed first. 146 | $R_d$ is derived from the parameter |glot->tenseness|. 147 | 148 | $R_d$ is then clamped to be in between 0.5 and 2.7, as these 149 | are good approximations\cite{lu2000glottal}. 150 | 151 | @= 152 | glot->Rd = 3 * (1 - glot->tenseness); 153 | glot->waveform_length = 1.0 / glot->freq; 154 | 155 | Rd = glot->Rd; 156 | if(Rd < 0.5) Rd = 0.5; 157 | if(Rd > 2.7) Rd = 2.7; 158 | 159 | @ $R_d$ can be used to calculate approximations for $R_a$, $R_g$, and $R_k$. 160 | The equations described below have been derived using linear regression. 161 | $$R_{ap} = {(-1 + 4.8R_d) \over 100}$$ 162 | $$R_{kp} = {(22.4 + 11.8R_d) \over 100}$$ 163 | 164 | $R_{gp}$ is derived using the results from $R_{ap}$ and $R_{kp}$ in 165 | the following equation described in Fant 1997: 166 | 167 | $$R_d = (1/0.11)(0.5 + 1.2R_{k})(R_k / 4R_g + R_a)$$ 168 | 169 | Which yields: 170 | $$R_{gp} = {(R_{kp}/4)(0.5 + 1.2R_{kp})\over 171 | (0.11R_d - R_{ap}*(0.5+1.2R_{kp}))}$$ 172 | 173 | @= 174 | Ra = -0.01 + 0.048*Rd; 175 | Rk = 0.224 + 0.118*Rd; 176 | Rg = (Rk/4)*(0.5 + 1.2*Rk)/(0.11*Rd-Ra*(0.5+1.2*Rk)); 177 | 178 | @ The parameters approximating $R_a$, $R_g$, and $R_k$ can be used to 179 | calculate the timing parameters $T_a$, $T_p$, and $T_e$ in the LF model: 180 | 181 | $$T_a = R_{ap}$$ 182 | $$T_p = 2R_{gp}^{-1}$$ 183 | $$T_e = T_p + T_pR_{kp}$$ 184 | 185 | @= 186 | Ta = Ra; 187 | Tp = (SPFLOAT)1.0 / (2*Rg); 188 | Te = Tp + Tp*Rk; 189 | 190 | @ @= 191 | epsilon = (SPFLOAT)1.0 / Ta; 192 | shift = exp(-epsilon * (1 - Te)); 193 | delta = 1 - shift; 194 | 195 | @ @= 196 | rhs_integral = (SPFLOAT)(1.0/epsilon) * (shift-1) + (1-Te)*shift; 197 | rhs_integral = rhs_integral / delta; 198 | lower_integral = - (Te - Tp) / 2 + rhs_integral; 199 | upper_integral = -lower_integral; 200 | 201 | @ 202 | 203 | $$E_0 = -{E_e \over e^{\alpha T}\sin{\omega_g T_e}}$$ 204 | $$\omega = {\pi \over T_p}$$ 205 | $$\epsilon T_a = 1 - e^{-\epsilon(T_c - T_e)}$$ 206 | @= 207 | omega = M_PI / Tp; 208 | s = sin(omega * Te); 209 | 210 | y = -M_PI * s * upper_integral / (Tp*2); 211 | z = log(y); 212 | alpha = z / (Tp/2 - Te); 213 | E0 = -1 / (s * exp(alpha*Te)); 214 | 215 | @ @= 216 | glot->alpha = alpha; 217 | glot->E0 = E0; 218 | glot->epsilon = epsilon; 219 | glot->shift = shift; 220 | glot->delta = delta; 221 | glot->Te = Te; 222 | glot->omega = omega; 223 | -------------------------------------------------------------------------------- /header.w: -------------------------------------------------------------------------------- 1 | @* Header File. 2 | |CTANGLE| will end up generating two files: a single C amalgamation and this 3 | header file. 4 | 5 | This header file exists for individuals who wish to use Voc in their own 6 | programs. Voc follows Soundpipe's hardware-agnostic design, and should be 7 | trivial to throw in any DSP inner loop. 8 | 9 | The contents of the header is fairly minimal. Following a standard 10 | header guard, the contents consist of: 11 | \smallskip 12 | \item{$\bullet$} a |typedef| around the opaque struct |sp_voc| 13 | \item{$\bullet$} function declarations which adhere to the 4-stage 14 | Soundpipe module lifecycle model. 15 | \item{$\bullet$} a collection of setter/getter functions to allow to get and 16 | set data from the opaque struct. 17 | 18 | Since |Voc| makes use of opaque struct pointers, this header file will need 19 | to declare setter/getter functions for any user parameters. 20 | 21 | @(voc.h@>= 22 | #ifndef SP_VOC 23 | #define SP_VOC 24 | typedef struct sp_voc sp_voc; 25 | 26 | int sp_voc_create(sp_voc **voc); 27 | int sp_voc_destroy(sp_voc **voc); 28 | int sp_voc_init(sp_data *sp, sp_voc *voc); 29 | int sp_voc_compute(sp_data *sp, sp_voc *voc, SPFLOAT *out); 30 | int sp_voc_tract_compute(sp_data *sp, sp_voc *voc, SPFLOAT *in, SPFLOAT *out); 31 | 32 | void sp_voc_set_frequency(sp_voc *voc, SPFLOAT freq); 33 | SPFLOAT * sp_voc_get_frequency_ptr(sp_voc *voc); 34 | 35 | SPFLOAT* sp_voc_get_tract_diameters(sp_voc *voc); 36 | SPFLOAT* sp_voc_get_current_tract_diameters(sp_voc *voc); 37 | int sp_voc_get_tract_size(sp_voc *voc); 38 | SPFLOAT* sp_voc_get_nose_diameters(sp_voc *voc); 39 | int sp_voc_get_nose_size(sp_voc *voc); 40 | void sp_voc_set_tongue_shape(sp_voc *voc, 41 | SPFLOAT tongue_index, 42 | SPFLOAT tongue_diameter); 43 | void sp_voc_set_glottis_enable(sp_voc *voc, int enable); 44 | void sp_voc_set_tenseness(sp_voc *voc, SPFLOAT breathiness); 45 | SPFLOAT * sp_voc_get_tenseness_ptr(sp_voc *voc); 46 | void sp_voc_set_velum(sp_voc *voc, SPFLOAT velum); 47 | SPFLOAT * sp_voc_get_velum_ptr(sp_voc *voc); 48 | 49 | void sp_voc_set_diameters(sp_voc *voc, 50 | int blade_start, 51 | int lip_start, 52 | int tip_start, 53 | SPFLOAT tongue_index, 54 | SPFLOAT tongue_diameter, 55 | SPFLOAT *diameters); 56 | 57 | int sp_voc_get_counter(sp_voc *voc); 58 | 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /macros.tex: -------------------------------------------------------------------------------- 1 | \font\bigfont=cmssdc10 at 48pt 2 | \font\mediumfont=cmssdc10 at 24pt 3 | \font\sectionfont=cmssdc10 at 14pt 4 | 5 | \def\startcenter{% 6 | \par 7 | \begingroup 8 | \leftskip=0pt plus 1fil 9 | \rightskip=\leftskip 10 | \parindent=0pt 11 | \parfillskip=0pt 12 | } 13 | 14 | \def\stopcenter{% 15 | \par 16 | \endgroup 17 | } 18 | 19 | \def\sporthcode #1{ 20 | { 21 | \bigskip 22 | \begingroup \narrower 23 | \input sp/#1 24 | \endgroup 25 | \bigskip 26 | } 27 | 28 | } 29 | 30 | \def \sword #1 { 31 | {\hbox{\tt#1}} 32 | } 33 | 34 | \def \subsec #1 { 35 | {\medskip \noindent \sectionfont #1 \smallskip} 36 | } 37 | 38 | % Slightly redefined macros from cwebmac 39 | 40 | \def\MN#1{\par % common code for \M, \N 41 | {\xdef\secstar{#1}\let\*=\empty\xdef\secno{#1}}% remove \* from section name 42 | \ifx \secno\secstar \onmaybe \else \ontrue \fi 43 | \mark{{{\tensy x}\secno}{\the\gdepth}{\the\gtitle}} 44 | } 45 | 46 | \def\stsec_alt{\rightskip=0pt % get out of C mode (cf. \B) 47 | \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 48 | %\noindent{\let\*=\lapstar\bf\secstar.\quad}% 49 | \ifpdftex\smash{\raise\baselineskip\hbox to0pt{% 50 | \let\*=\empty\pdfdest num \secstar fith}} 51 | \else\ifpdf\smash{\raise\baselineskip\hbox to0pt{% 52 | \let\*=\empty\special{% 53 | pdf: dest (\romannumeral\secstar) [ @thispage /FitH @ypos ]}}}\fi\fi} 54 | 55 | 56 | \outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section 57 | \vskip\intersecskip\startsection\ignorespaces} 58 | 59 | \outer\def\N#1#2#3.{% beginning of starred section 60 | \ifacro{\toksF={}\makeoutlinetoks#3\outlinedone\outlinedone}\fi 61 | \gdepth=#1 62 | \gtitle={#3} 63 | \MN{#2}% 64 | \ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small 65 | \else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi 66 | \message{*\secno} % progress report 67 | \def\stripprefix##1>{}\def\gtitletoks{#3}% 68 | \edef\gtitletoks{\expandafter\stripprefix\meaning\gtitletoks}% 69 | \edef\next{\write\cont{\ZZ{\gtitletoks}{#1}{\secno}% write to contents file 70 | {\noexpand\the\pageno}{\the\toksE}}}\next % \ZZ{title}{depth}{sec}{page}{ss} 71 | \ifpdftex\expandafter\xdef\csname curr#1\endcsname{\secno} 72 | \ifnum#1>0\countB=#1 \advance\countB by-1 73 | \advancenumber{chunk\the\countB.\expnumber{curr\the\countB}}\fi\fi 74 | \ifpdf\special{pdf: outline #1 << /Title (\the\toksE) /Dest 75 | [ @thispage /FitH @ypos ] >>}\fi 76 | \ifon\stsec_alt{\bigheader{#3} {\rm (#2) }\quad \smallskip}\ignorespaces} 77 | 78 | \def\bigheader#1{\noindent {\mediumfont #1}} 79 | 80 | \let\url\relax 81 | 82 | \def\begincodecomment{ 83 | \medskip 84 | \begingroup 85 | \leftskip=1in 86 | \rightskip=1in 87 | \sl 88 | } 89 | 90 | \def\endcodecomment{ 91 | \par 92 | \medskip 93 | \endgroup 94 | } 95 | 96 | % display figures from gnuplot 97 | 98 | \def \displayfig #1 { 99 | \startcenter 100 | \epsfxsize=180pt 101 | \epsfbox{#1} 102 | \stopcenter 103 | } 104 | -------------------------------------------------------------------------------- /plots/nose.plt: -------------------------------------------------------------------------------- 1 | set terminal eps 2 | unset key 3 | set offsets 0, 0, 0.1, 0 4 | set title "Initial Nose Shape" 5 | set output "plots/nose.eps" 6 | plot "plots/nose.dat" with lines 7 | -------------------------------------------------------------------------------- /plots/tongueshape1.plt: -------------------------------------------------------------------------------- 1 | set terminal eps 2 | unset key 3 | set offsets 0, 0, 0.1, 0 4 | set title "Tract of tongueshape(20.5, 3.5)" 5 | set output "plots/tongueshape1.eps" 6 | plot "plots/tongueshape1.dat" with lines 7 | -------------------------------------------------------------------------------- /plots/tongueshape2.plt: -------------------------------------------------------------------------------- 1 | set terminal eps 2 | unset key 3 | set offsets 0, 0, 0.1, 0 4 | set title "Tract of tongueshape(25.5, 3.5)" 5 | set output "plots/tongueshape2.eps" 6 | plot "plots/tongueshape2.dat" with lines 7 | -------------------------------------------------------------------------------- /plots/tongueshape3.plt: -------------------------------------------------------------------------------- 1 | set terminal eps 2 | unset key 3 | set yrange [0:3.5] 4 | set title "Tract of tongueshape(20.5, 2.0)" 5 | set output "plots/tongueshape3.eps" 6 | plot "plots/tongueshape3.dat" with lines 7 | -------------------------------------------------------------------------------- /plots/tongueshape4.plt: -------------------------------------------------------------------------------- 1 | set terminal eps 2 | unset key 3 | set offsets 0, 0, 0.1, 0 4 | set title "Tract of tongueshape(24.8, 1.4)" 5 | set output "plots/tongueshape4.eps" 6 | plot "plots/tongueshape4.dat" with lines 7 | -------------------------------------------------------------------------------- /plots/tract.plt: -------------------------------------------------------------------------------- 1 | set terminal eps 2 | unset key 3 | set offsets 0, 0, 0.1, 0 4 | set title "Initial Tract Shape" 5 | set output "plots/tract.eps" 6 | plot "plots/tract.dat" with lines 7 | -------------------------------------------------------------------------------- /ref.bib: -------------------------------------------------------------------------------- 1 | @misc{pinktrombone, 2 | title={{\it Pink Trombone}}, 3 | howpublished={https://dood.al/pinktrombone/}, 4 | note = {Accessed: 04-19-2017}, 5 | author= {Neil Thapen} 6 | } 7 | 8 | @book{pharr2016physically, 9 | title={Physically based rendering: From theory to implementation}, 10 | author={Pharr, Matt and Jakob, Wenzel and Humphreys, Greg}, 11 | year={2016}, 12 | publisher={Morgan Kaufmann} 13 | } 14 | 15 | @book{knuth1992literate, 16 | title={Literate programming}, 17 | author={Knuth, Donald Ervin}, 18 | year={1992}, 19 | publisher={Center for the Study of Language and Information Stanford} 20 | } 21 | 22 | @inproceedings{lu2000glottal, 23 | title={Glottal source modeling for singing voice synthesis.}, 24 | author={Lu, Hui-Ling and Smith III, Julius O}, 25 | booktitle={ICMC}, 26 | year={2000} 27 | } 28 | 29 | @article{fant1997voice, 30 | title={The voice source in connected speech}, 31 | author={Fant, Gunnar}, 32 | journal={Speech communication}, 33 | volume={22}, 34 | number={2-3}, 35 | pages={125--139}, 36 | year={1997}, 37 | publisher={Elsevier} 38 | } 39 | 40 | @book{mullen2006physical, 41 | title={Physical modelling of the vocal tract with the 2D digital waveguide mesh}, 42 | author={Mullen, Jack}, 43 | year={2006}, 44 | publisher={University of York} 45 | } 46 | 47 | 48 | @book{knuth1994cweb, 49 | title={The CWEB system of structured documentation: version 3.0}, 50 | author={Knuth, Donald Ervin and Levy, Silvio}, 51 | year={1994}, 52 | publisher={Addison-Wesley Longman Publishing Co., Inc.} 53 | } 54 | -------------------------------------------------------------------------------- /sp.w: -------------------------------------------------------------------------------- 1 | @* Soundpipe Files. 2 | 3 | This section here outlines files specifically needed to fulfill the Soundpipe 4 | the requirements for being a Soundpipe module. 5 | 6 | The components of a fully implemented Soundpipe module consist of the 7 | following: 8 | 9 | \item{$\bullet$} The core callback code implementing create, destroy, 10 | initialize, and compute functions (the core of this document) 11 | \item{$\bullet$} An accompanying header file for the core code (see 12 | |$(voc.h@>|) 13 | \item{$\bullet$} An example file, showcase a simple usecase for the module 14 | in a small C program, using the namespace convenction ex\_FOO.c. 15 | \item{$\bullet$} A metadata file in the form of a Lua table. This file is 16 | mainly used to generate documentation for Soundpipe, but it is also used 17 | to generate Sporth ugen code. 18 | \item{$\bullet$} A soundpipe test file, using the namespace t\_FOO.c. This 19 | file gets included with Soundpipe's internal test utility, which implements 20 | a form of unit testing for DSP code. 21 | \item{$\bullet$} A soundpipe performance file, using the namespce p\_FOO.c. 22 | This file get inslucded with Soundpipe's internal performance utiltity, 23 | used to gauge how computationally expensive a given Soundpipe module is. 24 | 25 | \subsec{A small C Example} 26 | 27 | Each soundpipe module comes with a small example file showcasing how to use a 28 | module. This one utilizes the macro tongue control outlined in 29 | |@| to shape the vowel formants. In this case, a 30 | single LFO is modulating the tract position. 31 | 32 | In addition to providing some example code, these short programs often come 33 | in handy with debugging programs like GDB and Valgrind. 34 | @(ex_voc.c@>= 35 | 36 | #include 37 | #include 38 | #include 39 | #include "soundpipe.h" 40 | 41 | typedef struct { 42 | sp_voc *voc; 43 | sp_osc *osc; 44 | sp_ftbl *ft; 45 | } UserData; 46 | 47 | void process(sp_data *sp, void *udata) { 48 | UserData *ud = udata; 49 | SPFLOAT osc = 0, voc = 0; 50 | sp_osc_compute(sp, ud->osc, NULL, &osc); 51 | if(sp_voc_get_counter(ud->voc) == 0) { 52 | osc = 12 + 16 * (0.5 * (osc + 1)); 53 | sp_voc_set_tongue_shape(ud->voc, osc, 2.9); 54 | } 55 | sp_voc_compute(sp, ud->voc, &voc); 56 | sp->out[0] = voc; 57 | } 58 | 59 | int main() { 60 | UserData ud; 61 | sp_data *sp; 62 | sp_create(&sp); 63 | sp_srand(sp, 1234567); 64 | 65 | sp_voc_create(&ud.voc); 66 | sp_osc_create(&ud.osc); 67 | sp_ftbl_create(sp, &ud.ft, 2048); 68 | 69 | sp_voc_init(sp, ud.voc); 70 | sp_gen_sine(sp, ud.ft); 71 | sp_osc_init(sp, ud.osc, ud.ft, 0); 72 | ud.osc->amp = 1; 73 | ud.osc->freq = 0.1; 74 | 75 | sp->len = 44100 * 5; 76 | sp_process(sp, &ud, process); 77 | 78 | sp_voc_destroy(&ud.voc); 79 | sp_ftbl_destroy(&ud.ft); 80 | sp_osc_destroy(&ud.osc); 81 | 82 | sp_destroy(&sp); 83 | return 0; 84 | } 85 | 86 | @ \subsec{Soundpipe Unit Test} 87 | The prototypical soundpipe unit test will fill a buffer of memory with 88 | samples. The md5 of this buffer is taken, and then compared with a 89 | reference md5. If they match, the signal is sample-accurately identical 90 | to the reference and the test passes. A test that does not pass can mean 91 | any number of things went wrong, and indicates that the module should be 92 | seriously looked at it. 93 | 94 | @(t_voc.c@>= 95 | 96 | #include "soundpipe.h" 97 | #include "md5.h" 98 | #include "tap.h" 99 | #include "test.h" 100 | 101 | typedef struct { 102 | sp_voc *voc; 103 | sp_osc *osc; 104 | sp_ftbl *ft; 105 | } UserData; 106 | 107 | int t_voc(sp_test *tst, sp_data *sp, const char *hash) 108 | { 109 | uint32_t n; 110 | UserData ud; 111 | 112 | int fail = 0; 113 | SPFLOAT osc, voc; 114 | sp_voc_create(&ud.voc); 115 | sp_osc_create(&ud.osc); 116 | sp_ftbl_create(sp, &ud.ft, 2048); 117 | 118 | sp_voc_init(sp, ud.voc); 119 | sp_gen_sine(sp, ud.ft); 120 | sp_osc_init(sp, ud.osc, ud.ft, 0); 121 | ud.osc->amp = 1; 122 | ud.osc->freq = 0.1; 123 | 124 | for(n = 0; n < tst->size; n++) { 125 | /* compute samples and add to test buffer */ 126 | osc = 0; 127 | voc = 0; 128 | sp_osc_compute(sp, ud.osc, NULL, &osc); 129 | if(sp_voc_get_counter(ud.voc) == 0) { 130 | osc = 12 + 16 * (0.5 * (osc + 1)); 131 | sp_voc_set_tongue_shape(ud.voc, osc, 2.9); 132 | } 133 | sp_voc_compute(sp, ud.voc, &voc); 134 | sp_test_add_sample(tst, voc); 135 | } 136 | 137 | fail = sp_test_verify(tst, hash); 138 | 139 | sp_voc_destroy(&ud.voc); 140 | sp_ftbl_destroy(&ud.ft); 141 | sp_osc_destroy(&ud.osc); 142 | 143 | if(fail) return SP_NOT_OK; 144 | else return SP_OK; 145 | } 146 | 147 | @ \subsec{Soundpipe Perfomance Test} 148 | 149 | The essence of a performance test in Soundpipe consists of running the 150 | compute function enough times so that some significant computation time 151 | is taken up. From there it is measured using a OS timing utility like 152 | {\tt time}, and saved to a log file. The timing information from this 153 | file can be plotted against other soundpipe module times, which can be useful 154 | to see how certain modules perform relative to others. 155 | 156 | @(p_voc.c@>= 157 | 158 | #include 159 | #include 160 | #include "soundpipe.h" 161 | #include "config.h" 162 | 163 | int main() { 164 | sp_data *sp; 165 | sp_create(&sp); 166 | sp_srand(sp, 12345); 167 | sp->sr = SR; 168 | sp->len = sp->sr * LEN; 169 | uint32_t t, u; 170 | SPFLOAT out = 0; 171 | 172 | sp_voc *unit[NUM]; 173 | 174 | for(u = 0; u < NUM; u++) { 175 | sp_voc_create(&unit[u]); 176 | sp_voc_init(sp, unit[u]); 177 | } 178 | 179 | for(t = 0; t < sp->len; t++) { 180 | for(u = 0; u < NUM; u++) sp_voc_compute(sp, unit[u], &out); 181 | } 182 | 183 | for(u = 0; u < NUM; u++) sp_voc_destroy(&unit[u]); 184 | 185 | sp_destroy(&sp); 186 | return 0; 187 | } 188 | -------------------------------------------------------------------------------- /sp/chant.sp: -------------------------------------------------------------------------------- 1 | _voc "./voc.so" fl 2 | 3 | 36 0.3 1 4 jitter + mtof 4 | 5 | 0.1 1 sine 0 1 biscale 6 | 0.9 7 | 0.9 8 | 0.3 1 sine 0 1 biscale 9 | _voc fe 36 mtof 70 5 eqfil 10 | 11 | dup dup 0.97 10000 revsc drop -14 ampdb * dcblk + 12 | 13 | _voc fc 14 | -------------------------------------------------------------------------------- /sp/rant.sp: -------------------------------------------------------------------------------- 1 | # It kind of sounds like an angry rant 2 | # _voc "./voc.so" fl 3 | 100 4 | 8 metro 0.3 maygate 200 * + 0.1 port 5 | 30 1 10 jitter + 6 | 7 | 0 1 3 randi 8 | 0 1 3 20 1 randi randi 9 | 0.7 10 | 0 11 | voc 12 | # _voc fe 13 | 14 | 1 metro 0.7 maygate 0.03 port * 15 | 16 | dup dup 1 2 8000 zrev drop -10 ampdb * + 17 | 18 | # _voc fc 19 | -------------------------------------------------------------------------------- /sp/test.sp: -------------------------------------------------------------------------------- 1 | _voc "./voc.so" fl 2 | # frequency 3 | 170 8 1 5 jitter + 4 | # tongue position 5 | 0 1 5 randi 6 | # tongue diameter 7 | 0 1 20 randi 8 | # breathiness 9 | 0.4 0.7 9 randi 10 | # velum amount 11 | 0 12 | _voc fe 13 | 14 | # Add reverberation using the Zita reverberator 15 | dup dup 1 2 8000 zrev drop -3 ampdb * + 16 | 17 | # close the plugin 18 | _voc fc 19 | -------------------------------------------------------------------------------- /sp/unya.sp: -------------------------------------------------------------------------------- 1 | _voc "./voc.so" fl 2 | _rate var 3 | _seq "0 2 4 7 9 11 12" gen_vals 4 | 5 | 15 inv 1 sine 0.3 3 biscale _rate set 6 | 7 | _rate get metro 1 _seq tseq 48 + 5 6 1 randi 1 sine 0.3 * + mtof 8 | 9 | _rate get metro 0.1 0.01 0.1 tenv 0.0 0.3 scale 10 | _rate get metro 0.1 0.1 0.3 tenv 0.0 _rate get metro 0.3 0.9 trand scale 11 | 0.8 12 | _rate get metro tog 13 | _voc fe 14 | 15 | 16 | dup dup 0.9 8000 revsc drop -14 ampdb * dcblk + 17 | 18 | _voc fc 19 | -------------------------------------------------------------------------------- /top.w: -------------------------------------------------------------------------------- 1 | @* Top-level Functions. 2 | 3 | Broadly speaking, the top-level functions are in charge of computing 4 | samples for the DSP inner-loop before, after, and during runtime. They get their 5 | name from the fact that they are the top level of abstraction in the program. 6 | These are the functions that get called in the Sporth Unit Generator 7 | implementation |@(ugen.c@>|. 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 | @ In the function |sp_voc_create|, an instance of Voc is created via |malloc|. 33 | 34 | @= 35 | int sp_voc_create(sp_voc **voc) 36 | { 37 | *voc = malloc(sizeof(sp_voc)); 38 | return SP_OK; 39 | } 40 | 41 | @ As a counterpart to |sp_voc_compute|, |sp_voc_destroy| frees all data 42 | previous allocated. 43 | 44 | @= 45 | int sp_voc_destroy(sp_voc **voc) 46 | { 47 | free(*voc); 48 | return SP_OK; 49 | } 50 | 51 | @ After data has been allocated with |sp_voc_create|, it must be initialized 52 | with |sp_voc_init|. 53 | 54 | @= 55 | int sp_voc_init(sp_data *sp, sp_voc *voc) 56 | { 57 | glottis_init(&voc->glot, sp->sr); /* initialize glottis */ 58 | tract_init(sp, &voc->tr); /* initialize vocal tract */ 59 | voc->counter = 0; 60 | return SP_OK; 61 | } 62 | 63 | @ The function |sp_voc_compute| is called during runtime to generate audio. 64 | This computation function will generate a single sample of audio and store it 65 | in the |SPFLOAT| pointer |*out|. 66 | 67 | @= 68 | int sp_voc_compute(sp_data *sp, sp_voc *voc, SPFLOAT *out) 69 | { 70 | SPFLOAT vocal_output, glot; 71 | SPFLOAT lambda1, lambda2; 72 | int i; 73 | 74 | @q vocal_output = 0; @> 75 | 76 | if(voc->counter == 0) { 77 | glottis_update(&voc->glot, voc->tr.block_time); 78 | tract_reshape(&voc->tr); 79 | tract_calculate_reflections(&voc->tr); 80 | for(i = 0; i < 512; i++) { 81 | vocal_output = 0; 82 | lambda1 = (SPFLOAT) i / 512; 83 | lambda2 = (SPFLOAT) (i + 0.5) / 512; 84 | glot = glottis_compute(sp, &voc->glot, lambda1); 85 | 86 | tract_compute(sp, &voc->tr, glot, lambda1); 87 | vocal_output += voc->tr.lip_output + voc->tr.nose_output; 88 | 89 | tract_compute(sp, &voc->tr, glot, lambda2); 90 | vocal_output += voc->tr.lip_output + voc->tr.nose_output; 91 | voc->buf[i] = vocal_output * 0.125; 92 | } 93 | } 94 | 95 | 96 | *out = voc->buf[voc->counter]; 97 | voc->counter = (voc->counter + 1) % 512; 98 | return SP_OK; 99 | } 100 | 101 | @ The function |sp_voc_compute_tract| computes the vocal tract component of 102 | Voc separately from the glottis. This provides the ability to use any input 103 | signal as an glottal excitation, turning the model into a formant filter. 104 | Compared to the main implementation in |@|, this function 105 | does not have the 512 sample delay. 106 | @= 107 | int sp_voc_tract_compute(sp_data *sp, sp_voc *voc, SPFLOAT *in, SPFLOAT *out) 108 | { 109 | SPFLOAT vocal_output; 110 | SPFLOAT lambda1, lambda2; 111 | 112 | if(voc->counter == 0) { 113 | tract_reshape(&voc->tr); 114 | tract_calculate_reflections(&voc->tr); 115 | } 116 | 117 | vocal_output = 0; 118 | lambda1 = (SPFLOAT) voc->counter / 512; 119 | lambda2 = (SPFLOAT) (voc->counter + 0.5) / 512; 120 | 121 | tract_compute(sp, &voc->tr, *in, lambda1); 122 | vocal_output += voc->tr.lip_output + voc->tr.nose_output; 123 | tract_compute(sp, &voc->tr, *in, lambda2); 124 | vocal_output += voc->tr.lip_output + voc->tr.nose_output; 125 | 126 | 127 | *out = vocal_output * 0.125; 128 | voc->counter = (voc->counter + 1) % 512; 129 | return SP_OK; 130 | } 131 | 132 | @ The function |sp_voc_set_frequency| sets the fundamental frequency 133 | for the glottal wave. 134 | 135 | @= 136 | void sp_voc_set_frequency(sp_voc *voc, SPFLOAT freq) 137 | { 138 | voc->glot.freq = freq; 139 | } 140 | 141 | @ The function |sp_voc_get_frequency_ptr| returns a pointer to the variable holding 142 | the frequency. This allows values to be set and read directly without. The 143 | use of a helper function. This function was notably created for use in a 144 | demo using the GUI library Nuklear. 145 | @= 146 | SPFLOAT * sp_voc_get_frequency_ptr(sp_voc *voc) 147 | { 148 | return &voc->glot.freq; 149 | } 150 | 151 | @ This getter function returns the cylindrical diameters representing 152 | tract. 153 | 154 | @= 155 | SPFLOAT* sp_voc_get_tract_diameters(sp_voc *voc) 156 | { 157 | return voc->tr.target_diameter; 158 | } 159 | 160 | @ Similar to |sp_voc_get_tract_diameters| in |@|, 161 | the function |sp_voc_get_current_tract_diameters| returns the diameters 162 | of the tract. The difference is that this function returns the 163 | actual slewed diameters used in |@|, rather than 164 | the target diameters. 165 | @= 166 | SPFLOAT* sp_voc_get_current_tract_diameters(sp_voc *voc) 167 | { 168 | return voc->tr.diameter; 169 | } 170 | 171 | @ This getter function returns the size of the vocal tract. 172 | @= 173 | int sp_voc_get_tract_size(sp_voc *voc) 174 | { 175 | return voc->tr.n; 176 | } 177 | 178 | @ This function returns the cylindrical diameters of the nasal cavity. 179 | @= 180 | 181 | SPFLOAT* sp_voc_get_nose_diameters(sp_voc *voc) 182 | { 183 | return voc->tr.nose_diameter; 184 | } 185 | 186 | @ This function returns the nose size. 187 | @= 188 | int sp_voc_get_nose_size(sp_voc *voc) 189 | { 190 | return voc->tr.nose_length; 191 | } 192 | 193 | @ The function |sp_voc_set_diameter()| is a function adopted from Neil Thapen's 194 | Pink Trombone in a function he called setRestDiameter. It is the main function 195 | in charge of the "tongue position" XY control. Modifications to the original 196 | function have been made in an attempt to make the function more generalized. 197 | Instead of relying on internal state, all variables used are parameters in 198 | the function. Because of this fact, there are quite a few function 199 | parameters: 200 | 201 | \item{$\bullet$} {\bf voc}, the core Voc data struct 202 | \item{$\bullet$} {\bf blade\_start}, index where the blade (?) starts. 203 | this is set to 10 in pink trombone 204 | \item{$\bullet$} {\bf lip\_start}, index where lip starts. this constant is 205 | set to 39. 206 | \item{$\bullet$} {\bf tip\_start}, this is set to 32. 207 | \item{$\bullet$} {\bf tongue\_index}, nominal range [12 .. 29] 208 | \item{$\bullet$} {\bf tongue\_diameter}, nominal range [2 .. 3.5] 209 | \item{$\bullet$} {\bf diameters}, the floating point array to write to 210 | 211 | For practical use cases, it is not ideal to call this function directly. 212 | Instead, it can be indirectly called using a more sane function 213 | |sp_voc_set_tongue_shape()|, found in the section |@|. 214 | 215 | @= 216 | void sp_voc_set_diameters(sp_voc *voc, @/ 217 | int blade_start, @/ 218 | int lip_start, @/ 219 | int tip_start, @/ 220 | SPFLOAT tongue_index,@/ 221 | SPFLOAT tongue_diameter, @/ 222 | SPFLOAT *diameters) { 223 | 224 | SPFLOAT grid_offset = 1.7; 225 | SPFLOAT fixed_tongue_diameter = 2+(tongue_diameter-2)/1.5; 226 | SPFLOAT tongue_amplitude = (1.5 - fixed_tongue_diameter + grid_offset); 227 | int i; 228 | SPFLOAT t; 229 | SPFLOAT curve; 230 | 231 | for(i = blade_start; i < lip_start; i++) { 232 | t = 1.1 * M_PI * 233 | (SPFLOAT)(tongue_index - i)/(tip_start - blade_start); 234 | fixed_tongue_diameter = 2+(tongue_diameter-2)/1.5; 235 | curve = tongue_amplitude * cos(t); 236 | if(i == lip_start - 1) curve *= 0.8; 237 | if(i == blade_start || i == lip_start - 2) curve *= 0.94; 238 | diameters[i] = 1.5 - curve; 239 | } 240 | } 241 | 242 | 243 | @ The function |sp_voc_set_tongue_shape()| will set the shape of the 244 | tongue using the two primary arguments |tongue_index| and |tongue_diameter|. 245 | It is a wrapper around the function described in |@|, 246 | filling in the constants used, and thereby making it simpler to work with. 247 | 248 | A few tract shapes shaped using this function have been generated below: 249 | 250 | \displayfig{plots/tongueshape1.eps} 251 | 252 | \displayfig{plots/tongueshape2.eps} 253 | 254 | \displayfig{plots/tongueshape3.eps} 255 | 256 | @= 257 | 258 | void sp_voc_set_tongue_shape(sp_voc *voc, 259 | SPFLOAT tongue_index, @/ 260 | SPFLOAT tongue_diameter) { 261 | SPFLOAT *diameters; 262 | diameters = sp_voc_get_tract_diameters(voc); 263 | sp_voc_set_diameters(voc, 10, 39, 32, 264 | tongue_index, tongue_diameter, diameters); 265 | } 266 | 267 | @ Voc keeps an internal counter for control rate operations called inside 268 | of the audio-rate compute function in |@|. The function 269 | |sp_voc_get_counter()| gets the current counter position. When the counter 270 | is 0, the next call to |sp_voc_compute| will compute another block of audio. 271 | Getting the counter position before the call allows control-rate variables 272 | to be set before then. 273 | 274 | @= 275 | 276 | int sp_voc_get_counter(sp_voc *voc) 277 | { 278 | return voc->counter; 279 | } 280 | 281 | @ The function |sp_voc_set_glottis_enable| controls the on/off state of 282 | the glottis. Attack and release envelopes are applied on transitions. 283 | @= 284 | void sp_voc_set_glottis_enable(sp_voc *voc, int enable) 285 | { 286 | voc->glot.enable = enable; 287 | } 288 | 289 | @ The function |sp_voc_set_tenseness| is used to set the tenseness variable, 290 | used when calculating glottal time coefficients in 291 | |@|, and is the main factor in calculating 292 | aspiration noise in |@|. Typically this is a value 293 | between 0 and 1. A value of 1 gives a full vocal sound, while a value of 0 294 | is all breathy. It is ideal to have a little bit of aspiration noise. 295 | Empirically good values tend to be in the range of $[0.6,0.9]$. 296 | 297 | @= 298 | void sp_voc_set_tenseness(sp_voc *voc, SPFLOAT tenseness) 299 | { 300 | voc->glot.tenseness = tenseness; 301 | } 302 | 303 | @ The function |sp_voc_get_tenseness_ptr| returns an |SPFLOAT| pointer to the 304 | parameter value directly controlling tenseness. This function is useful for 305 | GUI frontends that use direct pointer manipulation like Nuklear, the 306 | cross-platform UI framework used to make a demo for Voc. 307 | 308 | @= 309 | SPFLOAT * sp_voc_get_tenseness_ptr(sp_voc *voc) 310 | { 311 | return &voc->glot.tenseness; 312 | } 313 | 314 | @ The function |sp_voc_set_velum| sets the {\it velum}, or soft pallette of 315 | tract model. In the original implementation, the default value is 0.01, and 316 | set to a value of 0.04 to get a nasally sound. 317 | 318 | @= 319 | void sp_voc_set_velum(sp_voc *voc, SPFLOAT velum) 320 | { 321 | voc->tr.velum_target = velum; 322 | } 323 | 324 | @ The function |sp_voc_get_velum_ptr| returns the pointer associated with 325 | the velum, allowing direct control of the velum parameter. This function was 326 | created for use with a demo requiring direct access. 327 | 328 | @= 329 | 330 | SPFLOAT *sp_voc_get_velum_ptr(sp_voc *voc) 331 | { 332 | return &voc->tr.velum_target; 333 | } 334 | 335 | -------------------------------------------------------------------------------- /tract.w: -------------------------------------------------------------------------------- 1 | @* The Vocal Tract. 2 | The vocal tract is the part of the vocal model which takes the 3 | excitation signal (the glottis) and produces the vowel formants from it. 4 | 5 | The two main functions for the vocal tract consist of of an initialization 6 | function |tract_init| called once before runtime, and a computation 7 | function |tract_compute| called at twice the sampling rate. See 8 | |@| and |@| for more 9 | detail. 10 | 11 | @= 12 | @@/ 13 | @@/ 14 | @@/ 15 | @@/ 16 | @@/ 17 | @@/ 18 | 19 | @ The function |tract_init| is responsible for zeroing out variables 20 | and buffers, as well as setting up constants. 21 | 22 | @= 23 | static void tract_init(sp_data *sp, tract *tr) 24 | { 25 | int i; 26 | SPFLOAT diameter, d; /* needed to set up diameter arrays */@/ 27 | @@/ 28 | @@/ 29 | @@/ 30 | @@/ 31 | 32 | tract_calculate_reflections(tr); 33 | tract_calculate_nose_reflections(tr); 34 | tr->nose_diameter[0] = tr->velum_target; 35 | 36 | tr->block_time = 512.0 / (SPFLOAT)sp->sr; 37 | tr->T = 1.0 / (SPFLOAT)sp->sr; 38 | @ 39 | } 40 | 41 | @ @= 42 | tr->n = 44; 43 | tr->nose_length = 28; 44 | tr->nose_start = 17; 45 | 46 | tr->reflection_left = 0.0; 47 | tr->reflection_right = 0.0; 48 | tr->reflection_nose = 0.0; 49 | tr->new_reflection_left = 0.0; 50 | tr->new_reflection_right= 0.0; 51 | tr->new_reflection_nose = 0.0; 52 | tr->velum_target = 0.01; 53 | tr->glottal_reflection = 0.75; 54 | tr->lip_reflection = -0.85; 55 | tr->last_obstruction = -1; 56 | tr->movement_speed = 15; 57 | tr->lip_output = 0; 58 | tr->nose_output = 0; 59 | tr->tip_start = 32; 60 | 61 | @ Several floating-point arrays are needed for the scattering junctions. 62 | C does not zero these out by default. Below, the standard function 63 | |memset()| from |string.h| is used to zero out each of the blocks of memory. 64 | @= 65 | memset(tr->diameter, 0, tr->n * sizeof(SPFLOAT)); 66 | memset(tr->rest_diameter, 0, tr->n * sizeof(SPFLOAT)); 67 | memset(tr->target_diameter, 0, tr->n * sizeof(SPFLOAT)); 68 | memset(tr->L, 0, tr->n * sizeof(SPFLOAT)); 69 | memset(tr->R, 0, tr->n * sizeof(SPFLOAT)); 70 | memset(tr->reflection, 0, (tr->n + 1) * sizeof(SPFLOAT)); 71 | memset(tr->new_reflection, 0, (tr->n + 1) * sizeof(SPFLOAT)); 72 | memset(tr->junction_outL, 0, (tr->n + 1) * sizeof(SPFLOAT)); 73 | memset(tr->junction_outR, 0, (tr->n + 1) * sizeof(SPFLOAT)); 74 | memset(tr->A, 0, tr->n * sizeof(SPFLOAT)); 75 | memset(tr->noseL, 0, tr->nose_length * sizeof(SPFLOAT)); 76 | memset(tr->noseR, 0, tr->nose_length * sizeof(SPFLOAT)); 77 | memset(tr->nose_junc_outL, 0, (tr->nose_length + 1) * sizeof(SPFLOAT)); 78 | memset(tr->nose_junc_outR, 0, (tr->nose_length + 1) * sizeof(SPFLOAT)); 79 | memset(tr->nose_diameter, 0, tr->nose_length * sizeof(SPFLOAT)); 80 | memset(tr->noseA, 0, tr->nose_length * sizeof(SPFLOAT)); 81 | 82 | @ The cylindrical diameters approximating the vocal tract are set up 83 | below. These diameters will be modified and shaped by user control to 84 | shape the vowel sound. 85 | 86 | The initial shape of the vocal tract is plotted below: 87 | 88 | \displayfig{plots/tract.eps} 89 | 90 | @= 91 | for(i = 0; i < tr->n; i++) { 92 | diameter = 0; 93 | if(i < 7 * (SPFLOAT)tr->n / 44 - 0.5) { 94 | diameter = 0.6; 95 | } else if( i < 12 * (SPFLOAT)tr->n / 44) { 96 | diameter = 1.1; 97 | } else { 98 | diameter = 1.5; 99 | } 100 | 101 | tr->diameter[i] = 102 | tr->rest_diameter[i] = 103 | tr->target_diameter[i] = diameter; 104 | 105 | } 106 | 107 | @ The cylindrical diameters representing nose are set up. These are only 108 | set once, and are immutable for the rest of the program. 109 | 110 | The shape of the nasal passage is plotted below: 111 | 112 | \displayfig{plots/nose.eps} 113 | 114 | % TODO: use gnuplot to generate picture of what it looks like 115 | @= 116 | for(i = 0; i < tr->nose_length; i++) { 117 | d = 2 * ((SPFLOAT)i / tr->nose_length); 118 | if(d < 1) { 119 | diameter = 0.4 + 1.6 * d; 120 | } else { 121 | diameter = 0.5 + 1.5*(2-d); 122 | } 123 | diameter = MIN(diameter, 1.9); 124 | tr->nose_diameter[i] = diameter; 125 | } 126 | 127 | @ The vocal tract computation function computes a single sample of audio. 128 | As the original implementation describes it, this function is designed 129 | to run at twice the sampling rate. For this reason, it is called twice 130 | in the top level call back (see |@|). 131 | 132 | |tract_compute| has two input arguments. The variable |in| 133 | is the glottal excitation signal. The |lambda| variable is a coefficient 134 | for a linear crossfade along the buffer block, used for parameter smoothing. 135 | @= 136 | static void tract_compute(sp_data *sp, tract *tr, 137 | SPFLOAT @, in, 138 | SPFLOAT @, lambda) 139 | { 140 | @/ SPFLOAT @, r, w; 141 | int i; 142 | SPFLOAT @, amp; 143 | int current_size; 144 | transient_pool *pool; 145 | transient *n; 146 | 147 | @/ 148 | @@/ 149 | @@/ 150 | @@/ 151 | @@/ 152 | @@/ 153 | @@/ 154 | } 155 | 156 | @ A derivation of $w$ can be seen in section 2.5.2 of Jack Mullens 157 | PhD dissertation {\it Physical Modelling of the Vocal Tract 158 | with the 2D Digital Waveguide Mesh}. 159 | \cite{mullen2006physical} 160 | @= 161 | tr->junction_outR[0] = tr->L[0] * tr->glottal_reflection + in; 162 | tr->junction_outL[tr->n] = tr->R[tr->n - 1] * tr->lip_reflection; 163 | 164 | for(i = 1; i < tr->n; i++) { 165 | r = tr->reflection[i] * (1 - lambda) + tr->new_reflection[i] * lambda; 166 | w = r * (tr->R[i - 1] + tr->L[i]); 167 | tr->junction_outR[i] = tr->R[i - 1] - w; 168 | tr->junction_outL[i] = tr->L[i] + w; 169 | } 170 | 171 | @ @= 172 | i = tr->nose_start; 173 | r = tr->new_reflection_left * (1-lambda) + tr->reflection_left*lambda; 174 | tr->junction_outL[i] = r*tr->R[i-1] + (1+r)*(tr->noseL[0]+tr->L[i]); 175 | r = tr->new_reflection_right * (1 - lambda) + tr->reflection_right * lambda; 176 | tr->junction_outR[i] = r*tr->L[i] + (1+r)*(tr->R[i-1]+tr->noseL[0]); 177 | r = tr->new_reflection_nose * (1 - lambda) + tr->reflection_nose * lambda; 178 | tr->nose_junc_outR[0] = r * tr->noseL[0]+(1+r)*(tr->L[i]+tr->R[i-1]); 179 | 180 | @ @= 181 | for(i = 0; i < tr->n; i++) { 182 | tr->R[i] = tr->junction_outR[i]*0.999; 183 | tr->L[i] = tr->junction_outL[i + 1]*0.999; 184 | } 185 | tr->lip_output = tr->R[tr->n - 1]; 186 | 187 | @ @= 188 | tr->nose_junc_outL[tr->nose_length] = 189 | tr->noseR[tr->nose_length-1] * tr->lip_reflection; 190 | 191 | for(i = 1; i < tr->nose_length; i++) { 192 | w = tr->nose_reflection[i] * (tr->noseR[i-1] + tr->noseL[i]); 193 | tr->nose_junc_outR[i] = tr->noseR[i - 1] - w; 194 | tr->nose_junc_outL[i] = tr->noseL[i] + w; 195 | } 196 | 197 | @ @= 198 | for(i = 0; i < tr->nose_length; i++) { 199 | tr->noseR[i] = tr->nose_junc_outR[i]; 200 | tr->noseL[i] = tr->nose_junc_outL[i + 1]; 201 | } 202 | tr->nose_output = tr->noseR[tr->nose_length - 1]; 203 | 204 | @ The function |tract_calculate_reflections| computes reflection 205 | coefficients used in the scattering junction. Because this is a rather 206 | computationally expensive function, it is called once per render block, 207 | and then smoothed. 208 | 209 | First, the cylindrical areas of tract section are computed by squaring 210 | the diameters, they are stored in the struct variable |A|. 211 | 212 | Using the areas calculated, the reflections are calculated using the following 213 | formula: 214 | 215 | $$R_i = {A_{i - 1} - A_{i} \over A_{i - 1} + A_i} $$ 216 | 217 | To prevent some divide-by-zero edge cases, when $A_i$ is exactly zero, it 218 | is set to be $0.999$. 219 | 220 | From there, the new coefficients are set. %TODO: elaborate 221 | 222 | % TODO: The following eqn above will be derived by Julius. would be nice to 223 | % have! 224 | @= 225 | static void tract_calculate_reflections(tract *tr) 226 | { 227 | int i; 228 | SPFLOAT @, sum; @/ 229 | 230 | for(i = 0; i < tr->n; i++) { 231 | tr->A[i] = tr->diameter[i] * tr->diameter[i]; 232 | /* Calculate area from diameter squared*/ 233 | } 234 | 235 | for(i = 1; i < tr->n; i++) { 236 | tr->reflection[i] = tr->new_reflection[i]; 237 | if(tr->A[i] == 0) { 238 | tr->new_reflection[i] = 0.999; /* to prevent bad behavior if 0 */ 239 | } else { 240 | tr->new_reflection[i] = 241 | (tr->A[i - 1] - tr->A[i]) / (tr->A[i - 1] + tr->A[i]); 242 | } 243 | } 244 | 245 | tr->reflection_left = tr->new_reflection_left; 246 | tr->reflection_right = tr->new_reflection_right; 247 | tr->reflection_nose = tr->new_reflection_nose; 248 | 249 | sum = tr->A[tr->nose_start] + tr->A[tr->nose_start + 1] + tr->noseA[0]; 250 | tr->new_reflection_left = (SPFLOAT)(2 * tr->A[tr->nose_start] - sum) / sum; 251 | tr->new_reflection_right = (SPFLOAT)(2 * tr->A[tr->nose_start + 1] - sum) / sum; 252 | tr->new_reflection_nose = (SPFLOAT)(2 * tr->noseA[0] - sum) / sum; 253 | } 254 | 255 | @ Similar to |tract_calculate_reflections|, this function computes 256 | reflection coefficients for the nasal scattering junction. For more 257 | information on the math that is happening, see 258 | |@|. 259 | % TODO: is "nasal scattering junction" the proper terminology? 260 | @= 261 | static void tract_calculate_nose_reflections(tract *tr) 262 | { 263 | int i; 264 | 265 | for(i = 0; i < tr->nose_length; i++) { 266 | tr->noseA[i] = tr->nose_diameter[i] * tr->nose_diameter[i]; 267 | } 268 | 269 | for(i = 1; i < tr->nose_length; i++) { 270 | tr->nose_reflection[i] = (tr->noseA[i - 1] - tr->noseA[i]) / 271 | (tr->noseA[i-1] + tr->noseA[i]); 272 | } 273 | } 274 | 275 | @ %TODO: Explain these functions. 276 | 277 | @= 278 | 279 | static void tract_reshape(tract *tr) 280 | { 281 | SPFLOAT amount; 282 | SPFLOAT slow_return; 283 | SPFLOAT diameter; 284 | SPFLOAT target_diameter; 285 | int i; 286 | int current_obstruction; 287 | 288 | current_obstruction = -1; 289 | amount = tr->block_time * tr->movement_speed; 290 | 291 | for(i = 0; i < tr->n; i++) { 292 | slow_return = 0; 293 | diameter = tr->diameter[i]; 294 | target_diameter = tr->target_diameter[i]; 295 | 296 | if(diameter < 0.001) current_obstruction = i; 297 | 298 | if(i < tr->nose_start) slow_return = 0.6; 299 | else if(i >= tr->tip_start) slow_return = 1.0; 300 | else { 301 | slow_return = 302 | 0.6+0.4*(i - tr->nose_start)/(tr->tip_start - tr->nose_start); 303 | } 304 | 305 | tr->diameter[i] = move_towards(diameter, target_diameter, 306 | slow_return * amount, 2 * amount); 307 | 308 | } 309 | 310 | if(tr->last_obstruction > -1 && current_obstruction == -1 && 311 | tr->noseA[0] < 0.05) { 312 | append_transient(&tr->tpool, tr->last_obstruction); 313 | } 314 | tr->last_obstruction = current_obstruction; 315 | 316 | tr->nose_diameter[0] = move_towards(tr->nose_diameter[0], tr->velum_target, 317 | amount * 0.25, amount * 0.1); 318 | tr->noseA[0] = tr->nose_diameter[0] * tr->nose_diameter[0]; 319 | } 320 | 321 | @ In Pink Trombone, there is a special handling of diameters that are exactly 322 | zero. From a physical point of view, air is completly blocked, and this 323 | obstruction of air produces a transient "click" sound. To simulate this, 324 | any obstructions are noted during the reshaping of the vocal tract 325 | (see |@|), and the latest obstruction position is noted and pushed 326 | onto a stack of transients. During the vocal tract computation, the exponential 327 | damping contributes to the overal amplitude of the left-going and right-going 328 | delay lines at that precise diameter location. This can be seen in the section 329 | |@|. 330 | 331 | % TODO: Wouldn't it be nice to have a plot of what the transient looks like? 332 | 333 | @= 334 | @@/ 335 | @@/ 336 | 337 | @ The transient pool is initialized inside along with the entire vocal tract 338 | inside of |@|. It essentially sets the pool 339 | to a size of zero and that the first available free transient is at index "0". 340 | 341 | The transients in the pool will all have their boolean variable |is_free|, 342 | set to be true so that they can be in line to be selected. 343 | 344 | To remove any valgrind issues related to unitialized variables, {\it all} 345 | the members in the |transient| data struct are set to some parameter. 346 | 347 | @= 348 | tr->tpool.size = 0; 349 | tr->tpool.next_free = 0; 350 | for(i = 0; i < MAX_TRANSIENTS; i++) { 351 | tr->tpool.pool[i].is_free = 1; 352 | tr->tpool.pool[i].id = i; 353 | tr->tpool.pool[i].position = 0; 354 | tr->tpool.pool[i].time_alive = 0; 355 | tr->tpool.pool[i].strength = 0; 356 | tr->tpool.pool[i].exponent = 0; 357 | } 358 | 359 | @ Any obstructions noted during |@| must be appended to the list 360 | of previous transients. 361 | The function will return a 0 on failure, and a 1 on success. 362 | 363 | Here is an overview of how a transient may get appended: 364 | \item{0.} Check and see if the pool is full. If this is so, return 0. 365 | \item{1.} If there is no recorded next free (the id is -1), search for 366 | one using brute force and check for any free transients. If none can be 367 | found, return 0. Since |MAX_TRANSIENTS| is a low N, even the worst-case 368 | searches do not pose a significant performance penalty. 369 | \item{2.} With a transient found, assign the current root of the list to be 370 | the next value in the transient. (It does not matter if the root is NULL, 371 | because the size of the list will prevent it from ever being accessed.) 372 | \item{3.} Increase the size of the pool by 1. 373 | \item{4.} Toggle the |is_free| boolean of the current transient to be false. 374 | \item{5.} Set the |position|. 375 | \item{6.} Set the |time_alive| to be zero seconds. 376 | \item{7.} Set the |lifetime| to be 200ms, or 0.2 seconds. 377 | \item{8.} Set the |strength| to an amplitude 0.3. 378 | \item{9.} Set the |exponent| parameter to be 200. 379 | \item{10.} Set the |next_free| parameter to be $-1$. 380 | 381 | @= 382 | static int append_transient(transient_pool *pool, int position) 383 | { 384 | int i; 385 | int free_id; 386 | transient *t; 387 | 388 | free_id = pool->next_free; 389 | if(pool->size == MAX_TRANSIENTS) return 0; 390 | 391 | if(free_id == -1) { 392 | for(i = 0; i < MAX_TRANSIENTS; i++) { 393 | if(pool->pool[i].is_free) { 394 | free_id = i; 395 | break; 396 | } 397 | } 398 | } 399 | 400 | if(free_id == -1) return 0; 401 | 402 | t = &pool->pool[free_id]; 403 | t->next = pool->root; 404 | pool->root = t; 405 | pool->size++; 406 | t->is_free = 0; 407 | t->time_alive = 0; 408 | t->lifetime = 0.2; 409 | t->strength = 0.3; 410 | t->exponent = 200; 411 | t->position = position; 412 | pool->next_free = -1; 413 | return 0; 414 | } 415 | 416 | @ When a transient has lived it's lifetime, it must be removed from the list of 417 | transients. To keep things sane, transients have a unique ID for identification. 418 | This is preferred to comparing pointer addresses. While more efficient, this 419 | method is prone to subtle implementation errors. 420 | 421 | The method for removing a transient from a linked list is fairly typical: 422 | 423 | \item{0.} If the transient *is* the root, set the root to be the next value. 424 | Decrease the size by one, and return. 425 | \item{1.} Iterate through the list and search for the entry. 426 | \item{2.} Once the entry has been found, decrease the pool size by 1. 427 | \item{3.} The transient, now free for reuse, can now be toggled to be free, 428 | and it can be the next variable ready to be used again. 429 | @= 430 | 431 | static void remove_transient(transient_pool *pool, unsigned int id) 432 | { 433 | int i; 434 | transient *n; 435 | 436 | pool->next_free = id; 437 | n = pool->root; 438 | if(id == n->id) { 439 | pool->root = n->next; 440 | pool->size--; 441 | return; 442 | } 443 | 444 | for(i = 0; i < pool->size; i++) { 445 | if(n->next->id == id) { 446 | pool->size--; 447 | n->next->is_free = 1; 448 | n->next = n->next->next; 449 | break; 450 | } 451 | n = n->next; 452 | } 453 | } 454 | 455 | @ Transients are processed during |@|. The transient 456 | list is iterated through, their contributions are made to the Left and Right 457 | delay lines. 458 | 459 | In this implementation, the transients in the list are iterated through, and 460 | their contributions are calculated using the following exponential function: 461 | 462 | $$A = s2^{-E_0 * t}$$ 463 | 464 | Where: 465 | \item{$\bullet$} $A$ is the contributing amplitude to the left and right-going 466 | components. 467 | \item{$\bullet$} $s$ is the overall strength of the transient. 468 | \item{$\bullet$} $E_0$ is the exponent variable constant. 469 | \item{$\bullet$} $t$ is the time alive. 470 | 471 | This particular function also must check for any transients that need to 472 | be removed, and removes them. Some caution must be made to make sure that 473 | this is done properly. Because a call to |remove_transient| changes the 474 | size of the pool, a copy of the current size is copied to a variable for the 475 | for loop. 476 | Since the list iterates in order, it is presumably 477 | safe to remove values from the list while the list is iterating. 478 | 479 | 480 | @= 481 | pool = &tr->tpool; 482 | current_size = pool->size; 483 | n = pool->root; 484 | for(i = 0; i < current_size; i++) { 485 | amp = n->strength * pow(2, -1.0 * n->exponent * n->time_alive); 486 | tr->L[n->position] += amp * 0.5; 487 | tr->R[n->position] += amp * 0.5; 488 | n->time_alive += tr->T * 0.5; 489 | if(n->time_alive > n->lifetime) { 490 | remove_transient(pool, n->id); 491 | } 492 | n = n->next; 493 | } 494 | -------------------------------------------------------------------------------- /ugen.w: -------------------------------------------------------------------------------- 1 | @* External Sporth Plugins. 2 | 3 | Sporth, a stack-based synthesis language, is the preferred tool of choice for 4 | sound design experimentation and prototyping with Voc. 5 | A version of Voc has been ported to Sporth as third party plugin, known 6 | as an {\it external Sporth Plugin}. 7 | 8 | 9 | \subsec{Sporth Plugins as Seen from Sporth} 10 | In Sporth, one has the ability to dynamically load custom unit-generators 11 | or, {\it ugens}, into Sporth. Such a unit generator can be seen here in 12 | Sporth code: 13 | 14 | \sporthcode{test} 15 | 16 | In the code above, the plugin file is loaded via \sword{fl} (function load) 17 | and saved into the table \sword{\_voc}. An instance of \sword{\_voc} is created 18 | with \sword{fe} (function execute). Finally, the dynamic plugin is closed 19 | with \sword{fc} (function close). 20 | 21 | \subsec{Sporth plugins as seen from C.} 22 | 23 | Custom unit generators are written in C using a special interface provided by 24 | the Sporth API. The functionality of an external sporth ugen is nearly identical 25 | to an internal one, with exceptions being the function definition 26 | and how custom user-data is handled. Besides that, they can be seen as 27 | equivalent. 28 | 29 | The entirety of the Sporth unit generator is contained within 30 | a single subroutine, declared |static| so as to not clutter the global 31 | namespace. The crux of the function is a case switch outlining four unique 32 | states of operation, which define the {\it lifecycle} of a Sporth ugen. This 33 | design concept comes from Soundpipe, the music DSP library that Sporth 34 | is built on top of. 35 | 36 | These states are executed in this order: 37 | 38 | \begingroup 39 | \smallskip 40 | \leftskip=4pc 41 | \item{1.} Create: allocates memory for the DSP module 42 | \item{2.} Initialize: zeros out and sets up default values 43 | \item{3.} Compute: Computes an audio-rate sample (or samples) 44 | \item{4.} Destroy: frees all memory previously allocated in Create 45 | \par 46 | \endgroup 47 | 48 | Create and init are called once during runtime, compute is called as many 49 | times as needed while the program is running, and destroy is called 50 | once when the program is stopped. 51 | 52 | The code below shows the outline for the main Sporth Ugen. 53 | 54 | @(ugen.c@>= 55 | #include 56 | #include 57 | #include 58 | #ifdef BUILD_SPORTH_PLUGIN 59 | #include 60 | #include 61 | #include "voc.h" 62 | #else 63 | #include "plumber.h" 64 | #endif 65 | 66 | #ifdef BUILD_SPORTH_PLUGIN 67 | static int sporth_voc(plumber_data *pd, sporth_stack *stack, void **ud) 68 | #else 69 | int sporth_voc(sporth_stack *stack, void *ud) 70 | #endif 71 | { 72 | sp_voc *voc; 73 | SPFLOAT out; 74 | SPFLOAT freq; 75 | SPFLOAT pos; 76 | SPFLOAT diameter; 77 | SPFLOAT tenseness; 78 | SPFLOAT nasal; 79 | #ifndef BUILD_SPORTH_PLUGIN 80 | plumber_data *pd; 81 | pd = ud; 82 | #endif 83 | 84 | 85 | switch(pd->mode) { 86 | case PLUMBER_CREATE:@/ 87 | @; 88 | break; 89 | case PLUMBER_INIT: @/ 90 | @; 91 | break; 92 | 93 | case PLUMBER_COMPUTE: @/ 94 | @; 95 | break; 96 | 97 | case PLUMBER_DESTROY: @/ 98 | @; 99 | break; 100 | } 101 | return PLUMBER_OK; 102 | } 103 | 104 | @@/ 105 | 106 | @ 107 | The first state executed is {\bf creation}, denoted by the macro 108 | |PLUMBER_CREATE|. This is the state where memory is allocated, tables are 109 | created and stack arguments are checked for validity. 110 | 111 | It is here that the top-level function |@| is called. 112 | 113 | @= 114 | 115 | sp_voc_create(&voc); 116 | #ifdef BUILD_SPORTH_PLUGIN 117 | *ud = voc; 118 | #else 119 | plumber_add_ugen(pd, SPORTH_VOC, voc); 120 | #endif 121 | if(sporth_check_args(stack, "fffff") != SPORTH_OK) { 122 | plumber_print(pd, "Voc: not enough arguments!\n"); 123 | } 124 | nasal = sporth_stack_pop_float(stack); 125 | tenseness = sporth_stack_pop_float(stack); 126 | diameter = sporth_stack_pop_float(stack); 127 | pos = sporth_stack_pop_float(stack); 128 | freq = sporth_stack_pop_float(stack); 129 | sporth_stack_push_float(stack, 0.0); 130 | 131 | @ The second state executed is {\bf initialization}, denoted by the macro 132 | |PLUMBER_INIT|. This is the state where variables get initalised or zeroed out. 133 | It should be noted that auxiliary memory can allocated here for things 134 | involving delay lines with user-specified sizes. For this reason, it is 135 | typically not safe to call this twice for reinitialization. (The author admits 136 | that this is not an ideal design choice.) 137 | 138 | It is here that the top-level function |@| is called. 139 | 140 | @= 141 | #ifdef BUILD_SPORTH_PLUGIN 142 | voc = *ud; 143 | #else 144 | voc = pd->last->ud; 145 | #endif 146 | sp_voc_init(pd->sp, voc); 147 | nasal = sporth_stack_pop_float(stack); 148 | tenseness = sporth_stack_pop_float(stack); 149 | diameter = sporth_stack_pop_float(stack); 150 | pos = sporth_stack_pop_float(stack); 151 | freq = sporth_stack_pop_float(stack); 152 | sporth_stack_push_float(stack, 0.0); 153 | 154 | @ The third state executed is {\bf computation}, denoted by the macro 155 | |PLUMBER_COMPUTE|. This state happens during Sporth runtime in the 156 | audio loop. Generally speaking, this is where a Ugen will process audio. 157 | In this state, strings in this callback are ignored; only 158 | floating point values are pushed and popped. 159 | 160 | It is here that the top-level function |@| is called. 161 | 162 | @= 163 | #ifdef BUILD_SPORTH_PLUGIN 164 | voc = *ud; 165 | #else 166 | voc = pd->last->ud; 167 | #endif 168 | nasal = sporth_stack_pop_float(stack); 169 | tenseness = sporth_stack_pop_float(stack); 170 | diameter = sporth_stack_pop_float(stack); 171 | pos = sporth_stack_pop_float(stack); 172 | freq = sporth_stack_pop_float(stack); 173 | sp_voc_set_frequency(voc, freq); 174 | sp_voc_set_tenseness(voc, tenseness); 175 | 176 | if(sp_voc_get_counter(voc) == 0) { 177 | sp_voc_set_velum(voc, 0.01 + 0.8 * nasal); 178 | sp_voc_set_tongue_shape(voc, 12 + 16.0 * pos, diameter * 3.5); 179 | } 180 | 181 | sp_voc_compute(pd->sp, voc, &out); 182 | sporth_stack_push_float(stack, out); 183 | 184 | @ The fourth and final state in a Sporth ugen is {\bf Destruction}, denoted 185 | by |PLUMBER_DESTROY|. Any memory allocated in |PLUMBER_CREATE| 186 | should be consequently freed here. 187 | 188 | It is here that the top-level function |@| is called. 189 | @= 190 | #ifdef BUILD_SPORTH_PLUGIN 191 | voc = *ud; 192 | #else 193 | voc = pd->last->ud; 194 | #endif 195 | sp_voc_destroy(&voc); 196 | 197 | @ A dynamically loaded sporth unit-generated such as the one defined here 198 | needs to have a globally accessible function called |sporth_return_ugen|. 199 | All this function needs to do is return the ugen function, which is of type 200 | |plumber_dyn_func|. 201 | @= 202 | #ifdef BUILD_SPORTH_PLUGIN 203 | @[plumber_dyn_func sporth_return_ugen() @] 204 | { 205 | return sporth_voc; 206 | } 207 | #endif 208 | 209 | @ \subsec{A Ugen for the Vocal Tract Model} 210 | @(ugen.c@> += 211 | 212 | #ifdef BUILD_SPORTH_PLUGIN 213 | static int sporth_tract(plumber_data *pd, sporth_stack *stack, void **ud) 214 | { 215 | sp_voc *voc; 216 | SPFLOAT out; 217 | SPFLOAT pos; 218 | SPFLOAT diameter; 219 | SPFLOAT nasal; 220 | SPFLOAT in; 221 | 222 | switch(pd->mode) { 223 | case PLUMBER_CREATE:@/ 224 | sp_voc_create(&voc); 225 | *ud = voc; 226 | if(sporth_check_args(stack, "ffff") != SPORTH_OK) { 227 | plumber_print(pd, "Voc: not enough arguments!\n"); 228 | } 229 | nasal = sporth_stack_pop_float(stack); 230 | diameter = sporth_stack_pop_float(stack); 231 | pos = sporth_stack_pop_float(stack); 232 | in = sporth_stack_pop_float(stack); 233 | 234 | sporth_stack_push_float(stack, 0.0); 235 | break; 236 | case PLUMBER_INIT: @/ 237 | voc = *ud; 238 | sp_voc_init(pd->sp, voc); 239 | nasal = sporth_stack_pop_float(stack); 240 | diameter = sporth_stack_pop_float(stack); 241 | pos = sporth_stack_pop_float(stack); 242 | in = sporth_stack_pop_float(stack); 243 | 244 | sporth_stack_push_float(stack, 0.0); 245 | break; 246 | case PLUMBER_COMPUTE: @/ 247 | voc = *ud; 248 | nasal = sporth_stack_pop_float(stack); 249 | diameter = sporth_stack_pop_float(stack); 250 | pos = sporth_stack_pop_float(stack); 251 | in = sporth_stack_pop_float(stack); 252 | 253 | if(sp_voc_get_counter(voc) == 0) { 254 | sp_voc_set_velum(voc, 0.01 + 0.8 * nasal); 255 | sp_voc_set_tongue_shape(voc, 12 + 16.0 * pos, diameter * 3.5); 256 | } 257 | 258 | sp_voc_tract_compute(pd->sp, voc, &in, &out); 259 | sporth_stack_push_float(stack, out); 260 | break; 261 | case PLUMBER_DESTROY: @/ 262 | voc = *ud; 263 | sp_voc_destroy(&voc); 264 | break; 265 | } 266 | 267 | return PLUMBER_OK; 268 | } 269 | #endif 270 | @ \subsec{A multi ugen plugin implementation} 271 | New Sporth developments contemporary with the creation of Voc have lead to 272 | the development of Sporth plugins with multiple ugens. 273 | 274 | @(ugen.c@>+= 275 | #ifdef BUILD_SPORTH_PLUGIN 276 | static const plumber_dyn_func sporth_functions[] = { 277 | sporth_voc, 278 | sporth_tract, 279 | }; 280 | 281 | int sporth_return_ugen_multi(int n, plumber_dyn_func *f) 282 | { 283 | if(n < 0 || n > 1) { 284 | return PLUMBER_NOTOK; 285 | } 286 | *f = sporth_functions[n]; 287 | return PLUMBER_OK; 288 | } 289 | #endif 290 | 291 | @* Sporth Code Examples. 292 | 293 | Here are some sporth code examples. 294 | 295 | \subsec{Chant} 296 | \sporthcode{chant} 297 | 298 | \subsec{Rant} 299 | \sporthcode{rant} 300 | 301 | \subsec{Unya} 302 | \sporthcode{unya} 303 | 304 | -------------------------------------------------------------------------------- /voc.lua: -------------------------------------------------------------------------------- 1 | sptbl["voc"] = { 2 | 3 | files = { 4 | module = "voc.c", 5 | header = "voc.h", 6 | example = "ex_voc.c", 7 | }, 8 | 9 | func = { 10 | create = "sp_voc_create", 11 | destroy = "sp_voc_destroy", 12 | init = "sp_voc_init", 13 | compute = "sp_voc_compute", 14 | }, 15 | 16 | params = { 17 | }, 18 | 19 | modtype = "module", 20 | 21 | description = [[A vocal tract physical model. 22 | 23 | Based on the Pink Trombone algorithm by Neil Thapen, Voc implements a physical 24 | model of the vocal tract glottal pulse wave. The tract model is based on the 25 | classic Kelly-Lochbaum 26 | segmented cylindrical 1d waveguide model, and the glottal pulse wave is a 27 | LF glottal pulse model. 28 | 29 | The soundpipe source code for Voc is generated via ctangle, one half of the 30 | literate documentation system known CWEB. The CWEB are maintained in a 31 | separate repository. They are hosted on github here: 32 | http://www.github.com/paulbatchelor/voc 33 | 34 | This documentation is a stub. For a full overview on proper usage, consult 35 | the "Top-level functions" section of the documented code, a copy of which 36 | can be found at the Voc project page pbat.ch/proj/voc, or generate the PDF 37 | from the github page described above. 38 | ]], 39 | 40 | ninputs = 0, 41 | noutputs = 1, 42 | 43 | inputs = { 44 | }, 45 | 46 | outputs = { 47 | { 48 | name = "out", 49 | description = "Voc output." 50 | }, 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /voc.w: -------------------------------------------------------------------------------- 1 | \input macros 2 | \input btxmac 3 | \input epsf 4 | 5 | \startcenter 6 | {\bigfont Voc} 7 | \medskip 8 | {\mediumfont A vocal tract physical model implementation.} 9 | \subsec{By Paul Batchelor} 10 | Git Hash: 11 | {\tt 12 | \input version 13 | } 14 | \medskip 15 | \stopcenter 16 | \epsfxsize=40pt 17 | \epsfbox{by-sa.eps} 18 | \vfil \break 19 | \bigheader{Introduction} 20 | 21 | The following document describes {\it Voc}, an implementation 22 | of a vocal tract physical model. 23 | 24 | \subsec{Motivations and Goals} 25 | The human voice is a powerful tool for any composer, second only to silence. 26 | Even an approximation of the voice 27 | can tap into the entire range of human emotion. This is why 28 | the wind howls, floorboards moan, or R2D2 pouts. For computer musicians and 29 | sound designers alike, creating sonic elements with vocal qualities can give 30 | cold digital sounds a human-like relatable quality; an excellent tool for 31 | engaging an audience. 32 | 33 | % Perhaps talk about "sporth talking on the phone with his mother" patch here 34 | 35 | The goal of {\it Voc} is to provide a low level model for producing utterances 36 | and phonemes. It will neither attempt to sing or talk, but it will babble 37 | and chatter. A program which is closely aligned with Voc's scope is Neil 38 | Thapen's web application 39 | {\it Pink Trombone}. \cite{pinktrombone} 40 | In this program, vocal phonemes are generated through directly manipulating a 41 | virtual vocal tract in continuous time. 42 | 43 | 44 | \subsec{Literate Programming} 45 | 46 | As an experiment, the author has decided to use {\it literate programming} for 47 | this project. Literate programming, created by Donald Knuth \cite{knuth1992literate}, 48 | is the concept of melting documentation and code together. What you are reading is also a 49 | program! 50 | 51 | The biggest advantage of using literate programming for this project is the 52 | ability to use mathematical notation to describe concepts that are implemented. 53 | The C-language does not lend itself well for comprehensibility when it comes 54 | to DSP, even with comments. Nobody ever learned about DSP from C code alone! 55 | A very successful example of literate programming is the book {\it Physically Based 56 | Rendering} \cite{pharr2016physically}, which is both a textbook and software implementation of a 57 | physically accurate ray tracer. 58 | 59 | The underlying technology used here is CWEB\cite{knuth1994cweb}, 60 | the definitive literate programming 61 | tool developed by Donald Knuth, with some minor macro adjustments for formatting. 62 | 63 | 64 | @* Overview. 65 | 66 | In a literate program, it is customary (and somewhat mandatory) to provide 67 | an "overview" section. This section serves as the entry point in generating 68 | the C amalgamation file |voc.c|. Complying with the constraints of |CWEB|, 69 | the corresponding sections will appear at the bottom of this section. 70 | 71 | \subsec{The Core Voc Components} 72 | 73 | |@| is the header section of the C file (not be confused with 74 | the separate header file |@(voc.h@>|. This is where all the system includes, 75 | macros, global data, and structs are declared. 76 | 77 | |@| is the component of Voc concerned with producing the glottal 78 | excitation signal. 79 | 80 | |@| is implementation of the physical waveguide of the 81 | vocal tract. 82 | 83 | |@| is the section consisting of all public functions for 84 | controlling Voc, from instantiation to parametric control. 85 | 86 | \subsec{Supplementary Files} 87 | 88 | In addition to the main C amalgamation, there are a few other files 89 | that this literate program generates: 90 | 91 | |@(debug.c@>| is the debug utility used extensively 92 | through out the development of Voc, used to debug and test out features. 93 | 94 | |@(voc.h@>| is the user-facing header file that goes 95 | along with the C amalgamation. Anyone wishing to use this program will need 96 | this header file along with the C file. 97 | 98 | |@(plot.c@>| is a program that generates dat files, which 99 | can then be fed into gnuplot for plotting. It is used to generate the plots 100 | you see in this document. 101 | 102 | |@(ugen.c@>| provides an implementation of Voc as a Sporth unit generator, 103 | offering 5 dimensions of control. In addition the main Sporth plugin, there 104 | are also smaller unit generators implementing portions of Voc, such as 105 | the vocal tract filter. 106 | 107 | {\tt voc.lua} (not generated but included with the source code) contains 108 | metadata needed to implement Voc as a Soundpipe module. 109 | 110 | |@(ex_voc.c@>| Is a small C program that uses Voc, written in the style 111 | of a classic Soundpipe example. 112 | 113 | |@(t_voc.c@>| Is a small C program written for the Soundpipe testing utility. 114 | 115 | |@(p_voc.c@>| Is a small C program written for the Soundpipe perfomance 116 | measurement utility. 117 | 118 | \medskip 119 | 120 | 121 | @c 122 | @@/ 123 | @@/ 124 | @@/ 125 | @@/ 126 | 127 | @i data.w 128 | 129 | @i top.w 130 | 131 | @i glottis.w 132 | 133 | @i tract.w 134 | 135 | @i header.w 136 | 137 | @i debug.w 138 | 139 | @i ugen.w 140 | 141 | @i sp.w 142 | 143 | @* References. 144 | \bibliography{ref} 145 | \bibliographystyle{plain} 146 | --------------------------------------------------------------------------------