aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pd/INSTALL.txt20
-rw-r--r--pd/LICENSE.txt31
-rw-r--r--pd/README.txt44
-rw-r--r--pd/bin/nada0
-rw-r--r--pd/doc/1.manual/1.introduction.txt19
-rw-r--r--pd/doc/1.manual/fig1.1.jpgbin0 -> 6794 bytes
-rw-r--r--pd/doc/1.manual/fig1.2.jpgbin0 -> 4361 bytes
-rw-r--r--pd/doc/1.manual/fig1.3.jpgbin0 -> 1056 bytes
-rw-r--r--pd/doc/1.manual/fig1.4.jpgbin0 -> 5965 bytes
-rw-r--r--pd/doc/1.manual/fig1.5.jpgbin0 -> 6187 bytes
-rw-r--r--pd/doc/1.manual/fig3.1.jpgbin0 -> 1201 bytes
-rw-r--r--pd/doc/1.manual/fig3.10.jpgbin0 -> 2884 bytes
-rw-r--r--pd/doc/1.manual/fig3.2.jpgbin0 -> 2932 bytes
-rw-r--r--pd/doc/1.manual/fig3.3.jpgbin0 -> 2177 bytes
-rw-r--r--pd/doc/1.manual/fig3.4.jpgbin0 -> 2359 bytes
-rw-r--r--pd/doc/1.manual/fig3.5.jpgbin0 -> 2029 bytes
-rw-r--r--pd/doc/1.manual/fig3.6.jpgbin0 -> 2977 bytes
-rw-r--r--pd/doc/1.manual/fig3.7.jpgbin0 -> 2846 bytes
-rw-r--r--pd/doc/1.manual/fig3.8.jpgbin0 -> 1267 bytes
-rw-r--r--pd/doc/1.manual/fig3.9.jpgbin0 -> 5708 bytes
-rw-r--r--pd/doc/1.manual/fig7.1.jpgbin0 -> 2410 bytes
-rw-r--r--pd/doc/1.manual/fig7.2.jpgbin0 -> 7327 bytes
-rw-r--r--pd/doc/1.manual/fig7.3.jpgbin0 -> 2588 bytes
-rw-r--r--pd/doc/1.manual/fig7.4.jpgbin0 -> 3245 bytes
-rw-r--r--pd/doc/1.manual/fig7.5.jpgbin0 -> 2490 bytes
-rw-r--r--pd/doc/1.manual/fig7.6.jpgbin0 -> 7758 bytes
-rw-r--r--pd/doc/1.manual/fig8.1.jpgbin0 -> 2551 bytes
-rw-r--r--pd/doc/1.manual/fig8.2.jpgbin0 -> 2414 bytes
-rw-r--r--pd/doc/1.manual/fig8.3.jpgbin0 -> 2036 bytes
-rw-r--r--pd/doc/1.manual/fig8.4.jpgbin0 -> 3428 bytes
-rw-r--r--pd/doc/1.manual/fig8.5.jpgbin0 -> 5182 bytes
-rw-r--r--pd/doc/1.manual/fig8.6.jpgbin0 -> 7549 bytes
-rw-r--r--pd/doc/1.manual/fig9.1.jpgbin0 -> 15267 bytes
-rw-r--r--pd/doc/1.manual/fig9.2.jpgbin0 -> 17390 bytes
-rw-r--r--pd/doc/1.manual/fig9.3.jpgbin0 -> 38881 bytes
-rw-r--r--pd/doc/1.manual/index.htm161
-rw-r--r--pd/doc/1.manual/x1.htm112
-rw-r--r--pd/doc/1.manual/x2.htm1215
-rw-r--r--pd/doc/1.manual/x3.htm854
-rw-r--r--pd/doc/1.manual/x4.htm59
-rw-r--r--pd/doc/1.manual/x5.htm1231
-rw-r--r--pd/doc/2.control.examples/00.INTRO.txt19
-rw-r--r--pd/doc/2.control.examples/01.PART1.hello.pd16
-rw-r--r--pd/doc/2.control.examples/02.editing.pd17
-rw-r--r--pd/doc/2.control.examples/03.connections.pd58
-rw-r--r--pd/doc/2.control.examples/04.messages.pd35
-rw-r--r--pd/doc/2.control.examples/05.counter.pd45
-rw-r--r--pd/doc/2.control.examples/06.more.counters.pd55
-rw-r--r--pd/doc/2.control.examples/07.time.pd39
-rw-r--r--pd/doc/2.control.examples/08.depthfirst.pd48
-rw-r--r--pd/doc/2.control.examples/09.send_receive.pd35
-rw-r--r--pd/doc/2.control.examples/10.more.messages.pd56
-rw-r--r--pd/doc/2.control.examples/11.review.pd42
-rw-r--r--pd/doc/2.control.examples/12.PART2.subpatch.pd72
-rw-r--r--pd/doc/2.control.examples/13.locality.pd27
-rw-r--r--pd/doc/2.control.examples/14.dollarsigns.pd5
-rw-r--r--pd/doc/2.control.examples/15.array.pd70
-rw-r--r--pd/doc/2.control.examples/15.file.txt2
-rw-r--r--pd/doc/2.control.examples/16.more.arrays.pd23
-rw-r--r--pd/doc/2.control.examples/17.PART3.midi.pd35
-rw-r--r--pd/doc/2.control.examples/18.conditional.pd59
-rw-r--r--pd/doc/2.control.examples/19.random.pd39
-rw-r--r--pd/doc/2.control.examples/20.weighted-random.pd44
-rw-r--r--pd/doc/2.control.examples/21.markov.chain.pd105
-rw-r--r--pd/doc/2.control.examples/22.sequencing.pd20
-rw-r--r--pd/doc/2.control.examples/dollarsign.pd35
-rw-r--r--pd/doc/2.control.examples/dollarsign2.pd54
-rw-r--r--pd/doc/2.control.examples/sendnumber.pd20
-rw-r--r--pd/doc/3.audio.examples/00.INTRO.txt43
-rw-r--r--pd/doc/3.audio.examples/01.PART1.sinewave.pd20
-rw-r--r--pd/doc/3.audio.examples/02.amplitude.pd37
-rw-r--r--pd/doc/3.audio.examples/03.line.pd50
-rw-r--r--pd/doc/3.audio.examples/04.line2.pd59
-rw-r--r--pd/doc/3.audio.examples/05.output.subpatch.pd97
-rw-r--r--pd/doc/3.audio.examples/06.frequency.pd123
-rw-r--r--pd/doc/3.audio.examples/07.frequency.mod.pd105
-rw-r--r--pd/doc/3.audio.examples/08.phase.mod.pd246
-rw-r--r--pd/doc/3.audio.examples/09.review.pd43
-rw-r--r--pd/doc/3.audio.examples/10.PART2.wavetables.pd104
-rw-r--r--pd/doc/3.audio.examples/11.wavetable.FM.pd147
-rw-r--r--pd/doc/3.audio.examples/12.tabread4.pd128
-rw-r--r--pd/doc/3.audio.examples/13.tabread4.interpolation.pd44
-rw-r--r--pd/doc/3.audio.examples/14.more.tabread.pd106
-rw-r--r--pd/doc/3.audio.examples/15.table.switching.pd127
-rw-r--r--pd/doc/3.audio.examples/16.table.spectrum.pd143
-rw-r--r--pd/doc/3.audio.examples/17.foldover.pd112
-rw-r--r--pd/doc/3.audio.examples/18.PART3.pulse.pd126
-rw-r--r--pd/doc/3.audio.examples/19.just.say.pd152
-rw-r--r--pd/doc/3.audio.examples/20.pulse.spectrum.pd136
-rw-r--r--pd/doc/3.audio.examples/21.more.pulses.pd138
-rw-r--r--pd/doc/3.audio.examples/22.pulse.width.mod.pd98
-rw-r--r--pd/doc/3.audio.examples/23.stereo.pd87
-rw-r--r--pd/doc/3.audio.examples/24.even.odd.pd116
-rw-r--r--pd/doc/3.audio.examples/25.bandlimited.pd166
-rw-r--r--pd/doc/3.audio.examples/26.additive.pd92
-rw-r--r--pd/doc/3.audio.examples/27.PART4.samplers.pd111
-rw-r--r--pd/doc/3.audio.examples/28.sampler.loop.pd124
-rw-r--r--pd/doc/3.audio.examples/29.sampler.loop.smooth.pd148
-rw-r--r--pd/doc/3.audio.examples/30.sampler.scratch.pd175
-rw-r--r--pd/doc/3.audio.examples/31.sampler.nodoppler.pd180
-rw-r--r--pd/doc/3.audio.examples/32.sampler.transpose.pd207
-rw-r--r--pd/doc/3.audio.examples/33.sampler.oneshot.pd143
-rw-r--r--pd/doc/3.audio.examples/34.sampler.notes.pd321
-rw-r--r--pd/doc/3.audio.examples/35.qlist.txt147
-rw-r--r--pd/doc/3.audio.examples/35.sampler.poly.pd228
-rw-r--r--pd/doc/3.audio.examples/36.PART5.envelopes.pd90
-rw-r--r--pd/doc/3.audio.examples/37.adsr.pd34
-rw-r--r--pd/doc/3.audio.examples/38.envelope.dB.pd158
-rw-r--r--pd/doc/3.audio.examples/39.envelope.slew.pd102
-rw-r--r--pd/doc/3.audio.examples/40.envelope.pitch.pd209
-rw-r--r--pd/doc/3.audio.examples/41.envelope.portamento.pd148
-rw-r--r--pd/doc/3.audio.examples/42.PART6.analog.sequencer.pd133
-rw-r--r--pd/doc/3.audio.examples/43.monophonic.synth.pd135
-rw-r--r--pd/doc/3.audio.examples/44.sample.hold.pd160
-rw-r--r--pd/doc/3.audio.examples/45.envelope.follower.pd101
-rw-r--r--pd/doc/3.audio.examples/46.PART7.filters.pd72
-rw-r--r--pd/doc/3.audio.examples/47.bandpass.pd146
-rw-r--r--pd/doc/3.audio.examples/48.filter.sweep.pd173
-rw-r--r--pd/doc/3.audio.examples/49.filter.floyd.pd193
-rw-r--r--pd/doc/3.audio.examples/50.filter.noise.pd196
-rw-r--r--pd/doc/3.audio.examples/51.ring.modulation.pd153
-rw-r--r--pd/doc/3.audio.examples/52.ssb.modulation.pd150
-rw-r--r--pd/doc/3.audio.examples/53.delays.pd225
-rw-r--r--pd/doc/3.audio.examples/54.delay.loop.pd213
-rw-r--r--pd/doc/3.audio.examples/55.delay.variable.pd129
-rw-r--r--pd/doc/3.audio.examples/56.delay.pitchshift.pd226
-rw-r--r--pd/doc/3.audio.examples/57.delay.reverb.pd316
-rw-r--r--pd/doc/3.audio.examples/58.PART8.moreFM.pd132
-rw-r--r--pd/doc/3.audio.examples/59.packets.pd161
-rw-r--r--pd/doc/3.audio.examples/60.packet.spectrum.pd147
-rw-r--r--pd/doc/3.audio.examples/61.two.cosines.pd124
-rw-r--r--pd/doc/3.audio.examples/62.declickit.pd132
-rw-r--r--pd/doc/3.audio.examples/63.sweepable.FM.pd161
-rw-r--r--pd/doc/3.audio.examples/64.paf.pd234
-rw-r--r--pd/doc/3.audio.examples/65.paf.control.pd219
-rw-r--r--pd/doc/3.audio.examples/66.PART9.quartic.pd140
-rw-r--r--pd/doc/3.audio.examples/67.more.quartic.pd147
-rw-r--r--pd/doc/3.audio.examples/68.qlist.pd102
-rw-r--r--pd/doc/3.audio.examples/69.more.adsr.pd117
-rw-r--r--pd/doc/3.audio.examples/70.vibrato.pd158
-rw-r--r--pd/doc/3.audio.examples/71.adsr.sequenced.pd217
-rw-r--r--pd/doc/3.audio.examples/72.execution.order.pd127
-rw-r--r--pd/doc/3.audio.examples/73.control.blocksize.pd111
-rw-r--r--pd/doc/3.audio.examples/74.up.downsampling.pd191
-rw-r--r--pd/doc/3.audio.examples/adsr.pd78
-rw-r--r--pd/doc/3.audio.examples/adsr2.pd110
-rw-r--r--pd/doc/3.audio.examples/echo.pd17
-rw-r--r--pd/doc/3.audio.examples/osc-voice.pd89
-rw-r--r--pd/doc/3.audio.examples/partial.pd51
-rw-r--r--pd/doc/3.audio.examples/qlist.txt56
-rw-r--r--pd/doc/3.audio.examples/qlist2.txt5
-rw-r--r--pd/doc/3.audio.examples/sampvoice.pd105
-rw-r--r--pd/doc/3.audio.examples/spectrum-partial.pd57
-rw-r--r--pd/doc/4.fft.examples/00.INTRO.txt65
-rw-r--r--pd/doc/4.fft.examples/01.fftanalysis.pd143
-rw-r--r--pd/doc/4.fft.examples/02.noisefft.pd267
-rw-r--r--pd/doc/4.fft.examples/03.denoise.pd364
-rw-r--r--pd/doc/4.fft.examples/04.shifts.pd27
-rw-r--r--pd/doc/4.fft.examples/05.sheepgoat.pd395
-rw-r--r--pd/doc/4.fft.examples/06.sheepgoat2.pd365
-rw-r--r--pd/doc/4.fft.examples/07.tinbell.pd248
-rw-r--r--pd/doc/4.fft.examples/08.convobros.pd344
-rw-r--r--pd/doc/4.fft.examples/09.pvoc.pd397
-rw-r--r--pd/doc/4.fft.examples/10.phaselockedvoc.pd444
-rw-r--r--pd/doc/4.fft.examples/11.pianorev.pd378
-rw-r--r--pd/doc/4.fft.examples/12.sinedecomposer.pd308
-rw-r--r--pd/doc/4.fft.examples/13.partialtracer.pd783
-rw-r--r--pd/doc/4.fft.examples/14.waveformgrab.pd385
-rw-r--r--pd/doc/4.fft.examples/add-trace.pd152
-rw-r--r--pd/doc/4.fft.examples/osc-voice.pd54
-rw-r--r--pd/doc/4.fft.examples/x.wavbin0 -> 8864 bytes
-rw-r--r--pd/doc/5.reference/0.INTRO.txt143
-rw-r--r--pd/doc/5.reference/0_all_guis-INTRO.txt131
-rw-r--r--pd/doc/5.reference/acoustics.pd40
-rw-r--r--pd/doc/5.reference/acoustics~.pd81
-rw-r--r--pd/doc/5.reference/adc~_dac~.pd11
-rw-r--r--pd/doc/5.reference/append.pd629
-rw-r--r--pd/doc/5.reference/bag.pd27
-rw-r--r--pd/doc/5.reference/bang.pd13
-rw-r--r--pd/doc/5.reference/bang~.pd18
-rw-r--r--pd/doc/5.reference/biquad~.pd35
-rw-r--r--pd/doc/5.reference/bng.pd265
-rw-r--r--pd/doc/5.reference/bp~.pd40
-rw-r--r--pd/doc/5.reference/canvas.pd19
-rw-r--r--pd/doc/5.reference/change.pd23
-rw-r--r--pd/doc/5.reference/clip~.pd30
-rw-r--r--pd/doc/5.reference/cos~.pd32
-rw-r--r--pd/doc/5.reference/cputime.pd15
-rw-r--r--pd/doc/5.reference/delay.pd30
-rw-r--r--pd/doc/5.reference/delread~.pd33
-rw-r--r--pd/doc/5.reference/delwrite~.pd15
-rw-r--r--pd/doc/5.reference/drawnumber.pd35
-rw-r--r--pd/doc/5.reference/drawpolygon.pd42
-rw-r--r--pd/doc/5.reference/element.pd51
-rw-r--r--pd/doc/5.reference/env~.pd28
-rw-r--r--pd/doc/5.reference/fft~.pd64
-rw-r--r--pd/doc/5.reference/float.pd18
-rw-r--r--pd/doc/5.reference/framp~.pd40
-rw-r--r--pd/doc/5.reference/gatom.pd32
-rw-r--r--pd/doc/5.reference/get.pd46
-rw-r--r--pd/doc/5.reference/getsize.pd41
-rw-r--r--pd/doc/5.reference/graph.pd13
-rw-r--r--pd/doc/5.reference/hdial.pd282
-rw-r--r--pd/doc/5.reference/hip~.pd31
-rw-r--r--pd/doc/5.reference/hslider.pd303
-rw-r--r--pd/doc/5.reference/int.pd24
-rw-r--r--pd/doc/5.reference/key.pd19
-rw-r--r--pd/doc/5.reference/line.pd22
-rw-r--r--pd/doc/5.reference/line~.pd33
-rw-r--r--pd/doc/5.reference/lop~.pd31
-rw-r--r--pd/doc/5.reference/makefilename.pd17
-rw-r--r--pd/doc/5.reference/makenote.pd26
-rw-r--r--pd/doc/5.reference/math.pd60
-rw-r--r--pd/doc/5.reference/message.pd46
-rw-r--r--pd/doc/5.reference/metro.pd29
-rw-r--r--pd/doc/5.reference/midi.pd129
-rw-r--r--pd/doc/5.reference/moses.pd17
-rw-r--r--pd/doc/5.reference/my_canvas.pd243
-rw-r--r--pd/doc/5.reference/namecanvas.pd8
-rw-r--r--pd/doc/5.reference/netreceive.pd23
-rw-r--r--pd/doc/5.reference/netsend.pd55
-rw-r--r--pd/doc/5.reference/noise~.pd18
-rw-r--r--pd/doc/5.reference/numbox2.pd302
-rw-r--r--pd/doc/5.reference/openpanel.pd11
-rw-r--r--pd/doc/5.reference/operators.pd31
-rw-r--r--pd/doc/5.reference/osc~.pd58
-rw-r--r--pd/doc/5.reference/otherbinops.pd90
-rw-r--r--pd/doc/5.reference/pack.pd37
-rw-r--r--pd/doc/5.reference/pd.pd52
-rw-r--r--pd/doc/5.reference/phasor~.pd36
-rw-r--r--pd/doc/5.reference/pipe.pd41
-rw-r--r--pd/doc/5.reference/plot.pd58
-rw-r--r--pd/doc/5.reference/pointer.pd79
-rw-r--r--pd/doc/5.reference/poly.pd30
-rw-r--r--pd/doc/5.reference/print.pd13
-rw-r--r--pd/doc/5.reference/print~.pd18
-rw-r--r--pd/doc/5.reference/qlist.pd76
-rw-r--r--pd/doc/5.reference/qlist.txt3
-rw-r--r--pd/doc/5.reference/random.pd19
-rw-r--r--pd/doc/5.reference/readsf~.pd48
-rw-r--r--pd/doc/5.reference/realtime.pd15
-rw-r--r--pd/doc/5.reference/receive.pd26
-rw-r--r--pd/doc/5.reference/route.pd80
-rw-r--r--pd/doc/5.reference/rsqrt~.pd32
-rw-r--r--pd/doc/5.reference/samphold~.pd34
-rw-r--r--pd/doc/5.reference/savepanel.pd12
-rw-r--r--pd/doc/5.reference/select.pd73
-rw-r--r--pd/doc/5.reference/send.pd26
-rw-r--r--pd/doc/5.reference/send~.pd32
-rw-r--r--pd/doc/5.reference/set.pd45
-rw-r--r--pd/doc/5.reference/setsize.pd54
-rw-r--r--pd/doc/5.reference/setsize.txt21
-rw-r--r--pd/doc/5.reference/sigbinops.pd60
-rw-r--r--pd/doc/5.reference/sig~.pd20
-rw-r--r--pd/doc/5.reference/snapshot~.pd28
-rw-r--r--pd/doc/5.reference/soundfiler.pd66
-rw-r--r--pd/doc/5.reference/spigot.pd21
-rw-r--r--pd/doc/5.reference/sqrt~.pd32
-rw-r--r--pd/doc/5.reference/stripnote.pd16
-rw-r--r--pd/doc/5.reference/struct.pd26
-rw-r--r--pd/doc/5.reference/sublist.pd10
-rw-r--r--pd/doc/5.reference/swap.pd20
-rw-r--r--pd/doc/5.reference/switch~.pd45
-rw-r--r--pd/doc/5.reference/table.txt1
-rw-r--r--pd/doc/5.reference/tabosc4~.pd86
-rw-r--r--pd/doc/5.reference/tabplay~.pd66
-rw-r--r--pd/doc/5.reference/tabread.pd21
-rw-r--r--pd/doc/5.reference/tabread4~.pd43
-rw-r--r--pd/doc/5.reference/tabreceive~.pd6
-rw-r--r--pd/doc/5.reference/tabsend~.pd6
-rw-r--r--pd/doc/5.reference/tabwrite.pd21
-rw-r--r--pd/doc/5.reference/tabwrite~.pd30
-rw-r--r--pd/doc/5.reference/text.pd4
-rw-r--r--pd/doc/5.reference/textfile.pd59
-rw-r--r--pd/doc/5.reference/textfile.txt6
-rw-r--r--pd/doc/5.reference/threshold~.pd31
-rw-r--r--pd/doc/5.reference/throw~.pd34
-rw-r--r--pd/doc/5.reference/timer.pd15
-rw-r--r--pd/doc/5.reference/toggle.pd273
-rw-r--r--pd/doc/5.reference/trigger.pd37
-rw-r--r--pd/doc/5.reference/unpack.pd28
-rw-r--r--pd/doc/5.reference/until.pd25
-rw-r--r--pd/doc/5.reference/value.pd30
-rw-r--r--pd/doc/5.reference/vcf~.pd35
-rw-r--r--pd/doc/5.reference/vdial.pd282
-rw-r--r--pd/doc/5.reference/vd~.pd19
-rw-r--r--pd/doc/5.reference/vslider.pd302
-rw-r--r--pd/doc/5.reference/vu.pd247
-rw-r--r--pd/doc/5.reference/wrap~.pd26
-rw-r--r--pd/doc/5.reference/writesf~.pd27
-rw-r--r--pd/doc/5.reference/x_all_guis.pd20
-rw-r--r--pd/doc/6.externs/0.README.txt9
-rw-r--r--pd/doc/6.externs/dspobj~.c49
-rw-r--r--pd/doc/6.externs/foo1.c37
-rw-r--r--pd/doc/6.externs/foo2.c49
-rw-r--r--pd/doc/6.externs/makefile75
-rw-r--r--pd/doc/6.externs/test-dspobj~.pd11
-rw-r--r--pd/doc/6.externs/test-foo1.pd6
-rw-r--r--pd/doc/6.externs/test-foo2.pd8
-rw-r--r--pd/doc/7.stuff/audio-playpen/1_DSP_INTRO.pd84
-rw-r--r--pd/doc/7.stuff/audio-playpen/2_sampler.pd33
-rw-r--r--pd/doc/7.stuff/audio-playpen/3_filter_and_ring.pd64
-rw-r--r--pd/doc/7.stuff/audio-playpen/4_more_filters.pd55
-rw-r--r--pd/doc/7.stuff/audio-playpen/5_delay.pd55
-rw-r--r--pd/doc/7.stuff/audio-playpen/6_flanger.pd71
-rw-r--r--pd/doc/7.stuff/audio-playpen/README.txt9
-rw-r--r--pd/doc/7.stuff/audio-playpen/qdelay.pd34
-rw-r--r--pd/doc/7.stuff/audio-playpen/qgain.pd14
-rw-r--r--pd/doc/7.stuff/audio-playpen/qring.pd14
-rw-r--r--pd/doc/7.stuff/audio-playpen/qsample.pd114
-rw-r--r--pd/doc/7.stuff/audio-playpen/qslew.pd8
-rw-r--r--pd/doc/7.stuff/audio-playpen/qvd.pd32
-rw-r--r--pd/doc/7.stuff/data-structures/0.intro.txt113
-rw-r--r--pd/doc/7.stuff/data-structures/1.scalars.pd60
-rw-r--r--pd/doc/7.stuff/data-structures/2.getting.data.pd73
-rw-r--r--pd/doc/7.stuff/data-structures/3.setting.data.pd105
-rw-r--r--pd/doc/7.stuff/data-structures/4.append.pd36
-rw-r--r--pd/doc/7.stuff/data-structures/5.array.pd112
-rw-r--r--pd/doc/7.stuff/data-structures/6.file.pd68
-rw-r--r--pd/doc/7.stuff/data-structures/7.sequencer.pd192
-rw-r--r--pd/doc/7.stuff/data-structures/data-array.pd64
-rw-r--r--pd/doc/7.stuff/data-structures/data-start.pd40
-rw-r--r--pd/doc/7.stuff/data-structures/file.txt39
-rw-r--r--pd/doc/7.stuff/data-structures/score.txt94
-rw-r--r--pd/doc/7.stuff/data-structures/voice.pd127
-rw-r--r--pd/doc/7.stuff/data-structures/z.txt64
-rw-r--r--pd/doc/7.stuff/soundfile-tools/1.ring-mod.pd189
-rw-r--r--pd/doc/7.stuff/soundfile-tools/2.bandpass.pd202
-rw-r--r--pd/doc/7.stuff/soundfile-tools/3.phase.vocoder.pd551
-rw-r--r--pd/doc/7.stuff/soundfile-tools/4.looper.pd338
-rw-r--r--pd/doc/7.stuff/soundfile-tools/5.reverb.pd214
-rw-r--r--pd/doc/7.stuff/soundfile-tools/6.vocoder.pd314
-rw-r--r--pd/doc/7.stuff/soundfile-tools/README.txt2
-rw-r--r--pd/doc/7.stuff/synth/1.poly.synth.pd311
-rw-r--r--pd/doc/7.stuff/synth/README.txt7
-rw-r--r--pd/doc/7.stuff/synth/gadsr.pd146
-rw-r--r--pd/doc/7.stuff/synth/numset.pd27
-rw-r--r--pd/doc/7.stuff/synth/preset.pd54
-rw-r--r--pd/doc/7.stuff/synth/preset1.txt13
-rw-r--r--pd/doc/7.stuff/synth/preset2.txt13
-rw-r--r--pd/doc/7.stuff/synth/preset3.txt13
-rw-r--r--pd/doc/7.stuff/synth/preset4.txt13
-rw-r--r--pd/doc/7.stuff/synth/synthvoice.pd73
-rw-r--r--pd/doc/7.stuff/synth/test-gadsr.pd2
-rw-r--r--pd/doc/7.stuff/tools/latency.pd97
-rw-r--r--pd/doc/7.stuff/tools/load-meter.pd21
-rw-r--r--pd/doc/7.stuff/tools/testtone.pd366
-rw-r--r--pd/doc/sound/bell.aiffbin0 -> 312012 bytes
-rw-r--r--pd/doc/sound/voice.wavbin0 -> 126092 bytes
-rw-r--r--pd/doc/sound/voice2.wavbin0 -> 78194 bytes
-rw-r--r--pd/extra/README.txt37
-rw-r--r--pd/extra/choice/README.txt12
-rw-r--r--pd/extra/choice/choice.c128
-rw-r--r--pd/extra/choice/makefile94
-rw-r--r--pd/extra/complex-mod~.pd30
-rw-r--r--pd/extra/help-bonk~.pd162
-rw-r--r--pd/extra/help-choice.pd41
-rw-r--r--pd/extra/help-complex-mod~.pd26
-rw-r--r--pd/extra/help-fiddle~.pd107
-rw-r--r--pd/extra/help-hilbert~.pd18
-rw-r--r--pd/extra/help-loop~.pd66
-rw-r--r--pd/extra/help-paf~.pd165
-rw-r--r--pd/extra/help-pique.pd33
-rw-r--r--pd/extra/help-rev1.pd119
-rw-r--r--pd/extra/help-rlshift~.pd29
-rw-r--r--pd/extra/hilbert~.pd15
-rw-r--r--pd/extra/pique/makefile94
-rw-r--r--pd/extra/pique/pique.c238
-rw-r--r--pd/extra/rev1-final.pd106
-rw-r--r--pd/extra/rev1-stage.pd99
-rw-r--r--pd/extra/rev1~.pd64
-rw-r--r--pd/man/pd.125
-rw-r--r--pd/man/pdreceive.126
-rw-r--r--pd/man/pdsend.126
-rw-r--r--pd/obj/nada0
-rw-r--r--pd/portaudio/LICENSE.txt65
-rw-r--r--pd/portaudio/MSP-README.txt3
-rw-r--r--pd/portaudio/README.txt81
-rw-r--r--pd/portaudio/pa_asio/pa_asio.cpp2998
-rw-r--r--pd/portaudio/pa_common/pa_convert.c402
-rw-r--r--pd/portaudio/pa_common/pa_host.h185
-rw-r--r--pd/portaudio/pa_common/pa_lib.c806
-rw-r--r--pd/portaudio/pa_common/pa_trace.c83
-rw-r--r--pd/portaudio/pa_common/pa_trace.h67
-rw-r--r--pd/portaudio/pa_common/portaudio.h463
-rw-r--r--pd/portaudio/pa_mac_core/notes.txt34
-rw-r--r--pd/portaudio/pa_mac_core/pa_mac_core.c1261
-rw-r--r--pd/portaudio/pablio/README.txt39
-rw-r--r--pd/portaudio/pablio/pablio.c307
-rw-r--r--pd/portaudio/pablio/pablio.def35
-rw-r--r--pd/portaudio/pablio/pablio.h108
-rw-r--r--pd/portaudio/pablio/pablio_pd.c335
-rw-r--r--pd/portaudio/pablio/pablio_pd.h110
-rw-r--r--pd/portaudio/pablio/ringbuffer.c199
-rw-r--r--pd/portaudio/pablio/ringbuffer.h101
-rw-r--r--pd/portaudio/pablio/ringbuffer_pd.c214
-rw-r--r--pd/portaudio/pablio/test_rw.c99
-rw-r--r--pd/portaudio/pablio/test_rw_echo.c123
-rw-r--r--pd/portaudio/pablio/test_w_saw.c108
-rw-r--r--pd/portaudio/pablio/test_w_saw8.c106
-rw-r--r--pd/portaudio/pablio/test_w_saw_pd.c108
-rw-r--r--pd/portaudio/portmidi-macosx/Makefile24
-rw-r--r--pd/portaudio/portmidi-macosx/README12
-rw-r--r--pd/portaudio/portmidi-macosx/pmdarwin.c36
-rw-r--r--pd/portaudio/portmidi-macosx/pminternal.h100
-rw-r--r--pd/portaudio/portmidi-macosx/pmmacosx.c336
-rw-r--r--pd/portaudio/portmidi-macosx/pmmacosx.h4
-rw-r--r--pd/portaudio/portmidi-macosx/pmtestbin0 -> 24685 bytes
-rw-r--r--pd/portaudio/portmidi-macosx/pmtest.c136
-rw-r--r--pd/portaudio/portmidi-macosx/pmutil.c86
-rw-r--r--pd/portaudio/portmidi-macosx/pmutil.h44
-rw-r--r--pd/portaudio/portmidi-macosx/portmidi.c358
-rw-r--r--pd/portaudio/portmidi-macosx/portmidi.h338
-rw-r--r--pd/portaudio/portmidi-macosx/porttime.h30
-rw-r--r--pd/portaudio/portmidi-macosx/ptdarwin.c58
-rwxr-xr-xpd/src/configure3021
-rw-r--r--pd/src/configure.in192
-rw-r--r--pd/src/d_arithmetic.c842
-rw-r--r--pd/src/d_array.c1075
-rw-r--r--pd/src/d_ctl.c496
-rw-r--r--pd/src/d_dac.c183
-rw-r--r--pd/src/d_delay.c314
-rw-r--r--pd/src/d_fft.c342
-rw-r--r--pd/src/d_fftroutine.c1001
-rw-r--r--pd/src/d_filter.c534
-rw-r--r--pd/src/d_global.c312
-rw-r--r--pd/src/d_math.c573
-rw-r--r--pd/src/d_mayer_fft.c419
-rw-r--r--pd/src/d_misc.c260
-rw-r--r--pd/src/d_osc.c531
-rw-r--r--pd/src/d_resample.c225
-rw-r--r--pd/src/d_soundfile.c2200
-rw-r--r--pd/src/d_ugen.c1078
-rw-r--r--pd/src/g_all_guis.c937
-rw-r--r--pd/src/g_all_guis.h320
-rw-r--r--pd/src/g_array.c1358
-rw-r--r--pd/src/g_bang.c600
-rw-r--r--pd/src/g_canvas.c1482
-rw-r--r--pd/src/g_canvas.h564
-rw-r--r--pd/src/g_editor.c1658
-rw-r--r--pd/src/g_graph.c1119
-rw-r--r--pd/src/g_guiconnect.c94
-rw-r--r--pd/src/g_hdial.c675
-rw-r--r--pd/src/g_hslider.c708
-rw-r--r--pd/src/g_io.c612
-rw-r--r--pd/src/g_mycanvas.c439
-rw-r--r--pd/src/g_numbox.c979
-rw-r--r--pd/src/g_readwrite.c722
-rw-r--r--pd/src/g_rtext.c478
-rw-r--r--pd/src/g_scalar.c373
-rw-r--r--pd/src/g_template.c1673
-rw-r--r--pd/src/g_text.c1070
-rw-r--r--pd/src/g_toggle.c528
-rw-r--r--pd/src/g_traversal.c1084
-rw-r--r--pd/src/g_vdial.c672
-rw-r--r--pd/src/g_vslider.c688
-rw-r--r--pd/src/g_vumeter.c762
-rw-r--r--pd/src/install-sh251
-rw-r--r--pd/src/m_atom.c129
-rw-r--r--pd/src/m_binbuf.c1138
-rw-r--r--pd/src/m_class.c772
-rw-r--r--pd/src/m_conf.c100
-rw-r--r--pd/src/m_glob.c121
-rw-r--r--pd/src/m_imp.h223
-rw-r--r--pd/src/m_memory.c88
-rw-r--r--pd/src/m_obj.c676
-rw-r--r--pd/src/m_pd.c296
-rw-r--r--pd/src/m_pd.h594
-rw-r--r--pd/src/m_sched.c462
-rw-r--r--pd/src/makefile3
-rw-r--r--pd/src/makefile.clean3
-rw-r--r--pd/src/makefile.dependencies0
-rw-r--r--pd/src/makefile.in233
-rw-r--r--pd/src/makefile.irix65
-rw-r--r--pd/src/makefile.nt95
-rw-r--r--pd/src/notes.txt264
-rw-r--r--pd/src/s_entry.c10
-rw-r--r--pd/src/s_file.c54
-rw-r--r--pd/src/s_freebsd.c3072
-rw-r--r--pd/src/s_inter.c794
-rw-r--r--pd/src/s_linux.c3087
-rw-r--r--pd/src/s_loader.c142
-rw-r--r--pd/src/s_mac.c356
-rw-r--r--pd/src/s_main.c803
-rw-r--r--pd/src/s_nt.c1586
-rw-r--r--pd/src/s_path.c279
-rw-r--r--pd/src/s_portaudio.c197
-rw-r--r--pd/src/s_print.c150
-rw-r--r--pd/src/s_sgi.c433
-rw-r--r--pd/src/s_unix.c445
-rw-r--r--pd/src/s_watchdog.c47
-rw-r--r--pd/src/t_main.c123
-rw-r--r--pd/src/t_tk.h10
-rw-r--r--pd/src/t_tkcmd.c367
-rw-r--r--pd/src/u_main.tk2570
-rw-r--r--pd/src/u_pdreceive.c305
-rw-r--r--pd/src/u_pdsend.c150
-rw-r--r--pd/src/x_acoustics.c193
-rw-r--r--pd/src/x_arithmetic.c898
-rw-r--r--pd/src/x_connective.c1452
-rw-r--r--pd/src/x_gui.c377
-rw-r--r--pd/src/x_interface.c78
-rw-r--r--pd/src/x_midi.c1314
-rw-r--r--pd/src/x_misc.c319
-rw-r--r--pd/src/x_net.c362
-rw-r--r--pd/src/x_qlist.c345
-rw-r--r--pd/src/x_time.c519
-rw-r--r--pd/src/z.pd41
-rw-r--r--pd/src/z2.pd12
-rw-r--r--pd/src/z3.pd3
-rw-r--r--pd/src/z4.pd2
-rw-r--r--pd/src/z5.pd8
-rw-r--r--pd/src/z6.pd4
513 files changed, 104610 insertions, 0 deletions
diff --git a/pd/INSTALL.txt b/pd/INSTALL.txt
new file mode 100644
index 00000000..a40aaa72
--- /dev/null
+++ b/pd/INSTALL.txt
@@ -0,0 +1,20 @@
+Detailed installations instructions are in the HTML documentation, which
+you can find in doc/1.manual.
+
+Quick compilation instructions for Linux or Mac OSX (assuming for Mac OSX that
+you've got Tck/Tk installed):
+
+Change to "src" subdirectory.
+
+type "./configure", possibly adding flags as follows:
+
+ To enable ALSA 0.9x (the latest one), add "--enable-alsa".
+ To enable the older ALSA 0.5x, add "--enable-old-alsa".
+ To enable Ritsch's RME 9652 driver, add --enable-rme".
+ To put Pd in /usr/bin instead of /usr/local/bin, add "--prefix=/bin".
+
+Type "make depend" and then "make"; or, if you prefer, as superuser
+type "make install".
+
+To run pd, either type the full pathname as in /home/me/pd/bin/pd, or else
+if you've chosen "make install", just type "pd".
diff --git a/pd/LICENSE.txt b/pd/LICENSE.txt
new file mode 100644
index 00000000..34ee8399
--- /dev/null
+++ b/pd/LICENSE.txt
@@ -0,0 +1,31 @@
+This software is copyrighted by Miller Puckette and others. The following
+terms apply to all files associated with the software unless explicitly
+disclaimed in individual files.
+
+The authors hereby grant permission to use, copy, modify, distribute,
+and license this software and its documentation for any purpose, provided
+that existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions. No written agreement,
+license, or royalty fee is required for any of the authorized uses.
+Modifications to this software may be copyrighted by their authors
+and need not follow the licensing terms described here, provided that
+the new terms are clearly indicated on the first page of each file where
+they apply.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+
+RESTRICTED RIGHTS: Use, duplication or disclosure by the government
+is subject to the restrictions as set forth in subparagraph (c) (1) (ii)
+of the Rights in Technical Data and Computer Software Clause as DFARS
+252.227-7013 and FAR 52.227-19.
diff --git a/pd/README.txt b/pd/README.txt
new file mode 100644
index 00000000..a348223e
--- /dev/null
+++ b/pd/README.txt
@@ -0,0 +1,44 @@
+This is the README file for Pd, a free real-time computer music software
+package resembling Max. You can get Pd for Linux, Windows, Mac OSX, or IRIX
+from http://www.crca.ucsd.edu/~msp/software.html or ftp://felix.ucsd.edu.
+Installation instructions are in the HTML DOCUMENTATION at:
+
+ http://www.crca.ucsd.edu/~msp/Pd_documentation/index.htm
+
+If you download and unpack Pd, you will also find the html documentation
+locally in the file, .../pd-whatever/doc/1.manual/index.htm. To unpack Pd:
+
+LINUX (or freeBSD). Download Pd, which will be a ".tar.gz" file; to unpack it,
+type "zcat [name].tar.gz | tar xf -" to a shell. This creates a directory with
+a name like "pd-0.35". There are also RPMs available.
+
+Microsoft Windows. Pd is distributed as a "zip" file. Unzip this,
+creating a directory such as \pd.
+
+IRIX. Download Pd, which will be a "tar.Z" file. You can unpack this by
+typing "zcat [name].tar.Z | tar xf -" to a shell.
+
+Macintosh. The web browser will automatically unpack the distributions
+into a folder such as "pd-0.35" on your desktop.
+
+If you have qustions about Pd, or if you wish to be notified of releases,
+check the Pd mailing list: http://iem.mhsg.ac.at/mailinglists/pd-list/
+
+Many extensions to Pd are available, notably for handling video and 3D
+graphics; see the html documentation for pointers.
+
+COPYRIGHT. Except as otherwise noted, all files in the Pd distribution are
+
+ Copyright (c) 1997-2001 Miller Puckette and others.
+
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "LICENSE.txt," included in the Pd distribution.
+(Note that tcl/tk, expr, and some other files are copyrighted separately).
+
+ACKNOWLEDGEMENTS. Thanks to Harry Castle, Mark Danks, Christian Feldbauer,
+Guenter Geiger, Kerry Hagan, Trevor Johnson, Fernando Lopez-Lezcano, Karl
+MacMillan, Thomas Musil, Toshinori Ohkouchi, Winfried Ritsch, Vibeke Sorensen,
+Rand Steiger, Shahrokh Yadegari, David Zicarelli, Iohannes Zmoelnig, and
+probably many others for contributions of code, documentation, ideas, and
+expertise. This work has received generous support from the Intel Research
+Council.
diff --git a/pd/bin/nada b/pd/bin/nada
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pd/bin/nada
diff --git a/pd/doc/1.manual/1.introduction.txt b/pd/doc/1.manual/1.introduction.txt
new file mode 100644
index 00000000..c8fa88a8
--- /dev/null
+++ b/pd/doc/1.manual/1.introduction.txt
@@ -0,0 +1,19 @@
+PD_VERSION
+
+A real-time graphical programming environment for live interactive
+computer music, Pd works on SGI machines, Microsoft Windows,
+Linux, and Max OSX.
+
+Pd is copyrighted, but is free for you to use for any reasonable purpose.
+See the file:
+ PD_BASEDIR/LICENSE.txt
+
+HTML documentation for Pd lives in:
+ file:PD_BASEDIR/doc/1.manual/index.htm
+or:
+ http://www.crca.ucsd.edu/~msp/Pd_documentation/index.htm
+
+The Pd mailing list archive lives in:
+ http://iem.kug.ac.at/mailinglists/pd-list/
+
+Many more useful links are listed in the HTML documentation, section 1.2.
diff --git a/pd/doc/1.manual/fig1.1.jpg b/pd/doc/1.manual/fig1.1.jpg
new file mode 100644
index 00000000..bfc76f64
--- /dev/null
+++ b/pd/doc/1.manual/fig1.1.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig1.2.jpg b/pd/doc/1.manual/fig1.2.jpg
new file mode 100644
index 00000000..c33c755c
--- /dev/null
+++ b/pd/doc/1.manual/fig1.2.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig1.3.jpg b/pd/doc/1.manual/fig1.3.jpg
new file mode 100644
index 00000000..caf29b2d
--- /dev/null
+++ b/pd/doc/1.manual/fig1.3.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig1.4.jpg b/pd/doc/1.manual/fig1.4.jpg
new file mode 100644
index 00000000..28a29dc6
--- /dev/null
+++ b/pd/doc/1.manual/fig1.4.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig1.5.jpg b/pd/doc/1.manual/fig1.5.jpg
new file mode 100644
index 00000000..4b01c59f
--- /dev/null
+++ b/pd/doc/1.manual/fig1.5.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig3.1.jpg b/pd/doc/1.manual/fig3.1.jpg
new file mode 100644
index 00000000..f8348970
--- /dev/null
+++ b/pd/doc/1.manual/fig3.1.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig3.10.jpg b/pd/doc/1.manual/fig3.10.jpg
new file mode 100644
index 00000000..4625ce0c
--- /dev/null
+++ b/pd/doc/1.manual/fig3.10.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig3.2.jpg b/pd/doc/1.manual/fig3.2.jpg
new file mode 100644
index 00000000..994d41c7
--- /dev/null
+++ b/pd/doc/1.manual/fig3.2.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig3.3.jpg b/pd/doc/1.manual/fig3.3.jpg
new file mode 100644
index 00000000..91cac54a
--- /dev/null
+++ b/pd/doc/1.manual/fig3.3.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig3.4.jpg b/pd/doc/1.manual/fig3.4.jpg
new file mode 100644
index 00000000..e2f2fe53
--- /dev/null
+++ b/pd/doc/1.manual/fig3.4.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig3.5.jpg b/pd/doc/1.manual/fig3.5.jpg
new file mode 100644
index 00000000..9a79a2b3
--- /dev/null
+++ b/pd/doc/1.manual/fig3.5.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig3.6.jpg b/pd/doc/1.manual/fig3.6.jpg
new file mode 100644
index 00000000..fcbcf3da
--- /dev/null
+++ b/pd/doc/1.manual/fig3.6.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig3.7.jpg b/pd/doc/1.manual/fig3.7.jpg
new file mode 100644
index 00000000..84dcd7f7
--- /dev/null
+++ b/pd/doc/1.manual/fig3.7.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig3.8.jpg b/pd/doc/1.manual/fig3.8.jpg
new file mode 100644
index 00000000..ab03a207
--- /dev/null
+++ b/pd/doc/1.manual/fig3.8.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig3.9.jpg b/pd/doc/1.manual/fig3.9.jpg
new file mode 100644
index 00000000..6e9655c7
--- /dev/null
+++ b/pd/doc/1.manual/fig3.9.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig7.1.jpg b/pd/doc/1.manual/fig7.1.jpg
new file mode 100644
index 00000000..b677f6bd
--- /dev/null
+++ b/pd/doc/1.manual/fig7.1.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig7.2.jpg b/pd/doc/1.manual/fig7.2.jpg
new file mode 100644
index 00000000..54690d0e
--- /dev/null
+++ b/pd/doc/1.manual/fig7.2.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig7.3.jpg b/pd/doc/1.manual/fig7.3.jpg
new file mode 100644
index 00000000..a3b70ed3
--- /dev/null
+++ b/pd/doc/1.manual/fig7.3.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig7.4.jpg b/pd/doc/1.manual/fig7.4.jpg
new file mode 100644
index 00000000..88ba5b40
--- /dev/null
+++ b/pd/doc/1.manual/fig7.4.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig7.5.jpg b/pd/doc/1.manual/fig7.5.jpg
new file mode 100644
index 00000000..f9de4b3b
--- /dev/null
+++ b/pd/doc/1.manual/fig7.5.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig7.6.jpg b/pd/doc/1.manual/fig7.6.jpg
new file mode 100644
index 00000000..5f24af7a
--- /dev/null
+++ b/pd/doc/1.manual/fig7.6.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig8.1.jpg b/pd/doc/1.manual/fig8.1.jpg
new file mode 100644
index 00000000..57e59313
--- /dev/null
+++ b/pd/doc/1.manual/fig8.1.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig8.2.jpg b/pd/doc/1.manual/fig8.2.jpg
new file mode 100644
index 00000000..1dd48cd9
--- /dev/null
+++ b/pd/doc/1.manual/fig8.2.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig8.3.jpg b/pd/doc/1.manual/fig8.3.jpg
new file mode 100644
index 00000000..165c1c88
--- /dev/null
+++ b/pd/doc/1.manual/fig8.3.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig8.4.jpg b/pd/doc/1.manual/fig8.4.jpg
new file mode 100644
index 00000000..afc89a73
--- /dev/null
+++ b/pd/doc/1.manual/fig8.4.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig8.5.jpg b/pd/doc/1.manual/fig8.5.jpg
new file mode 100644
index 00000000..6fa3d0d1
--- /dev/null
+++ b/pd/doc/1.manual/fig8.5.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig8.6.jpg b/pd/doc/1.manual/fig8.6.jpg
new file mode 100644
index 00000000..2823e032
--- /dev/null
+++ b/pd/doc/1.manual/fig8.6.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig9.1.jpg b/pd/doc/1.manual/fig9.1.jpg
new file mode 100644
index 00000000..bab4b689
--- /dev/null
+++ b/pd/doc/1.manual/fig9.1.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig9.2.jpg b/pd/doc/1.manual/fig9.2.jpg
new file mode 100644
index 00000000..88ef528c
--- /dev/null
+++ b/pd/doc/1.manual/fig9.2.jpg
Binary files differ
diff --git a/pd/doc/1.manual/fig9.3.jpg b/pd/doc/1.manual/fig9.3.jpg
new file mode 100644
index 00000000..ecb66004
--- /dev/null
+++ b/pd/doc/1.manual/fig9.3.jpg
Binary files differ
diff --git a/pd/doc/1.manual/index.htm b/pd/doc/1.manual/index.htm
new file mode 100644
index 00000000..ddaa292d
--- /dev/null
+++ b/pd/doc/1.manual/index.htm
@@ -0,0 +1,161 @@
+<HTML>
+<HEAD>
+<TITLE>Pd Documentation</TITLE>
+</HEAD>
+<BODY bgcolor="#ffffff">
+<SMALL>
+<div style="width:6.5in; margin-left:.5in">
+
+<CENTER>
+<FONT size=+5>
+<B>Pd Documentation</B><BR>
+</FONT></CENTER>
+<P>
+This is the HTML documentation for Pd, a patchable environment for audio
+analysis, synthesis, and processing,
+with a rich set of multimedia capabilities. The latest version of this page
+can be found at:
+ <a href="http://www.crca.ucsd.edu/~msp/software.html" name=s1>
+ http://www.crca.ucsd.edu/~msp/software.html</A> .
+<OL>
+<LI> <a href="x1.htm" name=s1>introduction </A>
+<OL>
+ <LI> <a href="x1.htm#s1">guide to the documentation </A>
+ <LI> <a href="x1.htm#s2">other resources </A>
+</OL>
+
+<LI> <A href="x2.htm" name=s2>theory of operation </A>
+<OL>
+ <LI> <A href="x2.htm#s1"> overview </A>
+ <OL>
+ <LI> <A href="x2.htm#s1.1"> main window, canvases, and printout </A>
+ <LI> <A href="x2.htm#s1.2"> object boxes </A>
+ <LI> <A href="x2.htm#s1.3"> message, number, and symbol boxes </A>
+ <LI> <A href="x2.htm#s1.4"> patches and files </A>
+ </OL>
+ <LI> <A href="x2.htm#s2"> how to edit patches </A>
+ <OL>
+ <LI> <A href="x2.htm#s2.1"> edit and run mode </A>
+ <LI> <A href="x2.htm#s2.2"> creating boxes </A>
+ <LI> <A href="x2.htm#s2.3"> the selection </A>
+ <LI> <A href="x2.htm#s2.4"> deleting, cutting, and pasting </A>
+ <LI> <A href="x2.htm#s2.5"> changing the text </A>
+ <LI> <A href="x2.htm#s2.6"> connecting and disconnecting boxes </A>
+ <LI> <A href="x2.htm#s2.7"> properties and help </A>
+ </OL>
+ <LI> <A href="x2.htm#s3"> messages </A>
+ <OL>
+ <LI> <A href="x2.htm#s3.1"> anatomy of a message </A>
+ <LI> <A href="x2.htm#s3.2"> depth first message passing </A>
+ <LI> <A href="x2.htm#s3.3">
+ hot and cold inlets and right to left outlet order </A>
+ <LI> <A href="x2.htm#s3.3"> message boxes </A>
+ </OL>
+ <LI> <A href="x2.htm#s4"> audio signals </A>
+ <OL>
+ <LI> <A href="x2.htm#s4.1"> sample rate and format </A>
+ <LI> <A href="x2.htm#s4.2"> tilde objects and audio connections </A>
+ <LI> <A href="x2.htm#s4.3"> converting to and from messages </A>
+ <LI> <A href="x2.htm#s4.4"> switching and blocking </A>
+ <LI> <A href="x2.htm#s4.5"> nonlocal signal connections </A>
+ </OL>
+ <LI> <A href="x2.htm#s5"> scheduling </A>
+ <OL>
+ <LI> <A href="x2.htm#s5.1"> audio and messages </A>
+ <LI> <A href="x2.htm#s5.2"> computation load </A>
+ <LI> <A href="x2.htm#s5.3"> determinism </A>
+ </OL>
+ <LI> <A href="x2.htm#s6"> semantics </A>
+ <OL>
+ <LI> <A href="x2.htm#s6.1"> creation of objects </A>
+ <LI> <A href="x2.htm#s6.2"> persistence of data </A>
+ <LI> <A href="x2.htm#s6.3"> message passing </A>
+ <LI> <A href="x2.htm#s6.4"> inlets and lists </A>
+ <LI> <A href="x2.htm#s6.5"> dollar signs </A>
+ </OL>
+ <LI> <A href="x2.htm#s7"> subpatches </A>
+ <OL>
+ <LI> <A href="x2.htm#s7.1"> abstractions </A>
+ <LI> <A href="x2.htm#s7.2"> graph-on-parent subpatches </A>
+ </OL>
+ <LI> <A href="x2.htm#s8"> numeric arrays </A>
+ <LI> <A href="x2.htm#s9"> data structures </A>
+ <OL>
+ <LI> <A href="x2.htm#s9.1"> abstractions </A>
+ <LI> <A href="x2.htm#s9.2"> graph-on-parent subpatches </A>
+ <LI> <A href="x2.htm#s9.3"> limitations </A>
+ </OL>
+
+</OL>
+
+<LI> <a href="x3.htm" name=s3> getting Pd to run </A>
+<OL>
+ <LI> <a href="x3.htm#s1.1">IRIX (SGI) </A>
+ <LI> <a href="x3.htm#s1.2">Microsoft Windows </A>
+ <LI> <a href="x3.htm#s1.3">Linux </A>
+ <LI> <a href="x3.htm#s1.4">Mac OSX </A>
+ <LI> <a href="x3.htm#s3"> graphics rendering using GEM </A>
+ <LI> <a href="x3.htm#s4"> The Pd command line </A>
+ <LI> <a href="x3.htm#s5"> dealing with files </A>
+</OL>
+<LI> <a href="x4.htm" name=s4> writing Pd objects in C </A>
+
+<LI> <a href="x5.htm" name=s5> current status </A>
+<OL>
+ <LI> <a href="x5.htm#s1"> release notes </A>
+ <LI> <a href="x5.htm#s2"> known bugs </A>
+ <LI> <a href="x5.htm#s3"> differences from Max/MSP </A>
+</OL>
+
+</OL>
+
+<!
+ intro: what Pd is
+ guide to the documentation
+ other resources
+
+ Theory of operation
+ main window and canvases
+ messages
+ signals
+ loading, editing, and saving patches
+ subpatches
+ one-off and abstractions
+ blocking for signals
+ data
+
+ Making Pd work
+ how to get and install Pd
+ IRIX
+ NT
+ Linux
+ audio
+ testing it
+ the scheduler advance
+ IRIX
+ NT
+ Linux
+ GEM
+ getting it
+ running it
+ running Pd patches
+ command line options
+ opening & saving files
+ editing
+ file stuff
+ the path
+ abstractions
+ externs
+ the help feature
+ Writing Pd objects in C
+ release notes
+ features
+ bugs
+
+
+
+
+>
+
+</BODY>
+</HTML>
diff --git a/pd/doc/1.manual/x1.htm b/pd/doc/1.manual/x1.htm
new file mode 100644
index 00000000..946949e9
--- /dev/null
+++ b/pd/doc/1.manual/x1.htm
@@ -0,0 +1,112 @@
+<HTML>
+<HEAD>
+<TITLE>Pd Documentation 1</TITLE>
+</HEAD>
+<BODY bgcolor="#ffffff">
+<SMALL>
+<div style="width:6.5in; margin-left:.5in">
+
+<CENTER> <B>
+Pd Documentation chapter 1: introduction
+</B> </CENTER>
+<BR>
+<A href=index.htm#s1> back to table of contents </A>
+ <BR><BR>
+<P>
+This is the HTML documentation for the Pd computer program.
+Pd is free and can be downloaded from the internet;
+go to
+ <A href="http://www.crca.ucsd.edu/~msp/software.html">
+ http://www.crca.ucsd.edu/~msp/software.html</A>
+to get it.
+<H4> <A name=s1> 1.1. guide to the documentation </A> </H4>
+
+<P> Pd's documentation consists of:
+
+<UL>
+<LI> this HTML manual
+<LI> "reference" patches, one for each kind of object in Pd
+<LI> "example" patches showing how to do things
+<LI> sample C code
+</UL>
+
+<P>
+This manual has five sections:
+
+<OL>
+<LI> this overview
+<LI> <A href="x2.htm">
+ a theory of operations, explaining how Pd works </A>
+<LI> <A href="x3.htm">
+ instructions on installing Pd and getting it to run </A>
+<LI> <A href="x4.htm"> how to write C extensions to Pd </A>
+<LI> <A href="x5.htm"> release notes and known bugs </A>
+</OL>
+
+<P> In order to consult the reference and example patches, you'll first have
+to get Pd started as explained in this manual.
+
+<P>
+For a list of all the objects you can use in Pd, see the text file,
+"0.INTRO.txt" in the directory, "../5.reference". To get help on any
+Pd object you can right click on it; or you can browse the help patches
+by choosing "Pure Documentation..." in the Pd help menu and looking in
+5.reference.
+
+<P>
+The example patches are also available from the "Pure Documentation..." item
+in Pd's
+"help" menu. The example patches appear in subdirectories named
+"2.control.examples", "3.audio.examples" and "4.fft.examples." Some additional
+patches in "7.stuff" might also be helpful.
+
+<P>
+To get started writing your own C extensions, refer to chapter 4 of this manual.
+
+<H4> <A name=s2> 1.2. other resources </A> </H4>
+
+<P>
+Most of the interesting resources related to Pd show up on the Pd mailing list,
+maintained by Iohannes Zmoelnig. To subscribe or browse the archives
+visit:
+ <A href="http://iem.kug.ac.at/mailinglists/pd-list/">
+ http://iem.kug.ac.at/mailinglists/pd-list/</A>.
+
+. This is the
+best source of recent information regarding installation problems and bugs. It
+is perfectly reasonable to post "newbie" questions on this list; alternatively
+you can contact msp@ucsd.edu for help.
+
+<P> Many extensions to Pd are announced on the mailing list. In particular,
+for people interested in graphics, there is a A 3D graphics rendering package,
+named GEM, based on OpenGL, was written by Mark Danks, adapted to Linux by
+Guenter Geiger, and is now maintained by Iohannes Zmoelnig. GEM runs on
+Windows and Linux and probably will run with some coaxing on IRIX. You can get
+it from: <A href=http://iem.kug.ac.at/GEM>http://iem.kug.ac.at/GEM</A> .
+
+<P> A video processing package, Framestein, is by Juha Vehvilainen. This runs
+on Windows only: <A href=http://framestein.org> http://framestein.org </A>.
+
+<P>
+Here are some more Pd links (in the order I found them): <BR>
+
+<a href="http://www.crca.ucsd.edu/~msp"> Miller Puckette's home page</a><br>
+<a href="http://gige.epy.co.at/"> Guenter Geiger's home page</a><br>
+<a href="http://www.danks.org/mark"> Mark Dank's home page</a><br>
+<a href="http://wonk.epy.co.at">Pd page on Wonk (Klaus)</a><br>
+<a href="http://iem.kug.ac.at/~zmoelnig/index.html">
+ Johannes M Zmoelnig</a><br>
+<a href="http://iem.kug.ac.at/~math/pd/"> Norbert Math's Pd page</a> <br>
+<a href="http://iem.kug.ac.at/iemlib/"> Thomas Musil's IEMLIB</a> <br>
+<a href="http://www.pure-data.org/"> jfm3's Pure Data FAQ and downloads</a>
+(also available in Japanese translation).<br>
+<a href="http://iem.kug.ac.at/pdwiki/">
+Nicolas Lhommet's WikiWikiWeb page for Pd</a><br>
+<a href="http://iem.kug.ac.at/pdb/"> Norbert's searchable list of all known
+Pd objects</a><br>
+<a href="http://suita.chopin.edu.pl/~czaja/miXed/externs/xeq.html">
+Krzysztof Czaja's MIDI file support </a><br>
+<a href="http://www.davesabine.com/media/puredata.asp?action=pddp">
+David Sabine's Pd Documentation Project: new, highly detailed help windows</a><br>
+</BODY>
+</HTML>
diff --git a/pd/doc/1.manual/x2.htm b/pd/doc/1.manual/x2.htm
new file mode 100644
index 00000000..cc382e67
--- /dev/null
+++ b/pd/doc/1.manual/x2.htm
@@ -0,0 +1,1215 @@
+<HTML>
+<HEAD>
+<TITLE>Pd Documentation</TITLE>
+</HEAD>
+<BODY bgcolor="#ffffff">
+<SMALL>
+<div style="width:6.5in; margin-left:.5in">
+
+<CENTER> <B>
+Pd Documentation chapter 2: theory of operation
+</B> </CENTER>
+<BR>
+<A href=index.htm#s2> back to table of contents</A>
+ <BR><BR>
+<P>
+
+<P> The purpose of this chapter is to describe Pd's design and how it is
+supposed to work. Practical details about how to obtain, install, and run Pd
+are described in the next chapter. To learn digital audio processing basics
+such as how to generate time-varying sounds that don't click or fold over, a
+good reference is Dodge and Jerse, <I> Computer Music </I>.
+
+<H4> <A name=s1> 2.1 overview </A> </H4>
+
+Pd is a real-time graphical programming environment for audio and graphical
+processing. It resembles the Max/MSP system but is much simpler and more
+portable; also Pd has two features not (yet) showing up in Max/MSP: first,
+via Mark Dank's GEM package, Pd can be used for simultaneous computer
+animation and computer audio. Second, an experimental facility is provided
+for defining and accessing data structures.
+
+<H4> <A name=s1.1> 2.1.1. the main window, canvases, and printout </A> </H4>
+
+When Pd is running, you'll see a main "Pd" window, and possibly one or more
+"canvases" or "patches". The main Pd window looks like this:
+
+<P><CENTER>
+ <IMG src="fig1.1.jpg">
+</CENTER><P>
+
+<P> There are peak level and clip indicators for audio input and output; these
+report peak levels over all input and all output channels. Note that DC
+shows up as an input level; many cards have DC levels which show up in the
+50s. To see an RMS audio level, select "test audio and MIDI" from the help
+window. The main window display is intended only to help you avoid clipping
+on input and output. You can turn the peak meters on and off using the
+control at bottom left.
+
+<P> At bottom right is a control to turn audio processing on and off globally.
+Turning audio off does not relinquish the audio devices, it just stops the
+computation. The "audio" menu is also provided, with accelerators "Control-."
+to turn audio computation off and "Control-/" to turn it on. When audio is
+on, Pd is computing audio samples in real time according to whatever patches
+you have open (visible or not.)
+
+<P> The DIO (Digital I/O) error indicator flashes if there is a synchronization
+error for audio input or output. Click there to see a list of recent errors.
+This indicator is normally red at startup, and will turn red whenever the
+computation runs late (so that the DAC FIFOs fill and/or the ADC FIFOs empty)
+or if audio input and output are not running at the same rate. See
+<a href="x3.htm#s2"> audio and MIDI support </A>.
+
+<P> Pd documents are called "patches" or "canvases."
+Each open document has one main window and any number of
+subwindows. The subwindows can be opened and closed but are always running
+whether you can see them or not. Here is a simple Pd patch:
+
+<P><CENTER>
+ <IMG src="fig1.2.jpg">
+</CENTER><P>
+
+There are four <I> text items </I> in this patch: a number box (showing zero),
+an object box showing "print," and two comments. The number box and the object
+box are connected, the number box's output to the print box's input. Boxes may
+have zero or more inputs and/or outputs, with the inputs on top and the outputs
+on bottom.
+
+<P>
+Pd's printout appears on its standard output. Normally, you'll run Pd in a
+"shell" or "terminal" window which you'll keep open to see any printout or
+error messages.
+
+<H4> <A name=s1.2> 2.1.2. object boxes </A> </H4>
+<P> Pd patches can have four types of boxes: <I> object, message, GUI, </I>
+and <I> comment </I>.
+
+<P> You make <I> objects </I> by typing text into object boxes. The text is
+divided into <I> atoms </I> separated by white space. The first atom specifies
+what type of object Pd will make, and the other atoms, called <I> creation
+arguments </I>, tell Pd how to initialize the object. If you type for example,
+
+<P><CENTER>
+ <IMG src="fig1.3.jpg">
+</CENTER><P>
+
+the "+" specifies the <I> class </I> of the object.
+In this case the object will be the kind that carries out addition,
+and the "13" initializes the amount to add. Atoms are either numbers or <I>
+symbols </I> like "+".
+
+The text you type into an object box determines how
+many and what kinds of inlets and outlets the object will have. Some
+classes (like "+" always have a fixed arrangement of inlets and outlets,
+and in the case of other classes, the inlets and outlets will depend on the
+creation arguments.
+
+<P>
+Here for example is a simple MIDI synthesizer:
+
+<P><CENTER>
+ <IMG src="fig1.4.jpg">
+</CENTER><P>
+
+This patch mixes <I> control </I> objects (notein, stripnote, and ftom) with
+<I> tilde </I> objects osc~, *~, and dac~. The control objects carry out their
+function sporadically, as a result of one or more type of <I> event </I>. In
+this case, incoming MIDI note messages set off the control computation. The
+result of the computation is, when the note happens to be a "note on" (and not
+a "note off", to compute the frequency in cycles per second and pass it on to
+the oscillator ("osc~").
+
+<P> The second half of the patch, the osc~, *~, and dac~ objects, compute audio
+samples, in the same way as an analog synthesizer works. The osc~ object is
+acting as the interface between the two regimes, in that it takes control
+messages to set its frequency but talks to "*~" using an audio signal. Audio
+signals aren't sporadic; they are continuous streams of numbers. As a result
+tilde objects act under very different rules from control objects. The audio
+portion of the patch is always running, whether MIDI messages arrive or not;
+the function of control computations is to insert calculations between the
+audio computation which may change audio computation parameters such as
+the frequency of an oscillator.
+
+<H4> <A name=s1.3> 2.1.3. message and GUI boxes </A> </H4>
+
+The border of a box tells you how its text is interpreted and how the box
+functions. Object boxes use the text to create objects when you load a
+patch. <I> Message </I> boxes interpret the text as a message to send whenever
+the box is activated (by an incoming message or with the mouse.) In the example
+below the message box, when clicked, sends the message "21" to an object
+box which adds 13 to it.
+
+<P><CENTER>
+ <IMG src="fig1.5.jpg">
+</CENTER><P>
+
+The third box shown is a GUI box. GUI boxes come in many forms including
+number boxes (as in this example), toggles, sliders, and so on. Whereas the
+appearance of an object or message box is static when a patch is running, a
+number box's contents (the text) changes to reflect the current value held by
+the box. You can also use a number box as a control by clicking and dragging
+up and down, or by typing values in it. (There are also shift- and alt-click
+actions; see <A href="x2.htm#s2.7"> getting help </A> to find out how to look
+this up).
+
+<P> You can also create a "symbol" box which is like a number box but deals
+in symbols like "cat." You can type your own strings in (followed by "enter")
+or use it to display strings which arrive as messages to its inlet.
+
+<H4> <A name=s1.4> 2.1.4. patches and files </A> </H4>
+
+When you save a patch to a file, Pd doesn't save the entire state of all the
+objects in the patch, but only what you see: the objects' creation arguments
+and their interconnections. Certain data-storage objects have functions for
+reading and writing other files to save and restore their internal state.
+
+Pd finds files using a <I> path </I> which can be specified as part of Pd's
+startup arguments. The path specifies one or more directories, separated by
+colons (semicolons if you're using windows.) Most objects which can read files
+search for them along the search path, but when Pd writes files they go to
+the directory where the patch was found.
+
+<H4> <A name=s2> 2.2. editing Pd patches </A> </H4>
+
+<H4> <A name=s2.1> 2.2.1. edit and run mode </A> </H4>
+
+<P> A patch can be in edit or run mode; this really only affects how mouse
+clicks affect the patch. In edit mode, clicking and dragging selects and
+moves boxes or makes and cuts connections; in run mode clicking on boxes sends
+them messages which they react to in different ways. In run mode, number and
+message boxes can be used as controls. Normally, when you are in a performance
+you will stay in run mode; to change the patch you go to edit mode.
+
+<H4> <A name=s2.2> 2.2.2. creating boxes </A> </H4>
+
+<P> You can create boxes (objects, messages, GUIs, and comments) using the
+"put" menu. Note the handy accelerators. Object and message boxes are empty
+at first; drag them where you want them and type in the text. The GUI
+objects (which come in several flavors) require no typing; just create and
+place them.
+
+<P> You will often find it more convenient to select a box and "duplicate" it
+(in the Edit menu) than to use the "Put" menu. If you select and duplicate
+several items, any connections between them will be duplicated as well.
+
+<H4> <A name=s2.3> 2.2.3. the selection </A> </H4>
+
+Boxes in a Pd window may be selected by clicking on them. To select more
+than one object you may use shift-click or click "outside" and select all
+objects within a rectangle. You can't select lines, only boxes.
+
+Clicking on an unselected object, message, or comment box makes the text
+active, i.e., ready to be text edited. If you select using the rectangle
+method, the text isn't activated. This affects whether further clicks will
+displace teh object or select text within it.
+
+<H4> <A name=s2.4> 2.2.4. deleting, cutting, and pasting </A> </H4>
+
+If you select a box but don't activate the text in it, you can "delete" it
+by hitting the backspace key. You can "cut" "copy" and "paste" using menu
+items. Notice that pasting puts the new object(s) right down on top of the
+old ones.
+
+The "duplicate" menu item performs a copy and paste with a small offset so you
+can see the new boxes. You can drag them together to a new place on the
+screen.
+
+You can cut and paste between windows within Pd but cut/paste isn't
+integrated with the OS in any way. Cut/copy/paste for text strings isn't
+implemented yet, although in Linux and Irix at least you can "X-paste" into
+and out of "text" dialogs (created with the "edit text" menu item.)
+
+<H4> <A name=s2.5> 2.2.5. changing the text </A> </H4>
+
+<P> To change a text item, you can select it and then edit the text. If you
+only click once, the entire text is selected and your typing will replace
+everything. Click again and drag to select a portion of the text to retype.
+
+<P> If there's
+more than a small amount of text (in a comment, for example) you might want to
+select the text and choose "text editor" from the Edit menu, which opens a text
+editing window with a copy of the text in it. Hitting "send" in that window is
+exactly equivalent to retyping the text into Pd; you can send it to more than
+one box in sequence if you want.
+
+<P> If you click a box and move the mouse without releasing the button this
+displaces the entire box. If you wish to displace a box which is already
+sepected, first deselect the box by clicking outside it; otherwise you will be
+selecting text instead of moving the box.
+
+<P> <I> The updated text only becomes part of the patch when you deselect the
+object. </I> Changing the text in an "object" box actually deletes the old
+object and creates a new one; the internal state of the old one is lost.
+
+<H4> <A name=s2.6> 2.2.6. connecting and disconnecting boxes </A> </H4>
+
+To make a connection between two boxes, click on any outlet of the first
+one, drag toward an inlet of the second one, and release. You can actually
+release the mouse button anywhere within the target object and the connection
+will be made to the nearest inlet.
+
+Connections are broken simply by clicking on them (the cursor changes to an
+"X" when appropriate.)
+
+<H4> <A name=s2.7> 2.2.7. Properties and help </A> </H4>
+
+<P> all the "clicking" mentioned above is done with the left mouse button.
+The right button, instead, gives a popup menu for "properties" and "help".
+Properties are enabled for number boxes and graphs (and in the future may
+be available for other things as well.) Selecting "help" on an object gets
+a Pd patch that demonstrates how to use it. "Help for the canvas as a whole
+(click outsize any object) gives a list of all built-in objects.
+
+<H4> <A name=s2.7> 2.2.8. miscellaneous </A> </H4>
+
+<P> Control-q "quits" Pd, but asks you to comfirm the quit. To quit without
+having to confirm, use command-shift-Q.
+
+<H4> <A name=s3> 2.3. messages </A> </H4>
+
+<P> In Pd, objects intercommunicate by sending messages and/or audio signals.
+Pd messages are sporadic, like MIDI messages or music N "Note cards."
+
+<H4> <A name=s3.1> 2.3.1. anatomy of a message </A> </H4>
+
+Messages contain a selector followed by
+any number of arguments. The selector is a symbol, which appears in the patch
+as a non-numeric string with no white space, semicolons, or commas. The
+arguments may be symbols or numbers. Numbers in Pd are kept in 32-bit floating
+point, so that they can represent integers exactly between -8388608 and
+8388608. (In Max, there are separate data types for integers and floating
+point numbers; Pd uses only float.)
+
+<P> When a message is passed to something (which is often an inlet of a box
+but could be anything that can receive a message), the selector of the message
+is checked against the receiver. If the receiver recognizes messages of that
+selector, it carries out some corresponding action. For instance, here is a
+"float" object:
+
+<P><CENTER>
+ <IMG src="fig3.1.jpg">
+</CENTER><P>
+
+<P> The two rectangles at the top are usually both called "inlets" but
+the one at the left directs incoming messages to the "float" object itself,
+whereas the one at the right directs messages to an auxilliary "inlet"
+object. The float object proper (represented by the left-hand inlet) accepts
+messages with selector "float" and "bang". The right-hand inlet takes only
+the message selector "float". These two selectors, along with "symbol" and
+"list", are usually used to denote an object's main action, whatever it may be,
+so that objects can be interconnected with maximum flexibility.
+
+<P> It is possible to type messages which start with a number,
+which cannot be used as a selector. A single number is always given the
+"float" selector automatically, and a message with a number followed by other
+arguments is given the selector "list".
+
+<H4> <A name=s3.2> 2.3.2. depth first message passing </A> </H4>
+
+<P> In Pd whenever a message is initiated, the receiver may then send out
+further messages in turn, and the receivers of those messages can send yet
+others. So each message sets off a tree of consequent messages. This tree is
+executed in depth first fashion. For instance in the patch below:
+
+<P><CENTER>
+ <IMG src="fig3.2.jpg">
+</CENTER><P>
+
+<P> the order of arrival of messages is either A-B-C-D or A-C-D-B. The "C"
+message is not done until the "D" one is also, and the "A" is not done until
+all four are. It is indeterminate which of "B" or "C" is done first; this
+depends on what order you made the connections in (in Max, it's automatically
+sorted right to left).
+
+<P> Message-passing can give rise to infinite loops of the sort shown here:
+
+<P><CENTER>
+ <IMG src="fig3.3.jpg">
+</CENTER><P>
+
+<P> Here the left-hand "+" can't finish processing until the right-hand one has
+been sent the result "2", which can't finish processing that until the
+left-hand one has been sent "3", and so on. Pd will print an error message
+reporting a "stack overflow" if this happens.
+
+<P> However, it is legal to make a loop if there is a "delay" object somewhere
+in it. When the "delay" receives a message it schedules a messsage for the
+future (even if the time delay is 0) and is then "finished;" Pd's internal
+scheduler will wake the delay back up later.
+
+<H4> <A name=s3.3>
+2.3.3. hot and cold inlets and right to left outlet order </A> </H4>
+
+<P> With few exceptions (notably "timer"), objects treat their leftmost
+inlet as "hot" in the sense that messages to right inlets can result in output
+messages. So the following is a legal (and reasonable) loop construct:
+
+<P><CENTER>
+ <IMG src="fig3.4.jpg">
+</CENTER><P>
+
+Here the "f" is an abbreviation for "float". Note that the "+ 1" output is
+connected to the right-hand inlet of "f". This "cold" inlet merely stores the
+value for the next time the "f" is sent the "bang" message.
+
+It is frequently desirable to send messages to two or more inlets of an object
+to specify its action. For instance, you can use "+" to add two numbers; but
+to do it correctly you must make sure the right hand inlet gets its value
+first. Otherwise, when the left hand side value comes in, "+" will carry out
+the addition (since the left hand inlet is the "hot" one) and will add this
+value to whatever was previously sitting in the right hand inlet.
+
+<P> Problems can arise when a single outlet is connected (either directly or
+through arbitrarily long chains of message passing) to different inlets of a
+single object. In this case it is indeterminate which order the two inlets will
+receive their messages. Suppose for example you wish to use "+" to double a
+number. The following is incorrect:
+
+<P><CENTER>
+ <IMG src="fig3.5.jpg">
+</CENTER><P>
+
+<P> Here, I connected the left inlet before connecting the right hand one (although
+this is not evident in the appearance of the patch.) The "+" thus adds the
+new input (at left) to the previous input (at right).
+
+<P> The "trigger" object, abbreviated "t", can be used to split out connections
+from a single outlet in a determinate order. By convention, all objects in Pd,
+when sending messages out more than one outlet, do so from right to left. If
+you connect these to inlets of a second object without crossing wires, the
+second object will get its leftmost inlet last, which is usually what you
+want. Here is how to use "trigger" to disambiguate the previous example:
+
+<P><CENTER>
+ <IMG src="fig3.6.jpg">
+</CENTER><P>
+
+<P> "Cold" (non-leftmost) inlets are almost universally used to store single
+values (either numbers or symbols.) With the exception of "line" and "line~",
+these values are "sticky," i.e., once you set the value it is good until the
+next time you set it. (The "line" exception is for sanity's sake.)
+
+<P> One more question sometimes comes up in execution order, which is
+the order in which two messages are sent to a single "cold" inlet. In this
+situation, since the messages are merged, the last value to be received is
+the value that is used in the computation.
+
+<H4> <A name=s3.4> 2.3.4. message boxes </A> </H4>
+
+Message boxes are text boxes in which you type a message. When the message
+box is activated, either by clicking on it or sending something to its inlet,
+the message or messages are sent, either to the message box's outlet or
+elsewhere as specified.
+
+<P><CENTER>
+ <IMG src="fig3.7.jpg">
+</CENTER><P>
+
+The first of the message boxes above contains the single number 1.5; this
+message has an implicit selector of "float." The second is a list with three
+numbers in it, and in the third, the selector is "my" and the two arguments are
+the number 5 and the symbol "toes."
+
+<P> Multiple messages may be separated by commas as shown:
+
+<P><CENTER>
+ <IMG src="fig3.8.jpg">
+</CENTER><P>
+Here the three messages are the numbers 1, 2, and 3, and they are sent in
+sequence (with no intervening time between them, as with the "trigger" object,
+and having depth-first consequences so that whatever chain of actions depending
+on "1" takes place before anything depending on "2" and so on.)
+
+<P> Semicolons may also separate messages. A message following a semicolon must
+specify a symbol giving a destination (in other words, semicolons are like
+commas except that they clear the "current destination"
+so that the next message specifies a new one). The "current destination" is
+at first the message box's own outlet. In the example below, the leading
+semicolon immediately redirects messages from the outlet to an object named
+"fred" (which is here a receive object), and likewise the next message is sent
+to "sue."
+
+
+<P><CENTER>
+ <IMG src="fig3.9.jpg">
+</CENTER><P>
+
+Certain other objects (Pd windows, for example, and arrays) have Pd names and
+can be sent messages this way. Also, the special object "pd" is defined to
+which you may send messages to start and stop DSP.
+
+<P> You can put variables in message boxes as shown below:
+
+<P><CENTER>
+ <IMG src="fig3.10.jpg">
+</CENTER><P>
+
+Here, "$1", etc., refer to the arguments of the arriving message (and aren't
+defined if you send a "bang" message or if you click on the message box to
+activate it.) Dollar sign variables are either numbers or symbols depending
+on the incoming message; if symbols, you may even use them to specify variable
+message selectors or destinations.
+
+<H4> <A name=s4> 2.4. audio signals </A> </H4>
+
+<P>
+Using Pd you can build audio patches which can synthesize musical sounds,
+analyze incoming sounds, process incoming sounds to produce transformed
+audio outputs, or integrate audio processing with other media. This section
+describes how Pd treats audio signals.
+
+<H4> <A name=s4.1> 2.4.1. sample rate and format </A> </H4>
+
+<P>
+Pd's audio signals are internally kept as 32-bit floating point numbers, so
+you have all the dynamic range you could want. However, depending on your
+hardware, audio I/O is usually limited to 16 or 24 bits. Inputs all appear
+between the values of -1 and 1; and output values will be clipped to that range.
+
+<P>
+Pd assumes a sample rate of 44100 unless you override this in Pd's command line.
+Pd doesn't check that this matches the sample rate of audio input or output,
+nor does Pd attempt to set your computer's audio sample rate to its own. If
+the audio system is running at the wrong sample rate, audio output will
+be transposed (you can check this using the "test audio and MIDI" patch; see
+the help menu).
+
+<P>
+Pd can read or write samples to files either in 16-bit or 24-bit fixed point
+or in 32-bit floating point, in WAV, AIFF, or AU format, via the soundfiler,
+readsf, and writesf objects.
+
+<H4> <A name=s4.2> 2.4.2. tilde objects and audio connections </A> </H4>
+
+Audio computations in Pd are carried out by "tilde objects" such as "osc~"
+whoswe names conventionally end in a tilde character to warn you what they
+are. Tilde objects can intercommunicate via audio connections. When audio
+computation is turned on, or when you change the audio network while audio is
+on, Pd sorts all the tilde objects into a linear order for running; then this
+linear list is run down in blocks of 64 samples each; at 44100 Hz. this means
+the audio network runs every 1.45 milliseconds.
+
+<P> Inlets or outlets are configured in Pd either for messages or audio; it's
+an error to connect an audio outlet to a non-audio inlet or vice versa; usually
+these errors are detected at "sort time" when audio is started or the network
+changed with audio running. An object's leftmost inlet may accept both audio
+and messages; any other inlet is either one or the other. There is no quick
+way to tell whether an inlet or output is for audio or messages; consult the
+help window for the object.
+
+<P>
+The audio network, that is, the tilde objects and their interconnections,
+must be acyclic. If there are loops, you will see the error message at "sort
+time." When errors are reported at sort time there is no easy way to
+find the source of the error. You can build algorithms with feedback using
+nonlocal signal connections.
+
+<P>
+Your subpatches can have audio inlets and outlets via the inlet~ and outlet~
+objects.
+
+<H4> <A name=s4.3> 2.4.3. converting audio to and from messages </A> </H4>
+
+<P> If you want to use a control value as a signal, you can use the sig~ object
+to convert it. The +~, -~, *~, /~, osc~, and phasor~ objects can be configured
+to take control or signal inputs.
+
+<P> The other direction, signal to control, requires that you specify at what
+moments you want the signal sampled. This is handled by the snapshot~ object,
+but you can also sample a signal with tabwrite~ and then get access it via
+tabread or tabread4 (note the missing tildes!). There are also analysis
+objects, the simplest of which is "env~", the envelope follower.
+
+<H4> <A name=s4.4> 2.4.4. switching and blocking </A> </H4>
+
+You can use the switch~ or block~ objects to turn portions of your audio
+computation on and off and to control the block size of computation. There
+may be only one switch~ or block~ object in any window; it acts on the entire
+window and all of its subwindows, which may still have their own nested
+switch~/block~ objects. Switch~ and block~ take a block size and an overlap
+factor as arguments; so for instance, "block~ 1024 4" specifies 1024 sample
+blocks, overlapped by a factor of 4 relative to the parent window. Switch~
+carries a small computational overhead in addition to whatever overhead is
+associated with changing the block size.
+
+<P> Larger block sizes than 64 should result in small increases in run-time
+efficiency. Also, the fft~ and related objects operate on blocks so that
+setting the block size also sets the number of FFT channels. You may wish
+to use block sizes smaller than 64 to gain finer resolutions of message/audio
+interaction, or to reduce "block delay" in feedback algorithms. At the
+(untested) extreme, setting the block size to one allows you to write your
+own recursive filters.
+
+<P> You can use switch~ to budget your DSP computations; for instance you might
+want to be able to switch between two synthesis algorithms. Put each algorithm
+in its own subpatch (which can have sub-sub patches in turn, for a voice bank
+for instance), and switch each one off as you switch the other one on. Beware
+of clicks; if you have a line~ controlling output level, give it time to ramp to
+zero before you switch it off or it will be stuck at a nonzero value for the
+next time it comes back on.
+
+<P> When a subpatch is switched off its audio outputs generate zeros; this costs a
+fairly small overhead; a cheaper way to get outputs is to use throw~ inside
+the switched module and catch~ outside it.
+
+<H4> <A name=s4.5> 2.4.5. nonlocal signal connections </A> </H4>
+
+You may wish to pass signals nonlocally, either to get from one window to another, or
+to feed a signal back to your algorithm's input. This can be done using
+throw~/catch~, send~/receive~, or delwrite~/delread~ pairs. Throw~ and catch~
+implement a summing bus; throw~ adds into the bus and catch~ reads out the
+accumulated signal and zeros the bus for the next time around. There can be
+many throw~ objects associated with a single catch~, but a throw~ can't talk to
+more than one catch~. You can reset the destination of a throw~ if you want to.
+
+<P> Send~ just saves a signal which may then be receive~d any number of times; but
+a receive~ can only pick up one send~ at a time (but you can switch between
+send~s if you want.)
+
+<P> Don't try to throw~ and catch~ or send~ and receive~ between windows with
+different block sizes. The only re-blocking mechanisms which are well tested
+are inlet~ and outlet~.
+
+<P> When you send a signal to a point that is earlier in the sorted list of tilde
+objects, the signal doesn't get there until the next cycle of DSP computation,
+one block later; so your signal will be delayed by one block (1.45 msec by
+default.) Delread~ and delwrite~ have this same restriction, but here the 1.45
+msec figure gives the minimum attainable delay. For nonrecursive algorithms, a
+simple flanger for example, you might wish to ensure that your delread~ is
+sorted after your delwrite~. The only way to ensure this is to create the
+delread~ after you created the delwrite~; if things get out of whack, just
+delete and re-create the delread~.
+
+<H4> <A name=s5> 2.5. scheduling </A> </H4>
+
+Pd uses 64-bit floating point numbers to represent time, providing sample
+accuracy and essentially never overflowing. Time appears to the user
+in milliseconds.
+
+<H4> <A name=s5.1> 2.5.1. audio and messages </A> </H4>
+
+Audio and message processing are interleaved in Pd. Audio processing is
+scheduled every 64 samples at Pd's sample rate; at 44100 Hz. this gives a
+period of 1.45 milliseconds. You may turn DSP computation on and off by
+sending the "pd" object the messages "dsp 1" and "dsp 0."
+
+<P> In the intervals between, delays might time out or external conditions
+might arise (incoming MIDI, mouse clicks, or whatnot). These may cause a
+cascade of depth-first message passing; each such message cascade is completely
+run out before the next message or DSP tick is computed. Messages are never
+passed to objects during a DSP tick; the ticks are atomic and parameter changes
+sent to different objects in any given message cascade take effect
+simultaneously.
+
+<P> In the middle of a message cascade you may schedule another one at a delay
+of zero. This delayed cascade happens after the present cascade has finished,
+but at the same logical time.
+
+<H4> <A name=s5.2> 2.5.2. computation load </A> </H4>
+
+<P> The Pd scheduler maintains a (user-specified) lead on its computations;
+that is, it tries to keep ahead of real time by a small amount in order to be
+able to absorb unpredictable, momentary increases in computation time. This
+is specified using the "audiobuffer" or "frags" command line flags (see <a
+href="x3.htm" name=s3>getting Pd to run </A>).
+
+<P> If Pd gets late with respect to real time, gaps (either occasional or
+frequent) will appear in both the input and output audio streams. On the
+other hand, disk strewaming objects will work correctly, so that you may use
+Pd as a batch program with soundfile input and/or output. The "-nogui"
+and "-send" startup flags are provided to aid in doing this.
+
+<P> Pd's "realtime" computations compete for CPU time with its own GUI, which
+runs as a separate process. A flow control mechanism will be provided someday
+to prevent this from causing trouble, but it is in any case wise to avoid
+having too much drawing going on while Pd is trying to make sound. If a
+subwindow is closed, Pd suspends sending the GUI update messages for it;
+but not so for miniaturized windows as of version 0.32. You should really
+close them when you aren't using them.
+
+<H4> <A name=s5.3> 2.5.3. determinism </A> </H4>
+
+All message cascades that are scheduled (via "delay" and
+its relatives) to happen before a given audio tick will happen as scheduled
+regardless of whether Pd as a whole is running on time; in other words,
+calculation is never reordered for any real-time considerations. This is done
+in order to make Pd's operation deterministic.
+
+<P> If a message cascade is started by an external event, a time tag is given
+it. These time tags are guaranteed to be consistent with the times at which
+timeouts are scheduled and DSP ticks are computed; i.e., time never decreases.
+(However, either Pd or a hardware driver may lie about the physical time an
+input arrives; this depends on the operating system.) "Timer" objects which
+meaure time intervals measure them in terms of the logical time stamps of the
+message cascades, so that timing a "delay" object always gives exactly the
+theoretical value. (There is, however, a "realtime" object that measures real
+time, with nondeterministic results.)
+
+<P> If two message cascades are scheduled for the same logical time, they are
+carried out in the order they were scheduled.
+
+<H4> <A name=s6> 2.6. semantics </A> </H4>
+
+This section describes how objects in Pd are created, how they store data and
+how object and other boxes they pass messages among themselves.
+
+<H4> <A name=s6.1> 2.6.1. creation of objects </A> </H4>
+
+The text in a box has a different function depending on whether it is a message,
+atom (number/symbol), or object box. In message boxes the text specifies the
+message or messages it will send as output. In atom boxes the text changes
+at run time to show the state of the box, which is either a number or a symbol.
+
+<P> In an object box, as in a message box, the text specifies a message; but
+here the message is to be passed to Pd itself, once, and the
+message's effect is to create the object in question. When you open a file,
+all the objects created are created using their text as "creation messages."
+If you type a new message into an object box (or change it), the old object is
+destroyed and the message is used to create the new one.
+
+<P> The selector of the message (the first word in the message) is a selector
+which Pd interprets to mean which type of object to create. Any message
+arguments (called "creation arguments") are used to parametrize the object
+being created. Thus in "makenote 64 250" the selector "makenote" determines
+the class of object to create and the creation arguments 64 and 250 become the
+initial velocity and duration.
+
+<H4> <A name=s6.2> 2.6.2. persistence of data </A> </H4>
+
+Among the design principles of Pd is that patches should be printable, in the
+sense that the appearance of a patch should fully determine its functionality.
+For this reason, if messages received by an object change its action, since the
+changes aren't reflected in the object's appearance, they are not saved as part
+of the file which specifies the patch and will be forgotten when the patch is
+reloaded. In the same way, if you delete and then recreate an object the
+original object's state is not retained but is instead reinitialized (possibly
+as specified by creation arguments.)
+
+<P> An exception is made for subpatches whose "state" is the configuration of
+the subpatch; as a special case, this configuration is restored when the
+patch is read from a file. Also, if you rename the subpatch, for instance
+typing "pd jane" instead of "pd spot," the contents of the patch are preserved
+and only the text in the object box and the window title of the subpatch are
+changed.
+
+<P> It is probably bad style to specify creation arguments ala "makenote 64 250"
+if you are going to override them later; this is confusing to anyone who tries
+to understand the patch.
+
+<H4> <A name=s6.3> 2.6.3. message passing </A> </H4>
+
+Messages in Pd consist of a selector (a symbol) and zero or more arguments
+(which may be symbols or numbers). To pass a message to an object, Pd first
+checks the selector against the class of the object. Message boxes all are
+of one class and they all take the same incoming messages and dispense them
+according to their state, that is, the text typed into the box. The same
+holds for atom boxes (number or symbol) except that their state may change
+(it consists of the number or symbol showing).
+
+<P> Object boxes may have many different classes. The class is usually
+determined by the selector of the creation message, i.e., the first atom of the
+creation message which is usually a symbol.
+
+<P> Each class comes with a fixed collection of messages it may be sent. For
+esxample, the "float" or "f" object takes "bang" and "float." These messages
+are sent to "float" objects (objects whose class is float) via the leftmost,
+hot inlet. (The right inlet is a separate, auxiliary object.) Objects of
+class "float" respond to the message "bang" by outputting their current value,
+that is, by sending a "float" message to their outlet. They respond to "float"
+messages by setting their value and then outputting it.
+
+<P> Each other class (like "float") in Pd has its own protocol for responding
+to messages it is sent, and may take "float" and "bang" messages, or others
+in addition or instead of them.
+
+<H4> <A name=s6.4> 2.6.4. inlets and lists </A> </H4>
+
+The leftmost connection point at the top of most objects represents the object
+itself. Any other dark rectangle is a separate object called an "inlet"
+although in Pd there are 4 individual inlet classes. The class of the inlet
+determines which messages it will take: symbol, float, or other; and the inlet
+forwards the message either to the object proper or to some proxy, usually
+one that the object creates for the occasion.
+
+<P> Unless they arrange otherwise by defining a "list" method, objects respond
+to the "list" message by distributing the arguments of the message to their
+inlets, except for the first argument which is passed as a "float" or
+"symbol" message to the object proper.
+
+<H4> <A name=s6.5> 2.6.5. dollar signs </A> </H4>
+
+In message or object boxes, message arguments starting with a dollar sign
+and a number (like "$1" or "$3-bazoo") are variables which are substituted
+with values supplied as part of the environment the message is passed in.
+In the case of message boxes, the environment consists of the arguments of
+the "list" message (possibly extrapolated from "bang," "float,"
+or other) that the message box is responding to. Thus, if a message box gets
+"23 skidoo" and if it contains the text, "$2 until $1," out comes the message,
+"skidoo until 23."
+
+<P> Object boxes contain text wwhich forms a message to be sent to Pd to create
+and initialize the object. Here, $1, etc., are taken from the context in which
+the patch was loaded. When the patch is a new document or opened from a file
+the "$" variables are undefined. But if the patch is an abstraction (see the
+next section) they are
+taked from the abstractions' creation arguments.
+
+<P> Constructions such as "$1-x" are expanded by string concatentation. This
+is the mechanism for making local variables. In particular, $0 in an abstraction
+is a counter which is guaranteed to be unique to that abstraction, so sends and
+receives with names like "$0-bear" can be used as local send/receive pairs.
+
+<H4> <A name=s7> 2.7. subpatches </A> </H4>
+
+Pd offers two mechanisms for making subpatches, called "one-off subpatches"
+and "abstractions." In either case the subpatch appears as an object box
+in a patch. If you type "pd" or "pd my-name" into an object box, this creates
+a one-off subpatch. For instance, in this fragment:
+
+<P><CENTER> <IMG src="fig7.1.jpg"> </CENTER><P>
+
+the box in the middle, if clicked on, opens the sub-patch shown here:
+
+<P><CENTER> <IMG src="fig7.2.jpg"> </CENTER><P>
+
+<P> The contents of the subpatch are saved as part of the parent patch, in
+one file. If you make several copies of a subpatch you may change them
+individually.
+
+<P> The objects, "inlet,", "inlet~," "outlet," and "outlet~,", when put in a
+subpatch, create inlets and outlets for the object box containing the subpatch.
+This works equally for one-off subpatches and abstractions. The inlet~ and
+outlet~ versions create inlets and outlets for audio signals. You can't mix
+messages and audio in a subpatch inlet or outlet; they must be one or the other
+exclusively. Inlets and outlets appear on the invoking box in the same left-to-right
+order as they appear in the subpatch.
+
+<H4> <A name=s7.1> 2.7.1. abstractions </A> </H4>
+
+<P> To make an abstraction, save a patch with a name such as "abstraction1.pd"
+and then invoke it as "abstraction1" in an object box:
+
+<P><CENTER> <IMG src="fig7.3.jpg"> </CENTER><P>
+
+<P> Here we're invoking a separate file, "abstraction1.pd", which holds the
+patch shown here (the border is the same as for the subpatch above):
+
+<P><CENTER> <IMG src="fig7.4.jpg"> </CENTER><P>
+
+You may create many instances of "abstraction1" or invoke it from several
+different patches; and changing the contents of "abstraction1" will affect all
+invocations of it as they are created. An analogy from the "c" programming
+language is that one-off subpatches are like bracketed blocks of code and
+abstractions are like subroutines.
+
+<P> Abstractions are instantiated by typing the name of a patch (minus the ".pd"
+extension) into an object box. You may also type arguments; for instance if
+you have a file "my-abstraction.pd" you may type "my-abstraction 5" to set the
+variable $1 to 5. This is defined only for object boxes (not for messages) in
+the abstraction. (For message boxes, "$1", etc, have a different meaning as
+described above.) If you want to send a message with a $1 in the sense of a
+creation argument of an abstraction, you must generate it with an object box
+such as "float $1", "symbol $1", or perhaps "pack $1 $2", which may then be
+sent to a message box.
+
+<P> The corresponding feature in Max (both Opcode and Ircam) was the "#1"
+construct. In a Max abstraction, "#1", etc., are replaced by the creation
+argument. This has the disadvantage that you can't edit the abstraction as
+instantiated in the patch since the "#" variables are substituted. In Pd the
+"$" variables in object boxes are spelled literally as "$" variables so that
+it's meaningful to edit them from within their calling patch. On the Pd side,
+however, there is the disadvantage that it's confusing to have "$" expanded at
+a different time in an object box than in a message box. In an object box, the
+"$" argument is expanded at creation time, and in a message box, at message
+time.
+
+<H4> <A name=s7.2> 2.7.2. Graph-on-parent subpatches </A> </H4>
+
+If you open the "properties" dialog for a subpatch or an abstraction, you can
+check the "graph on parent" box to have the controls of the subpatch/abstraction
+appear on the parent. For instance, here is an invocation of "abstraction2":
+
+<P><CENTER> <IMG src="fig7.5.jpg"> </CENTER><P>
+
+where the patch "abstraction2.pd" contains:
+
+<P><CENTER> <IMG src="fig7.6.jpg"> </CENTER><P>
+
+Here, the number box in the abstraction shows up on the box that invoked
+the abstraction. The "graph on parent" flag is set in the abstraction
+(and is saved as part of the abstraction); to set it, open the "properties"
+dialog for the "abstraction2" canvas by right-clicking on any white space
+in the patch.
+
+<P> To open the subpatch, right click on the object and select "open". (On
+Macintoshes without a 2-button mouse, you can double-click in edit mode
+instead.) It doesn't work just to click on the object in run mode since clicks
+are sent to visible controls and/or arrays.
+
+<P> When the sub-patch is closed, all controls in it appear on the object
+instead; so the number box in the sub-patch in the example above is the same
+one as you see in the box. Only controls are made visible in this way
+
+<H4> <A name=s8> 2.8. numeric arrays </A> </H4>
+
+Linear arrays of numbers recur throughout the computer musician's bag of tricks,
+beginning with the wavetable oscillator. The wavetable oscillator later was
+reinvented as the looping sampler. Also, table lookup is used for nonlinear
+distortion of audio signals. In the domain of control, arrays of numbers
+can specify control mappings, probability densities, voicing data, and much
+more.
+
+<P> Arrays in Pd should be allocated (and possible read in from a file) before
+beginning to make sound, since memory allocation and disk operations may take
+long enough to cause audio buffer overruns or underruns. Pd provides two ways
+to define new arrays, as "graphs" and "tables". In either case the array
+has a pre-defined name and size (i.e., number of points). Elements of the
+array are stored as floating-point numbers, 4 bytes apiece
+
+<P> If you use an array to store a one-second sound at 44.1 kHz you will need
+176 kilobytes, or a one-minute sound, 10.6 megabytes. To store a sound with
+two or more channels, use a separate array for each channel.
+
+<P> Arrays are also useful as transfer functions, for example for nonlinear
+distortion of an audio signal, or to map a control onto a synthesis parameter.
+In situations like this one typically uses much shorter arrays, of no more
+than a few hundered elements. They are also useful for storing measured
+spectra derived from the fft~ objects, and probably for many other uses.
+
+<P> Arrays usually appear within subpatches created to house them, whether
+in "graph on parent" form (so that you see them within a rectangle drawn on
+the containing patch), or as a regular subpatch (which you see as a text box.)
+In the "graph on parent" form, an array appears as shown:
+
+<P><CENTER> <IMG src="fig8.1.jpg"> </CENTER><P>
+
+<P> Arrays are indexed from 0 to N-1 where N is the number of points in the
+array. You can read an array value using the tabread object:
+
+<P><CENTER> <IMG src="fig8.2.jpg"> </CENTER><P>
+
+Here we see that the third point of the array (index 2) has the value 0.4.
+To write into the array you can use the tabwrite object:
+
+<P><CENTER> <IMG src="fig8.3.jpg"> </CENTER><P>
+
+In this example, sending the message sets the third element to 0.5. (You
+may also send the two numbers to the two inlets separately.)
+
+<P> The two previous examples showed control operations to read and write from
+and to arrays. These may also be done using audio signals. For example,
+the patch below creates a 440 Hz. tone with "array1" as a waveform:
+
+<P><CENTER> <IMG src="fig8.4.jpg"> </CENTER><P>
+
+Here phasor~'s outputs a sawtooth wave, repeating 440 times per second, whose
+output range is from 0 to 1. The multiplier and adder adjust the range from
+1 to 11, and then the values are used as indices for tabread4~, which is a
+4-point interpolating table lookup module. (Much more detail is available in
+the audio example patches in the "pure documentation" series.)
+
+<P> To create a new array, select "array" from the "put" menu. Up will come
+a dialog window to set initial properties of the array. By default, a
+new graph is created to hold the array, but it may also be housed in the
+most recently created graph instead. Other properties may be specified there
+and/or changed later using the "properties" dialog.
+
+<P> If you select "properties" on an array in a graph, you two dialogs, one
+for the array and one for the graph. The array dialog looks like this:
+
+<P><CENTER> <IMG src="fig8.5.jpg"> </CENTER><P>
+
+You may use this to change the name and size, in addition to another property,
+"save contents". If "save contents" is selected, the array's values are stored
+in the containing patch; otherwise they're initialized to zero each time the
+patch is reloaded. If you intend to use arrays to store sounds, you will
+probably not wish to store them in the patch but as separate soundfiles. This
+will be more efficient, and you may also then use a sound editor to modify them
+outside Pd.
+
+<P> If you check "delete me" and then "OK", the array wil be deleted. This is
+an odd interface for deleting an object, and is only provided because Pd
+lacks a mechanism for selecting arrays (so that "cut" could serve).
+
+<P> The graph dialog (which also pops up) is shown here:
+
+<P><CENTER> <IMG src="fig8.6.jpg"> </CENTER><P>
+
+<P> The X bounds initially range from 0 to the number of points in the table
+minus one (this is a good choice for arrays, although graphs holding other
+kinds of objects might require other X bounds.) The Y bounds should be
+chosen to reflect the natural range of the table, so that stored sounds
+would naturally range from -1 to 1, but a sequence of frequency values might
+range from 0 to 20,000. Finally, you choose the screen size of the graph,
+width and height, in screen pixels.
+
+<P> Many other operations are defined for arrays; see the related patches
+in the tutorial (starting at 2.control/15.array.pd) for more possibliities.
+
+<H4> <A name=s8> 2.9. Data structures </A> </H4>
+(Note: this section is adapted from an article submitted to ICMC 2002.)
+
+<P> The original idea in developing Pd was to make a real-time computer music
+performance environment like Max, but somehow to include also a facility for
+making computer music scores with user-specifiable graphical representations.
+This idea has important precedents in Eric Lindemann's Animal and Bill Buxton's
+SSSP. An even earlier class of precedents lies in the rich variety of paper
+scores for electronic music before it became practical to offer a
+computer-based score editor. In this context, scores by Stockhausen (<I>
+Kontakte</I> and <I> Studie II</I>) and Yuasa (<I>Toward the Midnight Sun</I>)
+come most prominently to mind, but also Xenakis's <I>Mycenae-alpha</I>, which,
+although it was realized using a computer, was scored on paper and only
+afterward laboriously transcribed into the computer.
+
+<P> Pd is designed to to offer an extremely unstructured environment for
+describing data structures and their graphical appearance. The underlying
+idea is to allow the user to display any kind of data he or she wants to,
+associating it in any way with the display. To accomplish this Pd introduces
+a graphical data structure, somewhat like a data structure out of the C
+programming language, but with a facility for attaching shapes and colors to
+the data, so that the user can visualize and/or edit it. The data itself can
+be edited from scratch or can be imported from files, generated
+algorithmically, or derived from analyses of incoming sounds or other data
+streams.
+
+Here is one simple
+example of a very short musical sketch realized using Pd:
+
+<P><CENTER> <IMG src="fig9.1.jpg"> </CENTER><P>
+
+The example, which only lasts a few seconds, is a polyphonic collection of
+time-varying noise bands. The graphical ``score" consists of six objects, each
+having a small grab point at left, a black shape to show dynamic, and a colored
+shape to show changing frequency and bandwidth. The horizontal axis represents
+time and the vertical axis, frequency (although, as explained later, this
+behavior isn't built into pd). The dynamic and frequency shapes aren't
+constrained to be connected or even to be proximate, but since they pertain to
+the same sound their horizontal positions line up. In this example the last
+(furthest-right) object is percussive (as seen by the black shape) and has a
+fixed frequency and bandwidth, whereas the large, articulated shape in the
+center has a complicated trajectory in both frequency and dynamic. The color
+of the frequency trace determines the voice number used to realize it.
+
+<P> Each object is thus composed of a combination of scalar values (color;
+aggregate position in X and Y coordinates) and array values (time/value
+pairs for the black traces and time/frequency/bandwidth triples for the
+colored ones.) This is all specified by the user using Pd's ``template"
+mechanism.
+
+<P> Here is the template associated with the graphical objects
+shown above:
+
+<P><CENTER> <IMG src="fig9.2.jpg"> </CENTER><P>
+
+Templates consist of a data structure definition (the "struct" object) and
+zero or more drawing instructions ("filledpolygon" and "plot"). The "struct"
+object gives the template the name, "template-toplevel." The data structure
+is defined to contain three floating point numbers named "x", "y", and
+"voiceno," and two arrays, one named "pitch" whose elements belong to another
+template named "template-pitch," and similarly for the array "amp."
+
+<P> In general, data structures are built from four data types: scalar floats
+and symbols, arrays (whose elements share another, specified template) and
+lists (whose elements may have a variety of templates). The contents of a Pd
+window themselves form a list. Pd's correlate of Max's "table" object is
+implemented as a top-level array whose elements are scalars containing a single
+floating-point number.
+
+<P> Data structures in Pd may nest arbitrarily deeply using the array and list
+types. For example, a collection of sinusoidal tracks from an analysis engine
+could be implemented as an array of arrays of (pitch, amplitude)
+pairs; this appears as example 12 in Pd's FFT object online tutorial.
+
+<P> After the "struct" object in the template shown above, the remaining
+three objects are <I> drawing instructions </I> , first for a rectangle
+("filledpolygon"), and then for two arrays. The various graphical
+attributes that are specified for drawing instructions may be numerical
+constants or data structure field names; in the latter case the value varies
+depending on the data. For instance, the second creation argument to
+"plot" is the color. The first "plot" plots the "amp" field and the
+color is given as 0, or black. The second one plots "pitch" using the color
+"voiceno". In this way the color of the second trace is attached to the
+"voiceno" slot in the data structure, so that color will vary according to its
+"voiceno" slot.
+
+<H4> <A name=s9.1> 2.9.1. Traversal </A> </H4>
+
+<P> Pd objects are provided to traverse lists and arrays, and to address
+elements of data structures for getting and setting. Here is a patch showing
+how these facilities could be used, for example, to sequence the graphical
+score shown above:
+
+<P><CENTER> <IMG src="fig9.3.jpg"> </CENTER><P>
+
+<P> Pd has no built-in sequencer, nor even any notion that "x" values should be
+used as a time axis. (However, a "sort" function is provided, which reorders
+a list from left to right, on the assumption that users might often want to use Pd
+data collections as x-ordered sequences.) Recording sequences of events into
+lists, and/or playing the lists back as sequences, are functionalities that the
+user is expected to supply on top of Pd's offerings, which, it is hoped, would
+allow those functionalities within a much larger range of possibilities, to
+include random reorderings of events, score following, self-modifying scores,
+reactive improvisation, and perhaps much more.
+
+<P> Traversal of data is made possible by adding a new type of atom, "pointer",
+to the two previously defined types that make up messages, to wit, numbers and
+symbols. Unlike numbers and symbols, pointers have no printed form and thus
+can't be uttered in message boxes. Traversal objects such as "pointer" and
+"get" (among several others) can generate or use pointers. The pointer data
+type is also integrated into pipe-fitting objects such as "pack",
+"unpack",
+and "route".
+
+<P> In the patch shown above, the topmost "pointer" object holds a pointer to
+the next object to "play" (by sending it to one of the "voice"
+abstractions at bottom.) The pointer object takes a "traverse" message to
+set it to the head of the list (named "pd-data"), and "next" messages to
+move to (and output) the next datum in the list (i.e., the next in the list of
+six objects in the score). Another "pointer" object is also used, further
+down, as a storage cell for pointers just as "float" is for numbers.
+
+<P> The center of any sequencer is always the "delay" object, which must be
+fed the time difference between each event (including the non-event of hitting
+"start") and the next. As we extract each of the six objects in the score, we
+must wait the delay for playing that object, and then send its pointer to one
+of the "voice" abstractions to play it. However, we have to inspect the
+object itself to know the delay before playing it. So, in the loop, we peel off
+the first remaining object to play and inspect the time difference between it
+and the previous one, using this value to set the delay, but also storing the
+pointer in the lower "pointer" and "pack" objects.
+
+<P> The time difference needed to set the delay object is obtained using the
+"get template-toplevel x" object. (This is converted to incremental time
+("-"), corrected for tempo, and fed to the delay.) Pd provides
+the "get" and "set"
+objects for reading and writing values from data structures.
+The two "get" objects shown here obtain the "x" and "voiceno" fields
+of the current object. The template name (template-toplevel) is supplied
+to the "get" objects so that they can look up the offset of the necessary
+field(s) in advance, for greater run-time efficiency.
+
+<P> Once the delay has expired, the object's pointer is recalled (the lower
+"pointer" object), and the voice number is recalled. This is packed with
+the pointer itself and routed, so that the pointer goes to the appropriate
+voice. The voice number is shown as the color of the frequency trace in
+"999" units (first digit red, second green, third blue) and the "route" is
+arbitrarily set up to select among the six primary and secondary colors plus
+black.
+
+<P> The details of extracting the pitch and dynamic breakpoints from the arrays
+defined in the template are managed in the "voice" abstraction.
+The "voice"
+abstraction receives a
+pointer to a given object and manages the sequencing of the arrays; so it
+contains two sequencers itself. The nesting of the overall structure of
+the sequencer patch mirrors the nesting of the original data structures.
+Finally, the voice abstraction puts its audio output on a summing bus.
+
+<P> More general patches can easily be constructed which access heterogeneous lists
+of objects (having different templates). In this way, an arbitrarily rich
+personal "score language" can be developed and sequenced.
+
+<H4> <A name=s9.2> 2.9.2. Accessing and changing data </A> </H4>
+
+<P> In general, accessing or changing data is done via "pointers" to
+"scalars". Numbers and symbols withing scalars are accessed using the
+"get" object and changed, in the same way, using "set". Since lists
+and arrays are composed of scalars, every actual number or symbol in a data
+heap will be a number or symbol element of some scalar. To access them, it
+suffices to have objects to chase down elements of lists and arrays (given
+either a global name or a pointer to the containing scalar).
+
+<P> Lists are traversed in the way shown above; to get to a sublist of a scalar,
+the "get" object will provide a pointer, in the same way as it provides
+"float" or "symbol" elements of scalars. For arrays, an
+"element" object is provided which, given a scalar, a field name and
+a number, chases down the numbered, scalar, element of the named array field.
+
+<P> To alter "float" or "symbol" elements of scalars is straightforward
+using the "set" object, but arrays and lists can't be set by assignment;
+there is no suitable data type available withing messages. Lists could
+possibly be "settable" by passing pointers to other lists, but permitting this
+would have required either automatically doing deep copies of data structures
+to carry out the assignments, or else implementing a garbage collecting memory
+management system, either of which would be difficult to realize within
+real-time computation time constraints. Instead, all the data hanging from a
+scalar is considered as belonging to that scalar, and is left in memory until
+the scalar is deleted; the data may be changed atom by atom, but primitives
+are not provided which would imply unpredictable execution times.
+
+<P> The "getsize" and "setsize" objects are provided to access or change
+the number of elements in the array. For lists, an "append" object
+appends a new scalar for a given template to a list, after the element pointed
+to. (To insert a scalar at the beginning of a list, the pointer can be set to
+the "head" of the list, a formal location before the first list item.)
+Deletion is less flexible; the only operation is to delete an entire list.
+(There's no reason not to provide finer-grain deletion mechanisms except that
+it's not clear how to protect against stale pointers efficiently, except by
+voiding the entire collection of pointers into a list.)
+
+<H4> <A name=s9.3> 2.9.3. Editing </A> </H4>
+
+<P> The graphical score shown above can be edited by dragging breakpoints, or
+by adding and deleting them, using mouse clicks. Also, entire objects or
+collections of them may be copied, pasted, and dragged around the screen.
+Alternatively, there is an editable (or computer generate-able or parse-able)
+text representation for the data, which may be seen or changed in a dialog
+window or read and written to external text files.
+
+<P> Since the graphical presentation of data objects is determined by drawing
+instructions, the drawing instructions are interpreted backwards to alter data
+as a result of mouse operations. If a given graphical dimension is controlled
+by a variable, that variable is then controlled by dragging along that
+dimension; if the dimension is constant, it can't be altered by dragging.
+
+<P> Tricky situations can arise when the user changes the contents of templates.
+A change in drawing instructions can be accommodated by simply tracking
+down and redrawing all data objects using the template. However, changing
+the "struct" object itself make for less straightforward situations. The
+user might wish to reorder fields, delete them, add new ones, or rename them.
+When a "struct" object changes, Pd automatically conforms the data from the old
+structure to the new one. Fields with the same name as previously are maintained
+(reordering them as necessary); and if a field disappears but another of the
+same type appears, the new one(s) are taken to be renamings of the old one(s)
+in order of appearance. New fields which cannot be matched in this way with
+previously existing ones are assumed to be new and are initialized.
+
+<P> It can happen that two "struct" objects compete to define the same data
+structure, or that the user reads in data from a file which expects a different
+version of the structure, or alternatively, that the "struct" object for
+existing data objects disappears. For this reasn, Pd maintains a private
+representation of the last active version of a "struct" until all
+similarly named "structs," as well as all data using that "struct", have
+disappeared. If the user introduces a new version of the "struct" and only
+later deletes the "current" one, the data is only conformed to the new version
+once the old one is deleted. In this way we avoid getting into situations
+where data is left hanging without its structure definition, or where data ends
+up belonging to two or more structures of the same name. The worst that can
+happen is that data may lose their drawing instructions, in which case Pd
+supplies a simple default shape.
+
+<H4> <A name=s9.4> 2.9.4. Limitations </A> </H4>
+
+<P> When examples get more complicated and/or dense than the one shown here, it
+becomes difficult to see and select specific features of a data collection;
+more work is needed to facilitate this.
+There should be some facility for turning drawing instructions on and off, or
+perhaps for switching between versions of a template, depending on the user's
+desired view. There should also be a callback facility in the template for
+when an object is edited with the mouse, so that the user can bind actions to
+mouse clicks.
+
+<P> More generally, the collection of traversal objects that Pd provides is
+adequate to support a variety of modes of data collection and use, such as
+analysis and sequencing. But the patches required to traverse the data
+collections are not always simple. It would be desirable to find a more
+straightforward mechanism than that provided by the "pointer", "get"
+and "set" objects.
+
+<P> The "data" facility, although part of the original plan for Pd, has only
+recently been implemented in its current form, and as (hopefully) the user base
+grows there will surely be occasions for many further extensions of the data
+handling primitives and the graphical presentation and editing functions.
+
+</BODY>
+</HTML>
diff --git a/pd/doc/1.manual/x3.htm b/pd/doc/1.manual/x3.htm
new file mode 100644
index 00000000..18d220a6
--- /dev/null
+++ b/pd/doc/1.manual/x3.htm
@@ -0,0 +1,854 @@
+<HTML>
+<HEAD>
+<TITLE>Pd Documentation 3</TITLE>
+</HEAD>
+<BODY bgcolor="#ffffff">
+<SMALL>
+<div style="width:6.5in; margin-left:.5in">
+
+<CENTER> <B>
+Pd Documentation chapter 3: Getting Pd to run
+</B> </CENTER>
+<BR>
+<A href=index.htm#s3> back to table of contents </A>
+<BR><BR>
+
+Pd runs under Irix, Windows, and Linux.
+How to get Pd up and running depends on your operating system,
+but the overall strategy is the same.
+You must first get and install it, and
+then untangle whatever problems arise in handling audio and MIDI input
+and output, and finally get Pd to meet its real-time obligations reliably.
+
+In case of trouble also consult the Pd mailing list archive on
+ <A href="http://iem.kug.ac.at/mailinglists/pd-list/">
+ http://iem.kug.ac.at/mailinglists/pd-list/</A>
+, which often has late-breaking news about configuration problems and solutions.
+
+
+<P>
+You may be interested in getting only audio output or audio input, or
+you may need both to run simultaneously. By default, Pd will try to run
+both, but if you don't need either input or output, you may find that Pd
+runs more reliably, or at least more efficiently, with the unused direction
+turned off. This is controlled by Pd's command line flags.
+
+<P>
+Depending on your application you will have a more or less stringent latency
+requirement. Ideally, when any input (audio, MIDI, keyboard, network) is
+available, the outputs (in particular the audio output) should react instantly.
+In real life, it is necessary to buffer the audio inputs and outputs, trying
+always to keep some number of milliseconds ahead of real time to prepare for the
+inevitable occasions where the CPU runs off to service some different task
+from Pd. How small this latency can be chosen depends on your OS and your
+audio driver.
+
+<P>
+To test audio and MIDI, start Pd and select "test Audio and MIDI" from the
+"help" menu.
+
+<P> TIP: If Pd starts up but you get distortion or glitches in the audio
+output, this could be either because the "audio I/O buffer" isn't big enough,
+or else because the CPU load of the patch you're running is too great for the
+machine you have, or else because the ADC and DAC are out of sync or even at
+different sample rates. To test for the first possibility, try increasing the
+"-audiobuf" parameter in the command line (but see also under your OS below.)
+For the second, start up your favorite performance monitor program; and for the
+third, try starting Pd up with ADCs disabled.
+
+<P> Here are instructions for getting and installing Pd for the four
+operating systems it runs on: IRIX, MS Windows, Linux, and Max OSX.
+
+<H4> <A name=s1.1> 3.1. IRIX (SGI machines) </A> </H4>
+
+<P> Download Pd, which will be a "tar.Z" file. You can unpack this by
+typing "zcat [name].tar.Z | tar xf -" to a shell. This creates a directory
+named "pd".
+
+<P>
+Starting with release 0.25, Pd should come in "n32" and "o32" versions.
+"o32" is the default and will run on IRIX 5.x and up. "n32" runs faster,
+but only on 6.x and up. Also, "externs" have to be updated for n32. The
+"pd" executable (bin/pd in the distribution) is a symbolic link to either
+"pd-o32" or "pd-n32."
+
+<P> NOTE: "externs" appear to be broken in the N32 version... I'm not sure
+how long this has been true. If you want to use external objects, you have
+to use the O32 version.
+
+<P>
+Please note that the path to the Pd executable program can't contain
+space characters; don't put it in a directory named "Program Files"
+for example.
+
+<P>
+If for example you put Pd in ~, the executable program
+will be ~/pd/bin/pd. The program looks at its command line to
+figure out where it is, so it's best to invoke Pd by its full pathname.
+You should always invoke Pd from a Unix shell because many important
+messages appear on the standard error.
+
+<P>
+The simplest way to invoke Pd is to
+make an alias in your ".cshrc" file (assuming you use the "c" shell) such as:
+<PRE>
+
+ alias pd ~/pd/bin/pd
+
+</PRE>
+(assuming your Pd distribution landed in ~, for example).
+
+<P>
+Pd will open the "default" audio input and output devices, without regard
+for whether they are in sync or not. This will be bad if they aren't; use
+the "-noadc" or "-nodac" flag to disable either the input or output. Pd is
+supposed to handle up to 8 channels of audio in and/or out. (But at least
+one user had to recompile Pd on his Onyx to get 8 channels working.)
+
+<P>
+As to MIDI, Pd simply attempts to open all available MIDI devices for input and
+output, which is probably very bad on anything more recent than my Indy. If
+any MIDI ports fail to open either for input or output, all MIDI is disabled.
+
+<P> Pd has not been fixed to request real-time priority from Irix; it will
+compete with all other processes on your machine for CPU time.
+
+<H5> Audio and MIDI in IRIX </H5>
+
+<P>
+Pd takes command line arguments to set the number of input and output channels
+and the sample rate. These don't affect the SGI's audio settings, which you
+have to set separately using the "audio panel." Pd does detect the audio
+sample rate if you don't specify one on the command line.
+
+<P>
+On SGI machines, you have to work to get MIDI running. Before you start Pd, verify
+that least one MIDI port is configured open. Pd opens the FIRST MIDI port
+that's open. You might want to get rid of the "software" MIDI port if you're
+running 6.x. On Indys, the usual practice is to open serial port number 2
+because some systems configure port 1 as "console" by default. You can use the
+GUI if you want, or else just type
+<PRE>
+
+ startmidi -d /dev/ttyd2
+
+</PRE>
+to get port 2 speaking MIDI, and
+<PRE>
+
+ stopmidi
+
+</PRE>
+to stop it. You can test whether MIDI is configured by typing,
+<PRE>
+
+ ps -dafe | grep midi
+
+</PRE>
+and looking for "startmidi" processes.
+<P>
+It's a good idea to connect your serial port to your MIDI interface before
+typing the "startmidi" command, not afterward, at least in 5.x. We use the
+Opcode Studio 3 interface but in principle any Mac-compatible one should work.
+
+<P>
+The O2 apparently has RS232 ports, not RS422. I think SGI's web site says
+something about how to deal with this.
+
+<H4> <A name=s1.2> 3.2. Microsoft Windows </A> </H4>
+
+<P> Pd is compiled under NT, but shoould work under any version of Windows
+since 95. Pd will appear as a "zip" file. Unzip this, creating a directory
+such as \pd. (You can put it wherever you like but the path should have no
+spaces in it; so "Program Files" would be a bad place.)
+
+<P>
+If for example you put Pd in C:\pd, the executable program will be
+C:\pd\bin\pd. You can simply adjust your path to include C:\pd\bin and just
+invoke "pd" in a command prompt window. You can also make a "command prompt"
+shortcut to start Pd.
+
+<P> Pd requires "TCP/IP networking" to be turned on. This doesn't mean you
+have to be on a real network, but simply that Pd actually consists of two
+programs that make a "network link" to intercommunicate.
+
+<H5> The vanishing window </H5>
+
+<P> Pd is a "command line" program. Most error and diagnostic
+messages from Pd appear on the command prompt window Pd runs from.
+
+<P> If you start Pd from the "run" menu or as a shortcut, and if there's
+a problem with run-time flags (see the Pd command line below), Pd will
+print an error and exit. You won't see this error unless you arrange for the
+"command prompt" or "msdos" window to stay open after Pd exits. One way
+to do this is to make a "batch" file ("run.bat", say) containing the Pd
+command line.
+
+<H5> Audio in Microsoft Windows </H5>
+
+<P>
+You can ask for a list of audio and MIDI devices by typing
+"pd -listdev"; you can then specify which audio and MIDI device to use.
+Type "pd -help" (or make any mistake) to get the syntax for specifying
+which device to use.
+
+<P>
+Most PC sound cards seem to have MIDI built in; you don't seem to have to
+do anything special to get Pd to send and receive MIDI. You can list and
+choose MIDI devices in the same way as audio.
+
+<P>
+MIDI timing is very poor if you are using simultaneous audio input and output;
+if you suppress either audio input or output things will improve somewhat under
+NT; you can apparently get the jitter down to ~40 msec. On W95 performance is
+simply terrible. W98, with either audio input or output suppressed, offers
+fairly good MIDI timing (~5 msec jitter) but crashes occasionally.
+
+<P> Some NT and W98 drivers greet you with a constant trail of "resyncing
+audio" messages. Sometimes you can fix this by invoking Pd with the "-noresync"
+flag.
+
+<H5> ASIO </H5>
+
+<P> As of version 0.35 Pd supports ASIO. Invoke Pd as "pd -asio" and, if
+needed, specify "-sounddev" (etc.) flags to specify which device (see
+"the Pd command line" below.) You can also specify a "-blocksize" different
+from the default (256 samples) and "-audiobuf" in milliseconds. Pd will
+round this down to a power of two buffers, each of "-blocksize" in sample
+frames.
+
+<P> Using an RME Hammerfall, and specifying "-audiobuf 5 -blocksize 32" I
+was able to get about 7 milliseconds of throughput delay (as measured by
+the latency-measurement patch in 7.stuff/tools.) As always, you can specify
+"-channels" to any even number up to the maximum (32, I think) or can specify
+channel count separately for input and output (-inchannels and -outchannels).
+
+<H5> The special joys of Windows 95 </H5>
+
+<P>
+On Windows 95 you can expect a hard time. Every user who tries it seems to
+encounter a new problem. The best way to run Pd is to get into the "MSDOS
+Prompt" program and type \pb\bin\pd to it (or whatever the path ends up being.)
+You can probably put pd's "bin" directory in your path so that you just type
+"pd" to the prompt.
+
+<P>
+You don't want to run Pd from the "run" menu because if it fails to start up
+the window holding the error message will disappear instantly. Ditto for
+clicking on "batch files" or on the Pd executable itself.
+
+<P>
+The most common reason Pd might fail to start up in W95 is not having
+"networking" turned on. Pd is actually two programs that establish an IP
+interconnection. Beware that this sometimes fools Windows into calling your
+ISP for no reason.
+
+<P>
+It is often necessary to specify a huge audio buffer to get steady audio
+output in W95; see the command line arguments below.
+
+<H4> <A name=s1.3> 3.3. Linux </A> </H4>
+
+<P> What to do depends on which flavor of Linux you are running (e.g., Debian
+or Red Hat). The instructions here should work for Pd 0.33 and up regardless of
+your situation, but if you have any trouble just mail msp@ucsd.edu and I'll try
+to figure out what's wrong and update the instructions accordingly.
+
+<P> Before you start, you might want to check that you have the resources Pd
+needs. The main things you need are the C compiler, X windows (including
+the X development package for Pd to link against) and TK. If you're running
+Redhat or Mandrake 7.x or up, I think these are all present by default.
+The RedHat X client developer "RPM" package is called XFree86-devel.
+
+<P> You don't absolutely have to have the X server package running; you can run
+Pd on the microprocessor in your refrigerator as long as it can connect to an X
+server on another machine.
+
+<P> If you're running RedHat you might want to use RPM to install Pd. For
+other linux distributions, download the "tar.gz" version and compile it.
+
+<H5> Getting Pd as an RPM </H5>
+
+<P> Download Pd, perhaps from
+ <a href="http://www.crca.ucsd.edu/~msp/software.html">
+ http://www.crca.ucsd.edu/~msp/software.html</A> ,
+to a file such as "pd-0.33-0.i386.rpm".
+Open a "shell" window, cd to
+the directory containing the file, and type the command,
+<PRE>
+ rpm -i pd-0.33-0.i386.rpm
+</PRE>
+
+<P> (substituting the real file name.) Then you should be able to type "pd"
+to a shell and watch the Pd main window appear.
+
+<H5> Getting Pd as a .tar.gz </H5>
+
+<P>
+Download Pd, perhaps from
+ <a href="http://www.crca.ucsd.edu/~msp/software.html">
+ http://www.crca.ucsd.edu/~msp/software.html</A> ,
+to file such as "pd-linux-033.tar.gz". Open a "shell"
+window, cd to
+the directory containing the file, and type the command,
+<PRE>
+ zcat pd-linux-033.tar.gz | tar xf -
+</PRE>
+which creates a directory named "pd". I do this from my home directory.
+Next, compile it. "CD" to pd and read the INSTALL.txt, or else just cd
+to "pd/src" and type
+
+<BR> ./configure
+<BR> make depend
+<BR> make
+
+<P> You can pass flags to "configure" to customize your compilation:
+
+<PRE>
+ To enable ALSA 0.9x (the latest one), add "--enable-alsa".
+ To enable the older ALSA 0.5x, add "--enable-old-alsa".
+ To enable Ritsch's RME 9652 driver, add --enable-rme".
+ To put Pd in /usr/bin instead of /usr/local/bin, add "--prefix=/bin".
+</PRE>
+
+<P> After "make", just type "~/pd/bin/pd" to run pd.
+
+<P> Alternatively, as superuser, you can run "make install" after "make depend"
+and then anyone on your system can just type "pd" to run it.
+
+<H5> TK support trouble </H5>
+
+Some people have reported a problem with Pd findind the shared libraries,
+"libtcl.so" and "libtk.so". I don't know what causes this, but apparently you
+can fix it, as root, by linking /usr/lib/libtcl8.3.so to /usr/lib/libtcl.so and
+similarly for tk:
+
+<PRE>
+
+# cd /usr/lib
+# ln -s libtk8.3.so libtk.so
+# ln -s libtcl8.3.so libtcl.so
+
+</PRE>
+
+<H5> Testing audio and MIDI. </H5>
+
+<P>
+Next try audio. We want to know whether audio output works, whether audio
+input works, and whether they work simultaneously. First run "aumix" to
+see audio input and output gains and which device is "recording".
+Then test audio output by running
+<PRE>
+ pd -noadc
+</PRE>
+and selecting "test audio and MIDI" from the "help" menu. You should see a
+patch. Turn on the test tone and listen. Do the usual where's-the-signal
+business.
+
+<P>
+Then quit Pd and test audio input via
+<PRE>
+ pd -nodac
+</PRE>
+Re-open the test patch and hit "meter"; look at the levels. 100 dB is a
+hard clip; arrange gains so that the input signal tops out around 80 or 90,
+but no higher.
+
+<P> Now see if your audio driver can do full duplex by typing "pd" with no
+flags. If you see error messages involving /dev/dsp or /dev/dsp2, you're
+probably not able to run audio in and out at the same time. If on the other
+hand there's no complaint, and if the audio test patch does what you want, you
+might wish to experiment with the "-audiobuffer" flag to see what values of
+audio latency your audio system can handle.
+
+<H4> Audio hardware in Linux </H4>
+
+<P>
+Be forewarned: installing and testing audio and MIDI drivers in Linux can take
+days or weeks. There apears to be no single place where you can get detailed
+information on Linux audio. In addition to the information here, you should
+see what's posted on Guenter's page,
+
+ <a href="http://gige.epy.co.at/">
+ http://gige.epy.co.at/</A> .
+
+
+<P>
+Depending on your hardware and software, you might or might not be able to
+run "full duplex," i.e., use audio input and output at the same time. For
+many applications it's important to be able to do this, but if by any chance
+you don't need simultaneous input and output you will have much less trouble
+than if you do.
+
+<P>
+There are two widely-used driver sets, called "OSS" and "ALSA". OSS is
+included in the standard Linux kernels since version 2.2. However, for some
+audio cards you can find newer versions than are included in the kernel
+releases. You can get ALSA from
+
+ <a href="http://www.alsa-project.org/">
+ http://www.alsa-project.org/</A> .
+
+<P>
+(There is also a commercial version of the OSS drivers which costs $30 (slightly
+more for certain audio cards.) Hit
+
+ <a href="http://www.opensound.com/">
+ http://www.opensound.com/</A> .
+
+These might be easier to use than the free OSS drivers, but I've never tried
+them.)
+
+<P> ALSA is able to emulate OSS, so that you can usually run Pd using the
+default "OSS" settings even if it's actually ALSA that's running.
+
+<H5> Installing and configuring FREE OSS </H5>
+
+<P>
+OSS is really a collection of loadable device drivers. The commands
+for loading and unloading the drivers are "insmod" and "rmmod".
+You can see if the audio drivers are
+running using "lsmod" (as root.) If you see something like:
+<PRE>
+
+Module Pages Used by
+eepro100 3 1 (autoclean)
+opl3 3 0
+opl3sa2 1 0
+ad1848 4 [opl3sa2] 0
+mpu401 5 [opl3sa2] 0
+sound 15 [opl3 opl3sa2 ad1848 mpu401] 0
+soundcore 1 [sound] 6
+soundlow 1 [sound] 0
+aic7xxx 23 2
+
+</PRE>
+then OSS is running, and if all you see is:
+<PRE>
+
+eepro100 3 1 (autoclean)
+aic7xxx 23 2
+
+</PRE>
+then it isn't. You can turn OSS off by running "rmmod" repeatedly, starting
+with "opl3" (or whatever) so as not to remove any module before you remove
+all the modules that depend on it. In the above listing, "opl3*" is device
+dependent and you might see different names.
+
+<P>
+The file, "/etc/modules.conf" apparently controls which sound drivers are
+started at boot time. The sndconfig program updates this file but you can
+also change things manually, for instance to switch between two different sound
+cards. In Redhat 6.x and earlier, the file is named "conf.modules."
+
+<P> Here is a modules.conf file for OSS:
+
+<PRE>
+
+alias eth0 e100
+alias parport_lowlevel parport_pc
+alias char-major-81 bttv
+alias usb-controller usb-uhci
+alias sound-slot-0 i810_audio
+alias sound-slot-1 es1371
+
+</PRE>
+
+Here the two sound cards are the (motherboard resident) i810 driver and an
+ensoniq es1371.
+
+<P> In RedHat at least, the "sndconfig" program tries to automatically search
+for your soundcard. Unfortunlately it only finds the "first" one which is
+often not the one you want to use!
+
+<P> Under OSS, programs can stream sound using either
+"block" or "stream" mode. Stream mode is the more modern and better of the
+two, but the majority of drivers, even for new sound cards, only
+support "block." Pd makes "block" the default.
+
+<H5> ALSA (Advanced Linux Sound Architecture) </H5>
+
+<P> ALSA is newer, hence less stable and harder to use, than OSS.
+Alsa comes in a "finished" version (0.5.x) and a
+different, redesigned, "beta" version, 0.9. Installing ALSA can be tricky
+and/or confusing.
+
+<P> As of version 0.33 Pd works with either 0.5.x or 0.9.x versions.
+The RPM version of Pd is compiled for 0.9.x. If you're starting from the
+".tar.gz" version, you have to "./configure --enable-alsa" to get it; see
+the "INSTALL.txt" file in the installation.
+
+<P> By default, Pd uses OSS. If you are running ALSA, Pd will use ALSA's OSS
+emulation. To make Pd use ALSA "natively", i.e., the way ALSA is designed
+to be used, include the "-alsa" flag in the command line.
+
+<P> In ALSA, you can specify which sound card to use using the "-alsadev"
+flag. So, for instance, "-alsadev 3" means your third card, counting from
+one. You can also specify it the ALSA way: "-alsadev hw:3,0".
+
+<H5> Which sound card? </H5>
+
+<P>
+Here's a rundown on my experiences with sound cards so far. See
+also the Pd mailing list archives.
+
+<H6> opl3sa </H6>
+
+This is the old ISA "Yamaha" audio system. It comes on many Dell machines and
+seems to offer reasonable consumer quality audio, at least under NT. I
+believe the current version of OSS can get full duplex operation out of an
+OPL3sa audio system.
+This is an ISA ("plug and play" device and you have to deal with I/O
+addresses and all that.
+
+<H6> cs4232 </H6>
+
+The 1999 vintage dual-processor Dell machines have "cs4232" audio, which I
+couldn't get working.
+
+<H6> es1370 (old Creative PCI128s; Ensoniq AudioPCI) </H6>
+
+<P>
+The audio inputs and outputs on my PCI128 aren't clearly labelled and various
+documents give them inconsistent names. On my card there are 4 stereo
+mini jacks and a joystick port, in this order:
+
+<PRE>
+joystick black green red blue
+ bidirectional line-out mic-in line-in
+</PRE>
+
+<P> It used to be possilbe to get quadraphonic audio in and out
+of this card, but I haven't tried this in years.
+
+<H6> Creative SBLive </H6>
+
+This seems to work fine either with ALSA or OSS as of Pd version 0.35; earlier
+versions of Pd didn't see MIDI input under OSS (the driver's fault, not Pd's,
+but I figured out a workaround.)
+
+<H6> Sonorus Stud I/O </H6>
+
+This $1000 card is supposed to do multichannel digital I/O
+in Linux, via a beta version of a commercial OSS driver ($40).
+I don't know if anyone has used it with Pd.
+
+<H6> RME 9652 (Hammerfall) </H6>
+
+<P> This is the best sound card out there; it costs around $500 and has 3 ADAT
+I/O ports and one SPDIF. There is a "baby hammerfall" also, which I think is
+the "9632." DO NOT CONFUSE THE 9652/9632 WITH OTHER RME BOARDS WHICH MIGHT
+NOT WORK WITH PD.
+
+<P> Guenter Geiger has an OSS driver for Hammerfall for 2.4 kernels (such as
+RedHat 7.1 and up). You have to download and compile it:
+
+ <A HREF=http://gige.xdv.org/pages/rme>
+ http://gige.xdv.org/pages/rme </A>.
+
+<P> You must then run Pd using the "-32bit' flag, because this uses a
+non-standard extension of OSS to 32 bit samples.
+
+<P> There's an older driver by Winfried Ritsch, invoked using the "-rme"
+flag to Pd. This only works on 2.2 kernels, and you probably shouldn't
+try it. It will probably be discontinued after Pd version 0.35.
+
+<P> Hammerfalls now have an ALSA driver; from what I hear
+it won't work yet with Pd. I was unable to install the ALSA driver on the
+two machines I tried ("no such device").
+
+<H6> MIDIMAN </H6>
+
+Midiman sells devices with between 4 and 12 analog channels in and out, for
+which there are ALSA drivers. It seems to work fine with the old ALSA driver
+(0.5). I'm running mine in Alsa 0.9 beta 10. The driver name is "ice1712".
+
+<P> Alsa provides an "envy24control" program (in "utils". You should run
+this and check that your ice1712's sync source is internal if you have no
+SPDIF input, or "SPDIF" if you do. I think the default is now "internal"
+but don't take it for granted...
+
+<H6> i810/i815 </H6>
+
+In RedHat 7.0, motherboards with native i810 audio systems don't work in
+full duplex (they crash linux). Either run Pd -noadc or else (better) install
+ALSA.
+
+<H6> Yamaha YMF724 </H6>
+
+The OSS driver for this card appears not to support MIDI. I haven't
+tried with ALSA.
+
+<H6> ES1371 </H6>
+
+In OSS, audio and MIDI seem both to work fine with this chipset.
+
+<H4> <A name=s1.4> 3.4. Macintosh OSX </A> </H4>
+
+Pd version 0.35 supports Macintosh OSX, although there are still various
+problems. You can either download Pd with Mac OSX binaries, or just download
+the sources and compile it yourself.
+
+<H5> To install the binary OSX release: </H5>
+
+<P> First download and install TK for OSX
+(http://sourceforge.net/projects/tcl/). Get a recent one compiled for
+OSX, by chasing through "Mac OS X Tk Snapshots." I got
+version 8.4a4-2, in a file named "MacOSXTk8.4a4-2.tar.gz ". Unpacking this
+yields three directories: ./Applications/Wish Shell.app,
+./Library/Frameworks/Tcl.framework,
+and ./Library/Frameworks/Tk.framework. These must be moved, either to:
+<pre>
+ ~/Applications/Wish Shell.app
+ ~/Library/Frameworks/Tcl.framework
+ ~/Library/Frameworks/Tk.framework
+</pre>
+or, if you wish to make them available to other users (or make it possible to
+recompile Pd), in /Applications and /Library instead.
+
+<P> Then download and unpack the Pd binary distribution for OS X. This will
+create a directory with a name like ~/Desktop/pd-0.35-test22. You can move
+this elsewhere if you wish (to ~/pd, for example). To a shell window, type
+either "~/Desktop/pd-0.35-test22/bin/pd" or, if you moved it as suggested,
+"~/pd/bin/pd" . If you wish you can put the line,
+
+<pre>
+ alias pd ~/pd/bin/pd
+</pre>
+
+in the file, ~/.tcshrc, so that you can later just type "pd" to a shell. (The
+shell only reads the ~/.tcshrc file onstartup, so this won't take effect in
+any existing shells unless you specially type
+<pre>
+ source ~/.tcshrc
+</pre>
+to them.)
+
+<P> In some cases you have to explicitly give "-soundindev" and "-soundoutdev"
+flags for Pd to open audio correctly; "pd -listdev" should show you the
+correct device numbers.
+
+<P> To get MIDI working, you have to do the Mac OSX magic to get a USB
+MIDI interface installed. I've seen this done with Midisport devices and
+I think you just download the OSX driver and follow directions.
+
+<P> On the machine I tried, it was necessary to type,
+
+<pre>
+ pd -midiindev 1 -midioutdev 2
+</pre>
+
+to get MIDI working. At the moment, using a midiman Midisport 2x2, I'm getting
+several lines of debugging printout for each incoming MIDI message; it seems to
+be the driver printing it out. I don't know how to turn this off.
+
+<P> To get Pd running at high priority, so that you'll get fewer skips in the
+audio input and output, you must "renice" it. The easiest way to do this is
+to make it SETUID and use the "-rt" flag. To do this, become root (you might
+have to add a root account to do this) and type:
+
+<pre>
+ chown root ~ferguson/pd/bin/pd
+ chmod 4755 ~ferguson/pd/bin/pd
+</pre>
+(assuming your username is "ferguson").
+
+<H5> To compile your own Pd in OSX: </H5>
+
+Whether you've downloaded the source or the OSX binary distribution you can
+always Pd for yourself, whether to make your own improvements, or possibly
+so that you can get the newest version before it shows up compiled
+for Mac OS X.
+
+<P> To be able to compile Pd, you must have installed Tcl/Tk
+ specifically in
+/Applications/Wish Shell.app
+and /Library/Frameworks/Tk.framework and /Library/Frameworks/Tcl.framework.
+
+You must also get the "h" files from XFree86 and put them in
+/usr/X11R6/include. You can download just the H files from:
+<pre>
+ http://www.crca.ucsd.edu/~msp/x.tgz
+</pre>
+(the individual files seem to have adequate copyright notices so that
+I can just redistribute them.)
+
+Then, just as for linux, just unload pd-whatever.tar.gz into a directory
+such as ~/pd-0.35-test17 , cd to pd-0.35-test17/src, type "./configure"
+and "make".
+
+then type ~/pd-0.35-test17/bin/pd to a shell and enjoy!
+
+
+<H4> <A name=s3> 3.5. graphics rendering using GEM </A> </H4>
+
+<P>
+GEM, originally by Mark Danks but now supported by Iohannes Zmoelnig, is essentially an extension of Pd that allows you to do OpenGL programming
+using a suite of "GEM objects" roughly parallel to the tilde objects built
+into Pd for audio. Find out more from
+<a href="http://iem.kug.ac.at/~zmoelnig/index.html"> Johannes's page</a>.
+
+
+<H4> <A name=s4> 3.6. The Pd command line </A> </H4>
+
+Pd is a "command line" program. The best way to run it is from your
+"terminal emulator," "shell," or "MSDOS prompt." The command line is:
+
+<PRE>
+
+ pd [options] [patches to open]
+
+</PRE>
+
+although you may have to specify a path so your command interpreter can find
+Pd (OS dependent.) Possible options include:
+
+<PRE>
+
+audio configuration flags:
+-r <n> -- specify sample rate
+-inchannels ... -- number of audio in channels (by device, like "2" or "16,8")
+-outchannels ... -- number of audio out channels (by device)
+-channels ... -- specify both input and output channels
+-audiobuf <n> -- specify size of audio buffer in msec
+-blocksize <n> -- specify audio I/O block size in sample frames
+-sleepgrain <n> -- specify number of milliseconds to sleep when idle
+-nodac -- suppress audio output
+-noadc -- suppress audio input
+-noaudio -- suppress audio input and output (-nosound is synonym)
+-listdev -- list audio and MIDI devices
+-audioindev ... -- sound in device list; e.g., "2,1" for second and first
+-audiooutdev ... -- sound out device list, same as above
+-audiodev ... -- specify both -audioindev and -audiooutdev together
+
+(linux specific audio:)
+-frags <n> -- specify number of audio fragments (defeats audiobuf)
+-fragsize <n> -- specify log of fragment size ('blocksize' is better...)
+-stream -- use stream mode audio (e.g., for es1370 audio cards)
+-alsa -- use ALSA audio drivers
+-alsadev <n> -- ALSA device # (counting from 1) or name: default hw:0,0
+
+MIDI configuration flags:
+-midiindev ... -- midi in device list; e.g., "1,3" for first and third
+-midioutdev ... -- midi out device list, same format
+-mididev ... -- specify -midioutdev and -midiindev together
+-nomidiin -- suppress MIDI input
+-nomidiout -- suppress MIDI output
+-nomidi -- suppress MIDI input and output
+
+general flags:
+-path <path> -- add to file search path
+-open <file> -- open file(s) on startup
+-lib <file> -- load object library(s)
+-font <n> -- specify default font size in points
+-verbose -- extra printout on startup and when searching for files
+-d <n> -- specify debug level
+-noloadbang -- suppress all loadbangs
+-nogui -- suppress starting the GUI
+-guicmd "cmd..." -- substitute another GUI program (e.g., rsh)
+-send "msg..." -- send a message at startup (after patches are loaded)
+-rt or -realtime -- use real-time priority (needs root privilege)
+
+-frags <n> -- specify number of audio fragments (defeats audiobuf)
+-fragsize <n> -- specify log of fragment size ('blocksize' is better...)
+-blocksize <n> -- specify audio I/O block size in sample frames
+-stream -- use stream mode audio (e.g., for es1370 audio cards)
+-alsa -- use ALSA audio drivers
+-alsadev <n> -- ALSA device # (counting from 1) or name: default hw:0,0
+
+(MS Windows specific audio:)
+-resync -- resynchronize audio (default if more than 2 channels)
+-noresync -- never resynchronize audio I/O (default for stereo)
+-asio -- use ASIO audio driver (and not the 'MMIO' default)
+
+MIDI configuration flags:
+-midiindev ... -- midi in device list; e.g., "1,3" for first and third
+-midioutdev ... -- midi out device list, same format
+-nomidiin -- suppress MIDI input
+-nomidiout -- suppress MIDI output
+-nomidi -- suppress MIDI input and output
+
+general flags:
+-path <path> -- add to file search path
+-open <file> -- open file(s) on startup
+-lib <file> -- load object library(s)
+-font <n> -- specify default font size in points
+-verbose -- extra printout on startup and when searching for files
+-d <n> -- specify debug level
+-noloadbang -- suppress all loadbangs
+-nogui -- suppress starting the GUI
+-guicmd "cmd..." -- substitute another GUI program (e.g., rsh)
+-send "msg..." -- send a message at startup (after patches are loaded)
+-rt or -realtime -- use real-time priority (needs root privilege)
+
+</PRE>
+
+Here are some details on some of the audio and MIDI options (but see also the
+next section on file management.)
+
+<H5> sample rate </H5>
+
+The sample rate controls Pd's logical sample rate which need not be that of
+the audio input and output devices. If Pd's sample rate is wrong, time will
+flow at the wrong rate and synthetic sounds will be transposed. If the output
+and input devices are running at different rates, Pd will constantly drop frames
+to re-sync them, which will sound bad. You can disable input or output if this
+is a problem.
+
+<H5> audio buffer size </H5>
+
+You can specify an audio buffer size in milliseconds, typically between 10 and
+300, depending on how responsive your OS and drivers are. If this is set too
+low there will be audio I/O errors ("data late"). The higher the value is,
+on the other hand, the more throughput delay you will hear from the audio
+and/or control inputs (MIDI, GUI) and the audio coming out.
+
+<P> In Linux and Windows, you can also specify the audio block size in sample
+frames (but in Windows, this is only effective when using ASIO).
+
+<H5> MIDI devices </H5>
+
+<P> You can specify multiple MIDI input and output devices. For example,
+"pd -midiindev 3 -midioutdev 4,2" asks for the third MIDI input device and the
+fourth and second MIDI output device. The "channel message" midi objects in Pd
+such as notein or pgmout will take channels 1-16 to mean the first open MIDI
+port, 17-32 the second one, and so on. The midiin, sysexin, midiout objects
+give you a separate inlet to specify which of the open MIDI port numbers
+you want.
+
+<P> In Linux, if you
+ask for "pd -midioutdev 1" for instance, you get /dev/midi0 or /dev/midi00
+(or even /dev/midi). "-midioutdev 45" would be /dev/midi44. In NT, device
+number 0 is the "MIDI mapper", which is the default MIDI device you selected
+from the control panel; counting from one, the device numbers are card
+numbers as listed by "pd -listdev."
+
+<H4> <A name=s5> 3.7. dealing with files </A> </H4>
+
+Pd has a search path feature; you specify the path on the command line
+using the "-path" option. Paths may contain any number of files. If you
+specify several files in a single "-path" option they're separated by colons
+in unix or semicolons in NT. When Pd searches for an abstraction or an
+"extern" it uses the path to try to find the necessary file. The "read"
+messages to qlists and arrays (aka tables) work the same way. NO SPACES MAY
+APPEAR ANYWHERE IN THE SEARCH PATH, e.g., "c:\my nonsense\goobers" won't
+work.
+
+<P> Filenames in Pd are always separated by (unix-style) forward slashes, even
+if you're on Windows (which uses backslashes). This is so that patches can be
+ported more easily between operating systems. On the other hand, if you
+specify a filename on the command line (as in "pd -path c:\pdlib") the file
+separator should agree with the operating system. <BR>
+
+<P> If a filename in a patch has any "/" characters in it, the "path" is not
+used; thus, "../sounds/sample1.wav" causes Pd only to look relative to the
+directory containing the patch. (You may also invoke externs that way.)
+
+<P> As of version 0.35, there may be spaces in the path to Pd itself; also,
+the "openpanel" and "savepanel" objects can handle spaces. But still not
+the search path.
+
+</BODY>
+</HTML>
+
+
diff --git a/pd/doc/1.manual/x4.htm b/pd/doc/1.manual/x4.htm
new file mode 100644
index 00000000..5fe5d25d
--- /dev/null
+++ b/pd/doc/1.manual/x4.htm
@@ -0,0 +1,59 @@
+<HTML>
+<HEAD>
+<TITLE>Pd Documentation</TITLE>
+</HEAD>
+<BODY bgcolor="#ffffff">
+<SMALL>
+<div style="width:6.5in; margin-left:.5in">
+
+<CENTER> <B>
+Pd Documentation chapter 4: writing Pd objects in C
+</B> </CENTER>
+<BR>
+<H5> <A href=index.htm#s4> back to table of contents </A></H5>
+ <BR><BR>
+<P>
+
+You can write your own objects that you and others can use in their Pd
+applications. You can write them in C or (if you're smart and brave) in C++ or
+FORTRAN.
+
+<P> HOW EXTERNS ARE LOADED
+
+<P> Whenever you type the name of an object
+(into an "object" text box) that Pd doesn't yet know about, Pd looks for a
+relocatable object file, named, for instance, "profile.pd_irix5". Pd looks
+first in the directory containing the patch, then in directories in its
+"path." Pd will then add whatever object is defined there to its "class list,"
+which is the set of all Pd classes you can use. If all this works, Pd then
+attempts again to create the object you asked for, this time perhaps
+sucessfully. There is no difference between an object defined this way and an
+object built into Pd.
+
+<P> Once you load a new object into Pd, it's there for the duration of your Pd
+session. If you load another Pd document which supplies a different version of
+some Pd object, the object won't be updated. IF you're working on a new object
+and decide to change it, you have to exit and re-enter Pd to get the change to
+take.
+
+<P> In the "externs" subdirectory of the documentation you
+can find simple examples of "externs" with their source code and test patches;
+there are many other on the web (see <a href="x1.htm#s2">section 1.2 </A>).
+
+<P> Iohannes Zmoelnig has written an excellent guide to writing externs at
+<A href="http://iem.kug.ac.at/pd/externals-HOWTO/">
+ http://iem.kug.ac.at/pd/externals-HOWTO/</A> .
+
+<P> A paper by Theo Stojanov on the subject is at:
+<A href="http://www.music.mcgill.ca/~theo/html/audio/pd_externs.pdf">
+http://www.music.mcgill.ca/~theo/html/audio/pd_externs.pdf </A> .
+
+<P> NT HINT: In NT, Pd is compiled using Visual C 6.0. If you have VC 5.x
+your externs won't compile against Pd; you'll get an error about "disk full
+or bad DLL." Simply recompile Pd under 5.x and the problem goes away. Externs
+compiled under 5.x and 6.x are binary compatible; it's just the compilation
+that's sensitive.
+
+
+</BODY>
+</HTML>
diff --git a/pd/doc/1.manual/x5.htm b/pd/doc/1.manual/x5.htm
new file mode 100644
index 00000000..2203ebc7
--- /dev/null
+++ b/pd/doc/1.manual/x5.htm
@@ -0,0 +1,1231 @@
+<HTML>
+<HEAD>
+<TITLE>Pd Documentation</TITLE>
+</HEAD>
+<BODY bgcolor="#ffffff">
+<SMALL>
+<div style="width:6.5in; margin-left:.5in">
+
+<CENTER> <B>
+Pd Documentation chapter 5. current status
+</B> </CENTER>
+<P>
+This section tracks changes in Pd's current implementation.
+<BR>
+<H5> <A href=index.htm#s5> back to table of contents </A></H5>
+
+<H4> <A name=s2> 5.1. release notes </A> </H4>
+
+<P> ------------------ 0.35 -------------------------------
+
+<P> An experimental new feature called graph-on-parent allows subpatches and abstractions to show
+GUI features; so, for instance, you can make an oscillator with a number box to
+control the frewuency. This is described in section 2.7.2 of the HTML
+documentation and an example is shown in 7.stuff/synth1/.
+
+<P> Spaces are allowed in pathnames to Pd and to patches; however, the "path"
+variable still can't have spaces. (You can address path directories using
+relative pathnames as in "../sound" (or ..\sound on Windows), even if there
+are spaces further "up" the path to the patch. See 3.7, "dealing with files."
+
+<P> The soundfile reading routine (used in readsf~ and soundfiler) is much
+better at opening wav files with different header sizes and odd chunks.
+You can now read floating-point "wav" files -- although you can't write them
+yet.
+
+<P> Templates and data structures are extensively reworked. A "struct"
+object replaces "template", so that you specify the name of the structure as
+the first argument to "struct" (previously it was derived from the
+window name.) You can now have multiple "structs" of the same name; the
+oldest one is the "real" one, but if you delete that, the structures are
+all conformed to the next-oldest one, and so on. You can alter the contents of
+a "struct" and all the associated data will be modified to fit the new
+structure definition. Data are persistent, i.e., saved with the containing
+patch. You can copy and paste data between patches. If you save data to a file
+explicityly, you can read it into another patch and the data are conformed
+automatically to the new data structures.
+
+<P> A new version of Thomas Musil's GUI objects was merged in.
+
+<P> The testtone patch works for up to 6 channels of audio input and output.
+
+<P> Lots of improvements got made to audio I/O in general. In NT you may
+specify "-asio" to use ASIO drivers; see HTML documentation section 3.2.
+You may specify lists of audio input and output devices. In Linux, Pd
+will now attempt to open each /dev/dsp* only once, even if it's requested
+for reading and writing.
+
+<P> The "extra" directory is now searched after the directories in the
+search path, not before (so now you can override objects like "fiddle~").
+
+<P> A bug in paf~ is fixed.
+
+<P> In Linux, the ".pdrc" is now read before the command line arguments, so
+that command line arguments override the .pdrc (it was backwards before.)
+
+<P> In Linux, "help" now can invoke either mozilla or netscape to start
+up the HTML documentation. This doesn't work in Windows or Mac land yet.
+
+<P> In Linux, the "-32bit" flag was added, which you must now use if
+running Guenter's OSS RME Hammerfall driver. (This was necessary because
+OSS went and used the same "bit" for a different purpose, so taht Pd tried
+to open some other cards in 32bit mode inappropriately.)
+
+<P> In Linux, MIDI is now opened "-NODELAY" ... this makes the OSS Creative
+driver take MIDI input correctly which it didn't before.
+
+<P> In MS windows, you can now use "readsf~/writesf~" for spooling sounds to
+and from disk.
+
+<P> MS Windows bug fixes: -nosound was ignored, and now works. Also, clicking
+to open abstractions, when they were already open anyway, used to lose the
+keyboard; this should be fixed now. Finally, "netreceive" didn't work when
+running "-nogui". This is fixed, and moreover, you should definitely include
+a netreceive object in any -nogui patch in MSW, otherwise it eats up all
+available CPU time gratuitously.
+
+<P> The outlet is removed from the "table" object.
+
+<P> In MS Windows, Pd now has "-resync" and "-noresync" flags so that you
+can specify how to deal with audio input and output blocksize nonsense in
+MMIO. If "resync" is on, whenever the audio input and output seem out
+of whack the audio driver resynchronizes all input and output devices;
+otherwise the situation is simply ignored. "Noresync" is probably best for
+consumer stereo cards (and is the default if you're running only 2 channels in
+and out). If you're runnimg more than 2 channels in either direction, the
+default is "resync".
+
+<P> In soundfiler's read method, if you specify "-maxsize", that implies
+"-resize" (as it ought to.)
+
+<P> You can use $1-stlye names for arays and tables.
+
+<P> Pd will now refuse to make duplicate connections between objects.
+
+<P> Pd is (somewhat shakily) running on Macintosh OS/X. See section 3.4 of
+the HTML doc. For Macs with one-button mice, you can double-click in edit
+mode to simulate a right click. Unfortunately, the "alt" key doesn't work
+yet.
+
+<P> In Linux, ALSA audio is now fixed to clip, not wrap around, on output
+overflows.
+
+<P> Various problems were fixed with objects changing size. Number boxes never
+wrap to two lines (as they used to), and lines are reconnected appropriately
+when objects are resized.
+
+<P> A function call is added to retrieve a unique event-dependent number,
+so that objects like "buddy" can be written.
+
+<P> All the "sound" command-line flags now have "audio" equivalents.
+
+<P> The "-listdev" flag now works on Mac and MSW/ASIO.
+
+<P> Help file updates for env~, route, and pointer
+
+<P> ------------------ 0.34.3 -------------------------------
+
+<P> fixed a bug in "udp" netreceive that crashed pd
+
+<P> fixed a bug in tabosc4~ that caused gritty sound
+
+<P> changed "specfile" for RPM releases (thanks Fernando)
+
+<P> adopted Krzysztof's glob_setfilename bug fix
+
+<P> bug fixes from "the joy of global variables" thread in Pd list
+
+<P> made a help window for "table".
+
+<P> ------------------ 0.34.2 -------------------------------
+
+<P> fixed ".pdrc" bug
+
+<P> added an experimental "pd restart-audio" feature for (new) Alsa
+
+<P> ------------------ 0.34.1 -------------------------------
+
+<P> Bug fixes:
+
+<P> 1. Closing a window with objects selected crashed Pd.
+
+<P> 2. "find" when it opened a window to show the found object crashed Pd.
+
+<P> 3. (Linux only) Oversized .pdrc files crashed pd...
+
+<P> Also, I updated Thomas Musil's IEM GUI objects and their help files.
+
+
+<P> ------------------ 0.34 -------------------------------
+
+<P> NEW FEATURES:
+
+<P> I incorporated Thomas Musil's GUI objects (slider, button, etc.) into
+the Pd release so Thomas won't have to publish patches to Pd anymore. I
+didn't take the graphical inlets and outlets for reasons explained elsewhere,
+but Thomas might decide to continue supplying them on a patch basis.
+
+<P> Many new examples were added to the "2.control" amd expecially
+"3.audio" example patches. A list of differences batween Max/MSP and Pd
+now appears at the end of this section.
+
+<P> Finally, I fixed Pd to notice window iconification and suspend graphical
+updates for iconified windows.
+
+<P> Numbering of versions of Pd will now be as in "0.34.2" instead of
+"0.34PATCH2" which was confusing.
+
+<P> BUGS FIXED:
+
+<P> I incorporated Krzysztof Czaja's menuclose bug fix in g_canvas.c.
+
+<P> (lunix) the configure script is more rational.
+
+<P> the qlist and pack objects were fixed to handle reentrancy correctly.
+
+<P> Pd now complains about running out of memory (before it dies.) I intend
+to provide advance warning and automatically back out of loading patches that
+woudl run out of memory, but that's not in place yet.
+
+<P> Typing into a message box sometimes left you with lines from the output
+pointing to the wrong location. Fixed.
+
+<P> Reading of "wav" and nextstep soundfiles now handles the headers better.
+
+<P> ------------------ 0.33 -------------------------------
+
+<P> AUDIO AND MIDI:
+
+<P> MIDI time jitter is reduced. Theoretically, it could now be
+as low as the audio blocksize (and so if you care about MIDI timing, keep your
+audio blocksize low.) If you run Pd with audio in stream mode or without
+audio at all, and perhaps in some cases in block more too (?),
+the controlling parameter for MIDI jitter is "-sleepgrain", which specifies
+the interval of time Pd sleeps when it believes it's idle.
+
+<P> You can now specify multiple MIDI input and output devices. For example,
+"pd -midiindev 3 -midioutdev 4,2" asks for the third MIDI input device and the
+fourth and second MIDI output device. The "channel message" midi objects in Pd
+such as notein or pgmout will take channels 1-16 to mean the first open MIDI
+port, 17-32 the second one, and so on. The midiin, sysexin, midiout objects
+give you a separate inlet to specify which of the open MIDI port numbers
+you want.
+
+<P> (linux only) By default, Pd now reads and write audio in "block mode."
+Previously you have to specify "-frags" and/or "-fragsize" to get this.
+As of this version you have to specify "-streammode" to get the opposite,
+streaming mode. This mode seems only to work with a small number of sound
+cards, notably Ensoniq ens1370 and ens1371.
+
+<P> (linux only) Also, "-fragsize" is replaced with a more convenient
+"-blocksize" which you specify in sample frames. It defaults to 64 which is
+Pd's audio computation block size but may be larger or smaller. Typically you
+would specify "-audiobuf" and "-blocksize" and Pd will compute "-frags" for
+you; but you can also specify "-frags" explicitly.
+
+<P> (linux only) OSS and ALSA audio support are improved. You can now talk to
+RME9652 using Guenter's OSS driver; this is different from the "-RME" support
+which uses Winfried's older driver. Other multichannel OSS drivers might now
+work as well. Pd also seems to work with ALSA 0.9 Beta 4; I've tested this
+with Midiman Delta 66 and Soundblaster live. I plan to update the linux audio
+setup documentation accordingly.
+
+<P> NEW FEATURES:
+
+<P> I've put in Shahrokh's new expr, expr~, and fexpr~ objects. The latter
+allows you to make expressions referring to prior input and output samples in
+case you're interested in writing your own recursive filters, oscillators,
+or chaotic sound generators...
+
+<P> In support of expr, you can now use commas in "object" boxes; they just
+become symbols.
+
+<P> sqrt~ is fixed so that it apparently has 24-bit accurate mantissas.
+It turned out to be easier to just make it accurate than to confront the
+question of how a reduced-accuracy version should be named.
+
+<P> The bizarre framp~ object which does phase vocoder analysis got a help
+window. The phase vocoder example doesn't use framp~ and I had forgotten
+what it did until Guenter dug it back up.
+
+<P> (linux only) I finally got around to incorporating Guenter's autoconf
+stuff, and learned about rpms. Major new linux releases will probably be
+in .tar.gz and .rpm formats; "test" releases will probably just be in .tar.gz.
+I also fixed it so that the installation prefix is overridden if you invoke
+pd by its full pathname, so that you can still use compilations with
+installation prefixes before you actually install them.
+
+<P> (NT only) I added support for directX using the portaudio package
+by Ross Bencina and Phil Burk. I couldn't discover any way this would ever
+outperform the old "multimedia" API Pd uses. So the release contains the sources,
+but you have to recompile Pd to use directX. Use "makefile.nt.portaudio". Only
+1 or 2 channels of audio are supported. THe interesting thing is that the same
+code will run on Macintosh. There are a couple of other obstacles to a
+MacOS port of Pd though; it's hard to predict when this will be feasible.
+
+<P> BUG FIXES:
+
+<P> "drawnumber" was broken in 0.32 -- fixed.
+
+<P> new arrays in 0.32p6 got ill-fitting graphs -- fixed.
+
+<P> ------------------ 0.32 PATCH 6 -------------------
+
+<P> Got array and graph dialogs to behave better when there are more
+than one.
+
+<P> put in mtof~, etc.
+
+<P> made Pd search the "extra" directory without having to specify it in "path."
+
+<P> bug fix in exporting patches to Max
+
+<P> ------------------ 0.32 PATCH 5 -------------------
+
+<P> Reversed the order of these release notes so that the newest appear first.
+
+<P> Arrays can save their content with containing patch; the properties
+dialog selects this. The dialog shows up when you create a new array from
+the menu, and allows you to set the name and size. Only floating point arrays
+can be created and edited this way.
+
+<P> Bug fix: the figures in the NT web doc were garbage.
+
+<P> Bug fix: large tables (> 800 pixels and points) no longer crash the GUI.
+A related problem remains; large arrays are truncated to either 1000 points
+or 1000 pixels.
+
+<P> Bug fix: doing "save as" on an instantiated abstraction no longer sets
+the window title.
+
+<P> in linux, a couple of status messages on opening /dev/dsp only appear now
+if Pd is run "-verbose".
+
+
+<BR> <BR>
+<P> ------------------ 0.32 PATCH 2, 3, 4 -------------------
+
+<P> Hassled more with font size differnces between NT and Linux, and updated
+many help files. Minor bug fixes here and there.
+
+<P> the table object now takes a second argument to set size in points.
+
+<P> Improved underflow protection in some DSP objects.
+
+<P> pointer now has a "vnext" traversal method which goes forward to the
+next SELECTED object.
+
+<P> improvements to throw~ (it now sums) and receive~ fixed to be settable.
+
+<P> bug fix in which RME driver always thought sample rate was 44100.
+
+<BR> <BR>
+<P> ------------------ 0.32 PATCH 1 -------------------
+
+<P> bug fixes (bugs flagged by mik): vcf~ help window crashed; writesf~
+only wrote 1 channel soundfiles; "table" object didn't open when clicked
+on;
+
+<P> new object: tabosc4~ -- finally, a real wavetable oscillator for Pd.
+
+<P> much work on "data" editing; go to 7.stuff/data-structures, open patches
+5 and 7, and try clicking on things. Alt clicks delete or add points; regular
+clicks drag values around. The cursor changes to show you what will happen
+if you click.
+
+<BR> <BR>
+------------------- 0.32 -----------------
+
+<P> <strong> New objects: </strong>
+
+<P> midiin, sysexin, midiout. (I don't think MIDI sysex is working
+in Windows yet though.)
+
+<P> threshold~ as in Jmax, triggers from audio level.
+
+<P> value as in Max and Jmax.
+
+<P> writesf as in Jmax.
+
+<P> <strong> New startup flags: </strong>
+
+<P> -sleepgrain: if you aren't using audio I/O, this can reduce time jitter in
+MIDI I/O. Otherwise, MIDI I/O jitter is limited by the audio buffer size.
+
+<P> -noloadbang: cancels loadbangs.
+
+<P> -nogui: supress starting the GUI. You can then still talk to Pd using,
+perhaps among other possibilities, the new network connection programs now
+included in the release.
+
+<P> -guicmd: lets you specify the command string Pd calls to start the GUI,
+in case you've written your own GUI to replace the TK one Pd comes with.
+
+<P> -send: after loading all the patches specified in the command line,
+you can specify "startup" messages to send. For example, if you want to use
+Pd just to play 50-channel soundfiles from a shell, this is how you can specify
+the soundfile name on the command line.
+
+<P> <strong> bug fixes. </strong>
+
+<P> A readsf~ problem got fixed.
+
+<P> hitting the tab key used to cause Pd windows to relinquish the keyboard.
+
+<P> The $0 feature apperas now to work.
+
+<P> Inlets and outlets of subpatches sometimes got out of left-to-right order.
+
+<P> Scrollbars are less out of whack than they were before.
+
+<P> Pd now knows to de-iconify windows if you "vis" them from the parent.
+
+<P> <strong> in general: </strong>
+
+<P> In Linux the treatment of MIDI input is now much more efficient. Also,
+bugs were fixed in notin and (for SGI) bendin.
+
+<P> You can "select all" from the Edit menu.
+
+<P> standalone programs "pd-send" and "pd-receive" are provided that can send
+mesages to Pd or receive messages from Pd via the netsend~ and netreceive~
+objects. This should allow you to interface a wide variety of other programs
+with Pd either on the same machine or over the network. Also you should be
+able to hack the code into your own programs to make them interoperate with
+Pd and/or each other. The underlying protocol is called FUDI.
+
+<P> "Properties" for scalars, graphs, and number boxes: left click on them.
+In particular, number boxes can have fixed widths and finite ranges; if you
+make them one character wide they act as toggles. Later you'll be able to
+configure them as sliders.
+
+<P> As to scalars, the properties dialog lets you edit the data in the raw.
+Don't try to edit the template though; you can't.
+
+<P> You can now type into a "pd" object to change its name without losing the
+contents.
+
+<P> An experimental "scalar" _text_ object now allows abstractions to draw
+primitive control panels on their parents when you invoke them, as if they were
+Moog or Buchla modules. See the "7.stuff/data" examples.
+
+<P> New help windows for the "data" classes (pointer, append, template, etc.)
+and for send/receive which somehow I had neglected.
+
+<P> When you hit "copy" with nothing selected, the copy buffer used to be
+cleared. This is fixed to do nothing.
+
+<BR> <BR>
+------------------- 0.31 -----------------
+
+<P> ALSA support in Linux has been completely overhauled. It now works with
+Midiman (up to 10 in/12 out!) and es1370. There are problems with SBLive under
+ALSA but it works in OSS emulation with a "-frags" setting. See the "getting
+started" documentation.
+
+<P> In NT, the default is now "noresync" if you're running stereo. You can
+override this with the "-resync" flag. If you're running more than 2 channels
+it's the opposite (as it was before.)
+
+<P> "symbol" boxes now display symbols and let you type them in.
+
+<P> There was a bug when you renamed a patch from outside Pd; the old filename
+still showed in the title bar (and there were other bad side effects.) FIxed.
+
+<P> Protection was added against patches opening themselves as abstractions.
+
+<P> The "route" object's handling of leading symbols was improved. I'm not
+sure whether it's Max compatible or not.
+
+<P> You can draw into arrays with the mouse, at least in the case where there's
+at least one pixel per point. (I'm not sure if the other case even makes
+sense.)
+
+<P> Abstractions display their "$1", etc., arguments in the window title bar.
+
+<P> A "sort" method was added for lists to make them easier to use as
+sequencers.
+
+<P> The "save as" dialog makes a more reasonable choice of start-up directory.
+
+<P> "Trigger i" is now disallowed (it used to crash Pd.)
+
+<P> Getbytes and resizebytes now zero out new memory.
+
+<P> A memory leak reported by Hannes has been partly, hopefully mostly, fixed.
+
+<P> The "signal_free 2" bug reported by Fogar is fixed.
+
+<P> New graphs now reliably avoid using already-taken "graph%d" names.
+
+<P> The old bug which showed up as ".xxxxxxxxx: no such object" is fixed.
+
+<P> The FFT examples have been reworked and the "pique" and "shift" objects
+are moved to "extra".
+
+<BR> <BR>
+------------------- 0.30 -----------------
+<P> in Linux, you can get Pd to promote itself to "real time" priority.
+A "watchdog" process protects you from having Pd lock your machine up. You
+must request real time by running "pd -rt" or "pd -realtime". You must either
+be superuser or make Pd a root-owned SETUID program (chown root .../pd/bin/pd;
+chmod 4755 .../pd/bin/pd). For security reasons, Pd relinquishes root
+privelige immediately after setting its priority, before loading
+any patches or externs.
+
+<P> Protection was added against message loops.
+
+<P> loadbang was fixed so that loadbangs in abstractions go off before loadbangs
+in the owner patch. Within each patch, loadbangs go off forst in subpatches.
+
+<P> new object: tabplay~, a non-imterpolating sample reader.
+
+<P> new objects (in "extra" library): loop~; rev1~.
+
+<P> The "toys" library was renamed "extra" and incorporated in the Pd release.
+
+<P> In Linux, timeouts were added to the driver opening and closing code
+(which used to hang under some conditions.)
+
+<P> the "field" object was replaced by "template"; see "data.structures"
+examples in 7.stuff. Data lists can be read from and written to files now.
+
+<P> You can invoke an external object by pathname, as in "../../extra/loop~".
+
+<P> hip~, etc. should no longer get stuck when they get a NAN on input.
+
+<P> a bug was fixed in expanding symbols such as "$1-foo".
+
+<BR> <BR>
+------------------- 0.29 -----------------
+
+<P> readsf~ - a MAX/FTS style soundfile player, which reads multichannel
+soundfiles in wave, aiff, or next formats. The files must be 16 or 24 bit
+fixed point or 32 bit floating point (only nextstep headers understand the
+latter.) You can also override the header. A "skip" flag lets you read
+starting anywhere in the file. (Sorry: linux only for now; I can't find
+Posix threads packages for the other platforms.)
+
+<P> soundfiler - support for reading and writing soundfiles (wave, aiff,
+nextstep) to and from arrays. Multichannel soundfiles can be read into or
+written from several arrays at once. When reading you can ask that the tables
+be automatically resized; in any event the object obligingly outputs the number
+of samples actually read. When writing you can specify a sub-segment of the
+arrays, and/or request that the soundfile's maximum amplitude be normalized to
+one.
+
+<P> tabplay~ - a non-interpolating sample player
+
+<P> Garry Kling reports having compiled Pd for "yellowdog" linux on Macintosh
+computers. One "fix" has been made to s_linux.c to facilitate this. I don't
+have access to a Mac running linux at the moment so I can't verify whether
+any particular repease of mine actually works there.
+
+<P> Signal objects now automatically convert scalars to vectors, so that you
+can just run a number box into a signal input. One caveat is that the binops
+"+~", "-~", "*~", "/~", "max~", "min~" run slightly faster if you give them
+an argument to tell them that their right inlet will be scalar; so the
+construction "+~ 0" is still meaningful. This will get fixed at some later
+date...
+
+<P> Font sizes work in what I hope will be a more machine-portable way. On
+any machine, the point sizes 8, 10, 12, 14, 16, 24 are DEFINED to be the
+largest fonts Pd can find that don't exceed their size on my linux machine.
+This way I can write patches that everyone else can read, and others will
+at least have fewer portability problems than before. The downside is that
+your old patches may appear with a different type size than you want; use the
+"font" menu item to fix them.
+
+<P> The OSS support no longer asks the audio driver whether full duplex
+is needed; it just tries to open it. Apparently some drivers (such as
+ALSA's OSS emulation) might do full duplex but not implement the call Pd
+used to query for it.
+
+<P> You can give "-nomidi" as a flag (previously you had to type "-nomidiin
+-nomidiout".)
+
+<P> A GUI bug reported by Iain Mott was fixed.
+
+<P> You can now type symbols such as "$3-poodle" and the "$3" portion gets
+expanded properly. Someone was also asking about the FTS-style #0 feature,
+but I couldn't figure out how to reconcile it with Pd's usage of "$" for "#"
+in abstractions. So I'm still searching for a good way to provide local
+symbols.
+
+<P> the GUI now protects itself from "\", "{" and "}" characters by dropping
+them. I wonder how many NT users have crashed Pd trying to type in filenames
+with backslashes...
+
+<P> samphold_set and tabwrite_stop methods added. There turned out to be
+no help window for samphold~ so one was supplied.
+
+<BR> <BR>
+------------------- 0.28 -----------------
+
+<P> Version 0.28 has a primitive in-box text editor... about time!
+
+<P> the "front panel" now gives you information on audio levels and
+sync errors.
+
+<P> Message boxes flash, sort of, when you click them.
+
+<P>
+Support has been added for RME 9652 soundcards; see the Linux soundcard section of
+the documentation. Support files for RME and PCI128 (Ensoniq es1370) cards
+are released separately from Pd.
+
+<P> The delete and backspace keys clear the current selection. There is
+unfortunately no "undo" though; I'm not sure this is a good thing to have
+put in.
+
+<P> The "until" object has a "float" method which limits the number of bangs
+it will output.
+
+<P> The audio setup is better documented for NT and Linux.
+
+<P> The externs in 4.fft and 6.externs got recompiled and tested.
+
+<P> BUG FIX: the "read16" message to tables was broken on NT and is now fixed.
+
+<P> BUG FIX: In Linux, starting Pd up sometimes changed the audio mixer
+setting.
+
+<P> BUG FIX: sending "floats" to inlets expecting lists now works correctly.
+
+<P> BUG FIX: "route" on symbols now deals better with symbols, floats and lists.
+
+<BR> <BR>
+------------------- 0.27 -----------------
+<P>
+The main new feature is the "find" menu stuff. You can search for boxes
+containing specified atoms, including semicolons or commas. Most errors are
+now trackable, allowing you to "find last error". Look in the "Find" menu.
+
+<P>
+New objects written: change, max, max~, min, min~, and swap.
+
+<P>
+I looked in 0.INTRO.txt in 5.reference, and found that the objects
+bag, cputime, realtime, pipe, symbol, poly, and bang were missing.
+
+<P>
+Five or six bug fixes.
+
+<P>
+Some audio problems in 0.25 were addresses. In Linux, audio drivers that
+don't support the GETISPACE/GETOSPACE ioctl calls can be called using the
+(inferior) "-frags/-fragsize" mechanism. If you specify either a "-frags"
+or a "-fragsize" option, the GETIOSPACE calls are cancelled.
+
+<P>
+Under NT, for some audio drivers the 0.26 release gave a constant stream of
+"resync" events. I don't know what causes this but I added a "-noresync"
+option which simply never resyncs at all.
+
+<BR> <BR>
+------------------- 0.26 -----------------
+<P>
+phasor~ and osc~ can be configured to take floating point messages to set
+their frequencies, as an alternative to having an input signal to do the
+same. Also, +~, etc, can take floating point arguments (and messages) to
+add or multiply scalars. THe +~, etc, loops were unrolled to make them
+run faster.
+
+<P>
+A switch~ object is provided to let you switch sub-patches on and off. The
+inlet~ and outlet~ objects were re-written to avoid adding any overhead when
+moving signals in or out of sub patches.
+
+<P>
+In Linux at least, the audio latency is much reduced. It's possible to poll
+for audio I/O lateness errors by sending "pd audiostatus".
+
+<P>
+When reading a sample using tabread4~, you can switch between sample tables
+using the "set" message.
+
+<P>
+A new "textfile" object is like qlist but more flexible.
+
+<P>
+Many help windows got updated (but at least a dozen more need work urgently).
+
+<P>
+A dsp_addv function was added to allow variable-length DSP calls (for writers
+of tilde externs.)
+
+<P>
+It's possible for a tilde extern to have a name ending in "tilde" now. Name
+the setup routine "foo_tilde" for "foo~", etc.
+
+<P>
+The dac~ object was fixed to clip its output when out of range (before it
+wrapped around.)
+
+<P>
+A first line of protection was added against getting numerical underflow
+in delay feedback loops. Before, when a reverberator taled out there was
+a sudden jump in CPU usage because the numerical underflows would trap to the
+kernel. Now, if any delwrite~ is given a value less than 1e-20 or so, it
+records a true zero to avoid this.
+
+<P>
+Signal division checks for divide by zero.
+
+<P>
+A "Font bomb" feature is provided for resizing fonts and stretching and
+contracting patches to fit.
+
+<P>
+Pds now bind themselves to the symbol pd-<window-name).
+
+<P>
+IN Linux, if Pd is called as root it tries to promote its run-time
+priority. You can make pd a setuid root owned program if you want this
+behavior for non-root users who start pd.
+(Don't make pd-gui setuid though. That would make a security
+hole in your system.)
+
+<P>
+The Pd commend line can take multiple "open" arguments.
+
+<P>
+The file search path feature was fixed amd generalized.
+
+<P>
+Alt-clicking a table gives you a dialog to set its x and y range and pixel
+size.
+
+<BR> <BR>
+------------------- 0.25 -----------------
+<P>
+Lots of minor, under-the-hood improvements and bug fixes...
+<P>
+The Netsend/netreceive objects were improved; you can now choose between UDP
+and TCP and there's an outlet to tell you whether they're connected.
+<P>
+You can now alt click on an object to get its help window (and the help
+windows got a fair amount of work.)
+<P>
+multichannel audio I/O -- you can get up to 8 audio cnahhelsin and out.
+On SGI this is sdone correctly; on NT it's done using sequential "stereo"
+devices. I'm not sure of the status of multichannel in linux...
+<P>
+The "text" window got new accelerators and a bigger font size
+<P>
+there are 3 "tool" patches in 7.stuff: filtering, pvoc, ring mod.
+<P>
+In NT, command-line backslashes are converted to forward slashes.
+<P>
+There's a load measurement tool in the "help" menu.
+<P>
+The SGI version contains an n32 binary (look at the "bin" directory).
+
+<BR> <BR>
+------------------- 0.24 ---------------
+<P>
+new objects:
+<BR> - bang - convert any message to a "bang"
+<BR> - qlist - message sequencer
+<BR> - textfile - file to message converter
+<BR> - makefilename - format a name with a variable field
+<BR> - openpanel - "Open" dialog
+<BR> - savepanel - "Save as" dialog
+<P>
+Bug fixes:
+<BR> - Fixed a bug in "const" message to arrays
+<BR> - "exp" was broken on NT, now fixed
+<BR> - phase vocoder example improved
+<BR> - "read" message to arrays now zero out unread samples
+<BR> - bug fix in "key" object
+<BR> - bug fix in ifft~ (thanks to Peter Lunden)
+<BR> - "print" object fixed to distinguish between lists starting with symbols and
+ other messages
+<BR> - polygon, curve, fpolygon, fcurve renamed to fix name clash with Gem
+<BR> - improved "new object" placement on screen
+<BR> - fixed help dialog to remember previous directory (thanks to Harry Castle)
+<BR> - heterogeneous lists
+<P>
+
+Arrays can be written to and read from text files or from 16-bit
+binary files. See ../2.starter/2G for an overview.
+<P>
+
+Guenter Geiger has contributed a Max-style "table" object which
+creates an "array" object in a subwindow.
+<P>
+
+Guenter has also put in a "search path" feature for externs, abstractions,
+etc.
+<P>
+
+The Help menu got reworked.
+<P>
+
+Select and Route were extended to work Zack-style with symbols.
+<P>
+
+"random" takes seeds now (see the "help" window)
+<P>
+
+Some more work on graphical lists; you can see the current state in
+../7.stuff/data-structures. It's still nascent.
+
+------------------- 0.23 -------------------
+<P>
+A first cut at the "pure data" feature is now included. See section 6
+of the documentation for a quick introduction to it; see also patches 12 and
+14 in the FFT examples.
+<P>
+The documentation has been reorganized. The most interesting new features are:
+<BR> - some new "tutorial" patches
+<BR> - 15 "fft" examples
+<BR> - improved help navigation
+<P>
+more bug fixes:
+<BR> - titles on abstractions no longer saved inside file
+<BR> - left-to-right sorting of inlets/outlets now seems to work
+<BR> - nt audio setup got confused when driver couldn't do full duplex
+<BR> - opening window with audio on is now fixed
+<BR> - deleting inlets/outlets deletes connections first (used to crash)
+<BR> - 1e20 parsed correctly now
+<BR> - osc1~ fixed and optimized
+<BR> - resizing arrays with DSP on used to crash; now fixed
+<BR> - pasting now adds to the end of the list (used to add to beginning)
+<BR> - clicking now selects the most recent object when two or more overlap
+<BR> - Pd's "open" and "help" dialogs now maintain separate paths
+<P>
+The phasor~ object's "float" method has been REMOVED -- use the right-hand
+inlet to set the internal phase. This is so that I can later fix all tilde
+objects to convert messages to signals automatically at all signal inputs.
+
+<BR> <BR>
+------------------- 0.22 -------------------
+<BR>
+bug fixes
+<BR> - parsing 1e+006 gave symbol (now float)
+<BR> - "." parsed as number, should be symbol
+<BR> - change GUI polling loop to TK event dispatch (unix only)
+<BR> - improved "tidy up" feature
+<BR> - size check added to text boxes (used to crash; still not correct.)
+<BR> - occasional bug sending text with CRs to tk
+<BR> - binop startup bug
+<BR> - key accelerators for creators wrong
+<BR> - ftom range to 1500
+<BR> - bug in pack, unpack
+<BR> - windows restore bigger than saved
+<BR>
+<BR>
+
+Nt-specific bug fixes:
+<BR> - getsockopt for netreceive fails. Just omitted it for NT.
+<BR> - put tcl dlls in tcl bin, not pd bin
+<BR> --- archive tcl subsystem for easier version updates
+<BR> --- fix README accordingly
+<BR> - deal with bell sound
+<BR> - turn on optimization
+<BR> - looked for audio timeout bug but couldn't find it.
+<BR> <BR>
+
+------------------- 0.21 -------------------
+
+<P>
+bug fixes:
+
+<P>
+table size change with DSP on: It used to crash Pd to resize an array
+when DSP was turned on. This is now fixed.
+
+<P>
+deselect all when locking. When you lock a patch the selection is cleared.
+
+<P>
+unlock when pasting. .. and if you paste into a petch, it's unlocked.
+
+<P>
+
+lost keyboard events. Version 0.20 lost keyboard events and
+forgot window size changes. This should now be fixed.
+
+<BR> subpatches came up in wrong font size
+<BR> dirty flag on window title bar fixed
+<BR> improvement to netreceive suggested by Mark Danks
+<BR> style notes fleshed out as suggested by Larry Troxler
+<BR> fixed Bill Kleinsasser's bug (short and long array in same graph)
+
+<P>
+new features:
+
+<BR> phase setting for phasor~
+<BR> fft objects. Also, block~, for specifying block sizes and overlaps for FFTs.
+<BR> canvas_makefilename() (used, e.g., by array_read and write)
+<BR> "stuff" directory with examples of real Pd applications.
+
+<BR> <BR>
+------------------- 0.20 -------------------
+
+<P>
+In NT, the 0.19 release turned out not to contain all the files needed to make
+TCL run. This problem should now be fixed.
+
+<P>
+Also, the array_write routine was fixed.
+
+<BR> <BR>
+------------------- 0.19 -------------------
+
+<BR>
+notable new objects:
+
+<BR>
+- vcf~, a bandpass filter with a signal input for center frequency.
+<BR>
+- delread, delwrite, vd, as in ISPW Max.
+<BR>
+- various math and midi stuff
+<BR>
+- catch~, throw~, send~, receive~ for nonlocal signal connections
+<P>
+- an experimental facility for array of floats is included. You can make a new
+array (from the "put" menu) which will be given a name such as "array1". You
+can then send it "read <file>", "write <file>", "resize <N>", and "print"
+messages. File reading and writing is in ascii. "resize" changes the size of
+the array, and "print" prints its vital signs. You can then use "tabread4~"
+to do a 4-point interpolating table lookup, and tabwrite~ to write audio
+samples into the table.
+<P>
+Numbers now default to floating point, although certain objects like "spigot"
+and "metro" still convert their boolean inputs to integers so that 0.5 is
+"false." This behavior will probably change later. The "div" and "mod"
+objects are introduced for explicit integer division and remainder.
+<P>
+Number boxes drag in integer increments, or in hundredths if you hold the
+"shift" key down when you click.
+<P>
+Pd documents now save their font sizes. The font size is global to an entire
+document. New documents come up in the font size Pd was started in (using
+the "-font" flag.) If you want to change the font size of an existing
+document, use a text editor; the font size is the last argument on the first
+line. 8, 10, 12, 14, 16, 18, and 24 are supported.
+<P>
+The abbreviations "t," "f," and "i" stand for "trigger,", "float", and "int."
+<P>
+Inlets and outlets of subpatches are now sorted correctly; although there is
+still a problem deleting inlets/outlets which have connections.
+<P>
+The size and screen location of Pd documents is saved correctly.
+<P>
+Tilde objects now work in "subpages" although there is no way to send
+signals through their inlets and outlets; use throw~/catch~ or send~/receive~.
+<P>
+On NT, the default is to open both audio output and input (this used not
+to work.) The situation is still shaky; audio seems to hang up sporadically
+on my machine; but I seem to have installed my audio driver wrong anyway.
+I had to set a huge output FIFO (1/3 sec or so!) to get it to work at all.
+You can type "pd -dac", "pd -adc", or "pd -nosound" to get output only,
+input only, or no audio at all.
+NT's MIDI input and output are supported, but on my machine MIDI output is
+flaky. I'm curious how all this will work on other machines...
+<P>
+The list of classes is now:
+<P>
+
+GENERAL:
+field inlet outlet print int float send receive select route pack unpack
+trigger spigot moses delay metro line timer makenote stripnote random loadbang
+serial get netsend netreceive
+<P>
+
+MATH:
++ - * / == != > < >= <= & && | || %
+mod div sin cos tan atan atan2 sqrt log exp abs
+mtof ftom powtodb rmstodb dbtopow dbtorms
+<P>
+
+MIDI:
+notein ctlin pgmin bendin touchin polytouchin noteout ctlout pgmout bendout
+touchout polytouchout
+<P>
+
+SIGNAL:
+dac~ adc~ sig~ line~ snapshot~ +~ -~ *~ /~ phasor~ cos~ vcf~ noise~ env~ hip~
+lop~ bp~ biquad~ samphold~ clip~ rsqrt~ sqrt~ wrap~ print~ scope~ tabwrite~
+tabread4~ send~ receive~ catch~ throw~ delwrite~ delread~ vd~
+
+<BR> <BR>
+------------------- 0.18 -------------------
+
+<BR>
+Release notes now descrie the three platforms Pd runs on: IRIX and
+NT (maintained at UCSD) and LINUX, maintained by Guenter Geiger.
+
+<P>
+menu "close" on a dirty document now checks if you really want to close
+without saving (although "quit" will still exit Pd without verification.)
+
+<P>
+Got rid of "dll" error printout when loading abstractions
+
+<BR> <BR>
+------------------- 0.12 - 0.17 -------------------
+
+<BR>
+got Pd running under NT, although driver problems remain. Gem is also
+distributed for both platforms.
+
+<BR> <BR>
+------------------- 0.11 -------------------
+
+<BR>
+Here's a list of all the objects in this release:
+
+<BR>
+general: print int float send receive select pack unpack trigger spigot
+<BR>
+time handling: delay metro line timer
+<BR>
+arithmetic: + + - - * * / / == == != != > > < < >= >= <= <= & && | || %
+<BR>
+midi: notein noteout makenote stripnote
+<BR>
+other: random get
+<BR>
+signals: dac~ adc~ sig~ line~ snapshot~ +~ *~
+<BR>
+signal oscillators: phasor~ cos~
+<BR>
+signal filters: env~ hip~
+<BR>
+signal debugging : print~ scope~
+<BR>
+<BR>
+
+"spigot" replaces "gate" but has the inputs reversed.
+
+<BR> <BR>
+------------------- 0.10 -------------------
+<BR>
+
+Many bug fixes. This was the first pre-release to be put on the FTP site.
+
+<BR> <BR>
+------------------- 0.09 -------------------
+
+<BR> set up the "Help" menu
+<BR> Bug in DSP sorting fixed
+<BR> "Notein" and "noteout" objects
+<BR> Comments from the Put menu say "comment" (they were invisible before)
+<BR> The scheduler deals better when sound I/O malfunctions
+
+<BR> <BR>
+------------------- 0.08 -------------------
+
+<BR> metro bug
+<BR> scrollbars
+<BR> scheduler bug
+<BR> text box wraparound at 80 chars.
+<BR> fixed boxes to reconnect on retype
+
+<BR> <BR>
+------------------- 0.07 -------------------
+
+<BR>
+- made an adc~ object
+
+<BR> <BR>
+------------------- 0.06 -------------------
+
+<BR>
+- fixed two bugs in DSP sorting
+<BR>
+- added DSP on/off gui
+<BR>
+- added lock/unlock and changed the cursor behavior
+<BR>
+- fixed -font flag to set font pointsize
+
+<BR> <BR>
+------------------- 0.05 -------------------
+<P>
+- added scope~, which is just a stopgap until real sound editing comes up.
+<BR>
+- improved the open panel slightly.
+<BR>
+- added atoms (int only).
+<BR>
+- reworked text editing to reside in Pd, not Pd-gui.
+<BR>
+- included a dbx-debuggable Pd in the distribution. I haven't yet figured
+ out how to get dbx to work with externs though.
+
+<BR> <BR>
+------------------- 0.04 -------------------
+<P>
+fixed "cut" which crashed 0.03 if DSP was running.
+added clip~, print~, line~, snapshot~.
+
+
+<BR> <BR>
+------------------- 0.03 -------------------
+<P>
+"pd dsp 1", "pd dsp 0" messages added. If you edit a patch with DSP on,
+PD resorts the DSP network as needed. Unconnected and multiple signal inlets
+are allowed.
+
+<BR> <BR>
+------------------- 0.02 -------------------
+<P>
+A DSP network mechanism has been added. DSP objects are:
+sig~, +~, *~, phasor~, cos~.
+<P>
+Loading of externs is provided (although there is no search path mechanism
+so the extern has to be in the patch's current directory.) Look in
+pd/externs for an example.
+
+<BR> <BR>
+
+------------------- 0.01. -------------------
+<P>
+This first release serves mostly to test the "release" mechanism. A Pd
+"canvas" object is provided which does both graphing and patch editing.
+The editing features apply only to the Max-like part; the graphs have
+to be edited into a Pd file via text editor.
+<P>
+Four menu items (in the "put" menu) create the four kinds of "patchable"
+objects; they can be dragged and connected as in Max; to break a connection,
+just click on it (the cursor becomes a turkey to indicate this.) Cut,
+paste, and duplicate seem to work, and a "Pd" class offers subwindows.
+<P>
+The following max-like objects are included:
+
+ print;
+ +, *, -, /, ==, !=, >, <, >=, <=, &, |, &&, ||, %;
+ int, float, pack, unpack, trigger;
+ delay, metro, timer;
+ send, receive.
+<P> -----------------------------------------
+
+<H4> <A name=s2> 5.2. known bugs </A> </H4>
+
+<P> In the list below, starred items are still things needing attention...
+
+<P> *1. Timing of MIDI input/output is very shaky. Audio I/O is primitive, but
+there's at least a way to detect errors now for linux and NT.
+
+<P> *2. There is no flow control for graphical updates yet; the
+real-time process can easily block trying to write too fast to the GUI.
+
+<P> 3. PD dies if your patch has an infinite loop [fixed in 0.30 release.]
+
+<P> *4. If you cut a box which is a "Pd" or abstraction whose subpatch has
+items selected, Pd dies.
+
+<P> *5. Tables and other drawable items can draw far outside the window; there's
+no sanity check, Huge tables (>1000 points) are only partially drawn
+(the first 1000 points.)
+
+<P> 6. There's no way to order force a delread~ to make it read after
+a delwrite~ has written. [but see under 3.audio.examples how to do this now.]
+
+<P> 7. Pd doesn't know to suspend graphics updates when you minimize objects.
+Presumably minimization makes things better but it doesn't cut off graphics
+computation entirely as it should. [fixed for 0.34]
+
+<P> 8. If you load a nonexistent extern you get a spurious message,
+"consistency check failed: canvas_setargs". [fixed for 0.27 release.]
+
+<P> 9. Typing backslashes into objects upsets Tk [0.29 should suppress all
+backslashes; a real fix might come later.]
+
+<P> 10. Never type a dollar sign into a comment; you may have trouble
+opening your patch afterward... [fixed somewhere around 0.32]
+
+<P> *11. You'd better Turn DSP off before you type into a box that currently
+holds a "pd" object with tilde objects in the subpatch.
+
+<P> *12. In Linux, if you hit control C while Pd is opening MIDI, Pd hangs.
+
+<P> *13. In linux, Pd doesn't report audio data-late errors yet.
+
+<P> *14. Several objects, notably dac~, adc~, and env~, are incompatible with
+uses of block~ or switch~ objects that change block size frmo the default of
+64. Using switch~ without reblocking causes no problem. Don't try to
+read/write delay lines or use send~/receive~, or throw~/catch~, between
+windows with different block sizes.
+
+<H4> <A name=s3> 5.3. differences from Max/MSP </A> </H4>
+
+<P> It wasn't anyone's intention to make Pd a Max/MSP clone, but on the
+other hand, if there's no reason for a feature to appear differently in
+Pd than in Max/MSP, the choices in Pd tend to hew to those in Max/MSP.
+Moreover, some effort has been undertaken (but more is needed) to make the
+two interoperable.
+
+<P> You can use Pd to import and export patches to Max/MSP; just save as
+text to a file with extension ".pat", and then open it in Pd. You'll at
+least get something. If you stick to common or commonizable features
+you can actually develop patches for both platforms.
+
+<P> When specific objects exist on one platform and not on the other, it's
+often possible to make abstractions to imitate the missing objects, in a
+kind of personalized compatibility library.
+
+<P> There are, however, differences in semantics you'll want to know about;
+a partial list follows.
+
+<P> <bold> abstraction arguments. </bold>
+In Pd you can edit instantiations of abstractions and save the result back
+to the file of the abstraction. This isn't possible in Max, because the
+instantiations are different from the abstraction itself in that "#1", etc.,
+are replaced by the instantiation arguments. In Pd, these arguments appear
+as "$1", etc, and are translated at a slightly later stage of the instantiation
+process so that you still see them as "$" variables in the instantiation.
+<A href="x2.htm#s7.1"> (see Section 2.7. abstractions) </A>
+
+<P> In Pd, to make current all instantiations of the
+abstraction, either delete and recreate them or close and open the patch;
+this is done automatically in Max/MSP.
+
+<P> In Pd, if you select "save" while in a subpatch, the parent is saved. In
+Max/MSP, if you do this a dialogue box comes up asking if you want to save the
+subpatch as a separate file. (if you want to save a subpatch to a file in Pd,
+you have to copy and paste the contents to a new document.
+
+<P> In Pd, inlets and outlets are ordinary text objects; in Max/MSP they're
+"gui" objects from the palette.
+
+<P> In Max/MSP, if an object's outlet is connected to several destinations,
+corresponding messages are always sent in right-to-left screen order. In
+Pd, the messages are sent in the order you made the connections in. In either
+case, in situations where you care about the order it's appropriate to use
+a "trigger" object to specify.
+
+<P> In Pd, there's no "gate"; instead it's "spigot" with the inlets in the
+opposite, more natural order.
+
+<P> Switching subsets of the DSP patch on and off is done in completely
+different ways in Pd and Max/MSP, and block sizes are handled differently as
+well.
+
+<P> Max offers many "GUI" objects such as sliders, dials, VU meters, piano
+keyboards, even "bpatchers." Until version 0.34, the only two in Pd were the
+number box and graphical arrays. Starting in version 0.34, Pd incorporates
+Thomas Musil's GUI objects: sliders, switches, and so on. (Thanks Thomas!)
+Beyond this essential collection of GUI objects, it's unlikely you'll ever find
+any commonality between the two. Also, as of 0.34, importing and exporting to
+Max doesn't know about the Musil objects; I'll try to get that fixed for 0.35.
+
+<P> In Pd there's no "preset" object (I now think it's basically a bad idea)
+and you have to use explicit sends and receives to restore values to number
+boxes. Then just make a "message" box to re-send the values you want.
+
+<P> In Macintosh land, instead of getting tabosc4~ and arrays, you get cycle~
+and buffer~. The only gotcha is that you probably can't draw in buffer~ with
+the mouse as you can with arrays, but at least it's possible to
+make a patch that copies a "table" into a "buffer~".
+
+<P> The "bpatcher" feature in Max has a correlate, "graph on parent" subpatches,
+in Pd; however, Pd's version is quite different from Max's.
+
+</BODY>
+</HTML>
diff --git a/pd/doc/2.control.examples/00.INTRO.txt b/pd/doc/2.control.examples/00.INTRO.txt
new file mode 100644
index 00000000..c799044d
--- /dev/null
+++ b/pd/doc/2.control.examples/00.INTRO.txt
@@ -0,0 +1,19 @@
+This series of patches serves as a tutorial for Pd's "control" structure, as
+opposed to its audio functions (covered in the next series.) These tutorials
+are inspired by Chris Dobrian's Max tutorial patches.
+
+It's probably best to look at the first section here before going on to the
+audio portion, but afterward there's no reason not to browse back and forth
+between the two, and even the third series on "fft" based techniques.
+
+The relationship between "control" and "audio" is described in Pd's HTML
+documentation, which is more like a reference manual than an introduction.
+Also, you probably will need to look there to get Pd up and running stably so
+that you can enjoy the patches here.
+
+The patches are roughly divided as shown:
+
+1. objects and connections
+2. subpatches, tables, and organization
+3. specific techniques.
+
diff --git a/pd/doc/2.control.examples/01.PART1.hello.pd b/pd/doc/2.control.examples/01.PART1.hello.pd
new file mode 100644
index 00000000..e0a4daf1
--- /dev/null
+++ b/pd/doc/2.control.examples/01.PART1.hello.pd
@@ -0,0 +1,16 @@
+#N canvas 9 21 600 496 12;
+#X msg 204 32 hello world;
+#X obj 204 105 print;
+#X floatatom 321 32 0 0 0;
+#X text 215 48 message;
+#X text 319 49 atom;
+#X text 201 123 object;
+#X text 53 150 There are four types of text objects in Pd: message \, atom \, object \, and comment.;
+#X text 54 187 Messages respond to mouse clicks by sending their contents to one or more destinations. The usual destination is the "outlet" at the lower left corner of the box.;
+#X text 55 239 Click the message box and watch the terminal window Pd was started in. You should see the "hello world" message appear.;
+#X text 55 278 Atoms respond to "Dragging" up and down with the mouse \, by changing their contents and sending the result out their outlets. You can also type at an atom after clicking on it \; hit "enter" to output the number or click anywhere else to cancel.;
+#X text 52 359 Objects \, like "print" above \, may have all sorts of functions depending on what's typed into them. The "print" object simply prints out every message it receives.;
+#X text 53 415 To get help on an object \, right-click it. You should see a "help window" for the object.;
+#X text 354 470 updated for release 0.33;
+#X connect 0 0 1 0;
+#X connect 2 0 1 0;
diff --git a/pd/doc/2.control.examples/02.editing.pd b/pd/doc/2.control.examples/02.editing.pd
new file mode 100644
index 00000000..a2442ee8
--- /dev/null
+++ b/pd/doc/2.control.examples/02.editing.pd
@@ -0,0 +1,17 @@
+#N canvas 1 0 581 630 12;
+#X msg 195 36 hello world;
+#X obj 195 72 print;
+#X floatatom 304 36 0 0 0;
+#X text 194 15 message;
+#X text 304 14 atom;
+#X text 255 73 object;
+#X text 34 102 When you first open a Pd document like this one \, your cursor will be an arrow. Select "edit mode" in the Edit menu and the cursor will change to the image of a hand. The patch is now in edit mode. You can move any object by dragging it.;
+#X text 33 185 Select "Edit mode" again in the Edit menu and you're back to the arrow cursor which acts on objects without moving them.;
+#X text 32 373 You can create new objects by duplicating existing ones using the "duplicate" menu item. You can also "cut" and "paste" them. If you duplicate several connected objects the connections will be replicated too.;
+#X text 33 237 In Edit mode \, if you click on a message \, object \, or comment \, you can then retype the text. For objects this will create a new object and delete the old one. Pd will try to reconnect the newly created object in the same way as the old one.;
+#X text 34 442 Edit mode also lets you make and break connections between objects. Put the "hand" cursor over a line connecting two objects: it turns into an X. Clicking will delete the connection. Hold the cursor over an outlet and it becomes a circle (a patch point). Drag to any box and release \; you will be connected to the nearest inlet.;
+#X text 32 320 When you're done changing the contents of the box \, click outside the box to deselect it. This tells Pd to incorporate the new text.;
+#X text 328 604 updated for Pd version 0.33;
+#X text 35 544 The "put" menu creates new text items of any of the four types. You can also put a "symbol" box \, analogous to a number box but for showing and entering text strings.;
+#X connect 0 0 1 0;
+#X connect 2 0 1 0;
diff --git a/pd/doc/2.control.examples/03.connections.pd b/pd/doc/2.control.examples/03.connections.pd
new file mode 100644
index 00000000..3cde81fa
--- /dev/null
+++ b/pd/doc/2.control.examples/03.connections.pd
@@ -0,0 +1,58 @@
+#N canvas 185 28 660 552 12;
+#X floatatom 76 400 0 0 0;
+#X floatatom 189 401 0 0 0;
+#X floatatom 76 307 0 0 0;
+#X floatatom 553 161 0 0 0;
+#X floatatom 599 162 0 0 0;
+#X obj 553 135 +;
+#X floatatom 553 105 0 0 0;
+#X obj 599 136 +;
+#X text 114 16 In Pd \, most objects carry out their functions when
+they get messages in their rightmost inlets \, and their other inlets
+are for storing values that can modify the next action. Here \, the
+"+" object does its thing only when the left-hand input changes.;
+#X floatatom 26 109 0 0 0;
+#X floatatom 26 17 0 0 0;
+#X floatatom 48 41 0 0 0;
+#X obj 26 85 +;
+#X text 3 64 hot;
+#X text 53 66 cold;
+#X text 232 105 Here's the downside: drag this--->;
+#X text 551 180 good;
+#X text 600 181 bad;
+#X obj 76 376 *;
+#X obj 189 377 -;
+#X text 15 400 square;
+#X text 229 402 first difference;
+#X obj 76 330 trigger float float;
+#X text 412 526 updated for Pd version 0.33;
+#X text 19 433 Trigger takes any number of "bang" and "float" arguments
+(among others) and copies its input to its outlets \, in the requested
+forms \, in right-to-left order. Hook it to two inputs without crossing
+the wires and you get the expected result. Cross the wires and you
+get a memory effect.;
+#X text 9 136 In Pd you must sometimes think about what order an object
+is going to get its messages in. If an outlet is connected to more
+than one inlet it's undefined which inlet will get the cookie first.
+I've rigged this example so that the left-hand side box gets its inputs
+in the good \, right-to-left order \, so that the hot inlet gets hit
+when all the data are good. The "bad adder" happens to receive its
+inputs in the wrong order and is perpetually doing its addition before
+all the data are in. There's an object that exists solely to allow
+you to control message order explicitly:;
+#X connect 2 0 22 0;
+#X connect 5 0 3 0;
+#X connect 6 0 7 0;
+#X connect 6 0 7 1;
+#X connect 6 0 5 1;
+#X connect 6 0 5 0;
+#X connect 7 0 4 0;
+#X connect 10 0 12 0;
+#X connect 11 0 12 1;
+#X connect 12 0 9 0;
+#X connect 18 0 0 0;
+#X connect 19 0 1 0;
+#X connect 22 0 18 0;
+#X connect 22 0 19 1;
+#X connect 22 1 18 1;
+#X connect 22 1 19 0;
diff --git a/pd/doc/2.control.examples/04.messages.pd b/pd/doc/2.control.examples/04.messages.pd
new file mode 100644
index 00000000..a56dd92b
--- /dev/null
+++ b/pd/doc/2.control.examples/04.messages.pd
@@ -0,0 +1,35 @@
+#N canvas 0 0 591 442 12;
+#X floatatom 225 110 0 0 0;
+#X floatatom 184 109 0 0 0;
+#X msg 184 56 5 6;
+#X floatatom 132 108 0 0 0;
+#X floatatom 64 105 0 0 0;
+#X text 30 21 Most Pd messages are just numbers or short lists of numbers:;
+#X msg 64 55 5;
+#X obj 64 80 + 9;
+#X obj 132 83 +;
+#X obj 184 84 unpack;
+#X msg 288 55 5;
+#X obj 288 107 print;
+#X obj 288 81 pack 34 78;
+#X msg 132 55 5 6;
+#X floatatom 195 328 0 0 0;
+#X obj 195 303 +;
+#X msg 195 254 1.2 3.4;
+#X msg 205 277 5 6;
+#X text 36 206 Unlike Max \, in Pd all numbers are floating point. Numbers whose values happen to be integers are displayed without decimal points.;
+#X text 31 363 For more on messages \, get help on any message box by right-clicking.;
+#X text 329 409 updated for Pd release 0.33;
+#X text 34 149 If you send a list to an object with more than one inlet \, the items in the list are spread out over the inlets \, as seen in the 5+6 example above.;
+#X connect 2 0 9 0;
+#X connect 6 0 7 0;
+#X connect 7 0 4 0;
+#X connect 8 0 3 0;
+#X connect 9 0 1 0;
+#X connect 9 1 0 0;
+#X connect 10 0 12 0;
+#X connect 12 0 11 0;
+#X connect 13 0 8 0;
+#X connect 15 0 14 0;
+#X connect 16 0 15 0;
+#X connect 17 0 15 0;
diff --git a/pd/doc/2.control.examples/05.counter.pd b/pd/doc/2.control.examples/05.counter.pd
new file mode 100644
index 00000000..14c48dea
--- /dev/null
+++ b/pd/doc/2.control.examples/05.counter.pd
@@ -0,0 +1,45 @@
+#N canvas 0 0 685 496 12;
+#X floatatom 107 424 0 0 0;
+#X msg 53 344 bang;
+#X obj 107 399 + 1;
+#X obj 376 262 + 1;
+#X floatatom 152 197 0 0 0;
+#X floatatom 108 245 0 0 0;
+#X msg 108 196 bang;
+#X floatatom 169 107 0 0 0;
+#X msg 112 58 bang;
+#X obj 169 82 + 1;
+#X text 31 21 Here's a simple counter. Click repeatedly on the "bang
+message to see it:;
+#X text 422 263 to its cold inlet.;
+#X text 25 284 The incremented value is stored for the next "bang"
+to spit out.;
+#X text 28 322 Here's a timed counter. Hit the "bang" to start it...
+;
+#X obj 53 373 metro 500;
+#X msg 99 344 stop;
+#X obj 112 83 float;
+#X text 28 132 The "float" box is a storage element holding one floating-point
+number. The cold inlet (i.e. \, the one on the right) stores numbers.
+Sending the message "bang" to the hot inlet gets the number back out:
+;
+#X obj 108 221 float;
+#X obj 53 399 float;
+#X text 25 263 Float's outlet above is connected via;
+#X text 384 462 updated for Pd version 0.34;
+#X text 142 373 <-- new object: metronome. The "500" means every 500
+milliseconds--i.e. \, twice a second.;
+#X connect 1 0 14 0;
+#X connect 2 0 0 0;
+#X connect 2 0 19 1;
+#X connect 4 0 18 1;
+#X connect 6 0 18 0;
+#X connect 8 0 16 0;
+#X connect 9 0 7 0;
+#X connect 9 0 16 1;
+#X connect 14 0 19 0;
+#X connect 15 0 14 0;
+#X connect 15 0 14 0;
+#X connect 16 0 9 0;
+#X connect 18 0 5 0;
+#X connect 19 0 2 0;
diff --git a/pd/doc/2.control.examples/06.more.counters.pd b/pd/doc/2.control.examples/06.more.counters.pd
new file mode 100644
index 00000000..e0ef3c40
--- /dev/null
+++ b/pd/doc/2.control.examples/06.more.counters.pd
@@ -0,0 +1,55 @@
+#N canvas 8 0 659 487 12;
+#X floatatom 147 177 0 0 0;
+#X obj 147 151 + 1;
+#X msg 147 47 bang;
+#X obj 147 99 metro 500;
+#X msg 56 105 stop;
+#X obj 147 125 float;
+#X obj 147 73 trigger bang bang;
+#X msg 261 105 0;
+#X obj 56 79 select 10;
+#X text 305 102 first set value to zero;
+#X text 304 73 initialization is in two steps;
+#X text 305 121 (before starting the metronome);
+#X text 9 128 conditionally;
+#X text 9 145 stop the;
+#X text 10 159 metronome;
+#X text 184 46 <--- click here to start;
+#X floatatom 85 289 0 0 0;
+#X obj 85 315 >= 0;
+#X obj 85 341 select 0 1;
+#X obj 85 393 float;
+#X floatatom 139 420 0 0 0;
+#X msg 119 367 bang;
+#X obj 139 394 + 1;
+#X msg 85 367 -1;
+#X text 131 313 <-- are we nonnegative? (1 if true \, 0 if false);
+#X text 180 340 <-- selectively bang the first or second outlet;
+#X text 167 363 <-- as a result either clear or increment the counter
+;
+#X text 32 11 Here's a counter that counts from 1 to 10:;
+#X text 392 452 updated for Pd version 0.34;
+#X text 33 200 We're using one new object \, "select \, " which outputs
+a bang when it gets a matching value (10). This is useful for doing
+conditional computations \, such as this one which counts while its
+input is 0 or positive but clears when negative:;
+#X connect 1 0 0 0;
+#X connect 1 0 5 1;
+#X connect 1 0 8 0;
+#X connect 2 0 6 0;
+#X connect 3 0 5 0;
+#X connect 4 0 3 0;
+#X connect 5 0 1 0;
+#X connect 6 0 3 0;
+#X connect 6 1 7 0;
+#X connect 7 0 5 1;
+#X connect 8 0 4 0;
+#X connect 16 0 17 0;
+#X connect 17 0 18 0;
+#X connect 18 0 23 0;
+#X connect 18 1 21 0;
+#X connect 19 0 22 0;
+#X connect 21 0 19 0;
+#X connect 22 0 19 1;
+#X connect 22 0 20 0;
+#X connect 23 0 19 0;
diff --git a/pd/doc/2.control.examples/07.time.pd b/pd/doc/2.control.examples/07.time.pd
new file mode 100644
index 00000000..69398bd9
--- /dev/null
+++ b/pd/doc/2.control.examples/07.time.pd
@@ -0,0 +1,39 @@
+#N canvas 0 0 724 474 12;
+#X text 34 13 Besides the metronome \, there are three objects for
+dealing with time:;
+#X obj 64 117 print;
+#X msg 64 59 bang;
+#X msg 110 61 stop;
+#X obj 64 89 delay 2000;
+#X text 161 44 The delay objects sechedules an event for a future time
+expressed in milliseconds. Unlike in Max \, time values need not be
+integers. If a delay has been scheduled and you "bang" it again \,
+it is rescheduled (the previously scheduled output is cancelled.);
+#X msg 76 190 bang;
+#X obj 76 237 timer;
+#X text 160 117 The right inlet can be used to set the time value without
+scheduling any output.;
+#X text 35 156 The timer \, shown below \, measures the time elapsed
+between its left and right inlets:;
+#X obj 106 212 delay 123.45;
+#X floatatom 76 262 0 0 0;
+#X text 29 287 Note that all time calculations are idealized \; they
+do not show the effects of computation time or OS latency. This way
+you can write deterministic algorithms dealing with time passage.;
+#X obj 74 385 pipe 2000;
+#X floatatom 74 358 0 0 0;
+#X floatatom 74 411 0 0 0;
+#X text 165 359 The pipe object allocates memory dynamically in order
+to schedule any number of delayed events. The events may hold any collection
+of data (as usual \, for more details you can consult the help window.)
+;
+#X text 442 440 updated for Pd version 0.34;
+#X connect 2 0 4 0;
+#X connect 3 0 4 0;
+#X connect 4 0 1 0;
+#X connect 6 0 7 0;
+#X connect 6 0 10 0;
+#X connect 7 0 11 0;
+#X connect 10 0 7 1;
+#X connect 13 0 15 0;
+#X connect 14 0 13 0;
diff --git a/pd/doc/2.control.examples/08.depthfirst.pd b/pd/doc/2.control.examples/08.depthfirst.pd
new file mode 100644
index 00000000..8820d226
--- /dev/null
+++ b/pd/doc/2.control.examples/08.depthfirst.pd
@@ -0,0 +1,48 @@
+#N canvas 144 162 632 551 12;
+#X msg 64 51 1;
+#X obj 89 150 + 1;
+#X obj 209 187 print x1;
+#X obj 64 209 print x3;
+#X obj 114 122 print x2;
+#X obj 209 100 + 1;
+#X obj 209 129 + 1;
+#X obj 209 158 + 1;
+#X obj 64 80 t f f f f;
+#X obj 89 179 print x2;
+#X text 34 13 In Pd \, message passing is depth first \, so that in
+this patch:;
+#X text 104 51 <-- click here;
+#X text 17 243 ... you get "x1" first \, notwidthstanding the fact
+that "x2" and "x3" appear to be closer to the source. This means that
+you shouldn't do this:;
+#X msg 76 304 1;
+#X text 116 304 <-- maybe you shouldn't click here;
+#X obj 115 334 + 1;
+#X obj 76 333 f;
+#X floatatom 76 365 0 0 0;
+#X text 377 520 updated for Pd version 0.34;
+#X text 35 393 ... because the "depth" is infinite. The counters you've
+seen always have the message chain terminated somewhere in a cold inlet:
+;
+#X msg 75 453 1;
+#X obj 114 483 + 1;
+#X obj 75 482 f;
+#X floatatom 75 514 0 0 0;
+#X text 115 453 <-- better;
+#X connect 0 0 8 0;
+#X connect 1 0 9 0;
+#X connect 5 0 6 0;
+#X connect 6 0 7 0;
+#X connect 7 0 2 0;
+#X connect 8 0 3 0;
+#X connect 8 1 1 0;
+#X connect 8 2 4 0;
+#X connect 8 3 5 0;
+#X connect 13 0 16 0;
+#X connect 15 0 16 0;
+#X connect 16 0 17 0;
+#X connect 16 0 15 0;
+#X connect 20 0 22 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 21 0;
diff --git a/pd/doc/2.control.examples/09.send_receive.pd b/pd/doc/2.control.examples/09.send_receive.pd
new file mode 100644
index 00000000..374a74b5
--- /dev/null
+++ b/pd/doc/2.control.examples/09.send_receive.pd
@@ -0,0 +1,35 @@
+#N canvas 136 31 738 479 12;
+#X floatatom 88 199 0 0 0;
+#X obj 88 172 receive crackers;
+#X floatatom 248 204 0 0 0;
+#X obj 248 177 receive pickles;
+#X obj 88 145 send crackers;
+#X obj 250 139 send pickles;
+#X obj 389 138 send pickles;
+#X floatatom 392 203 0 0 0;
+#X obj 392 176 receive pickles;
+#X msg 51 306 \; pickles 99 \; crackers 56;
+#X floatatom 88 118 0 0 0;
+#X floatatom 250 112 0 0 0;
+#X floatatom 389 111 0 0 0;
+#X obj 371 404 r crackers;
+#X obj 371 377 s crackers;
+#X text 39 392 send and receive can be abbreviated:;
+#X text 48 245 You can use the semicolon feature of message boxes to
+address receives \, too. This is useful if you want to do a whole list
+of things:;
+#X text 166 305 The transaction takes place in zero time---i.e. \,
+if you tried to use "timer" to measure the time delay between the two
+\, you would get zero.;
+#X text 459 447 updated for Pd version 0.34;
+#X text 51 6 The send and receive objects allow you to make non-local
+connections. These work globally--you can use them to make two different
+patches intercommunicate if you wish. Any message a "send" gets appears
+at the output of every receive of the same name. There can be any number
+of sends and receives sharing the same name:;
+#X connect 1 0 0 0;
+#X connect 3 0 2 0;
+#X connect 8 0 7 0;
+#X connect 10 0 4 0;
+#X connect 11 0 5 0;
+#X connect 12 0 6 0;
diff --git a/pd/doc/2.control.examples/10.more.messages.pd b/pd/doc/2.control.examples/10.more.messages.pd
new file mode 100644
index 00000000..7f0c8539
--- /dev/null
+++ b/pd/doc/2.control.examples/10.more.messages.pd
@@ -0,0 +1,56 @@
+#N canvas 91 95 675 539 12;
+#X obj 211 341 print;
+#X msg 52 89 3 \, 4 \, 5;
+#X msg 44 62 3 4 5;
+#X msg 57 313 3 \$1 5;
+#X floatatom 57 286 4 0 0;
+#X msg 211 315 \$2 \$1 5;
+#X msg 211 290 45 67;
+#X msg 289 290 45 67;
+#X floatatom 28 425 4 0 0;
+#X floatatom 76 425 4 0 0;
+#X floatatom 332 179 4 0 0;
+#X floatatom 186 182 4 0 0;
+#X obj 186 155 receive number9;
+#X obj 332 155 receive 9bis;
+#X obj 44 178 print;
+#X text 27 5 In addition to using semicolons to separate messages \,
+you can use commas \, which continue a stream of messages to the same
+destination. Thus:;
+#X msg 65 116 3 \; number9 5 \; 9bis 45;
+#X text 126 89 <-- three separate messages;
+#X text 109 58 <-- one message: the list \, "3 4 5".;
+#X text 167 114 <-- three separate messages \, with three destinations.
+;
+#X text 406 511 updated for Pd version 0.34;
+#X msg 289 315 \; number9 \$1 \; 9bis \$2;
+#X text 9 209 You can use "$1" \, etc. \, as variables in messages.
+Send the message box a list whose elements supply the values. A number
+is just a list with one element.;
+#X obj 57 339 print;
+#X text 51 265 one variable:;
+#X text 216 263 two variables:;
+#X text 1 367 But to really exploit the possibilities using multiple
+variables \, you will need the "pack" object to get two or more values
+into the same message:;
+#X obj 28 507 print;
+#X obj 28 455 pack 0 0 0;
+#X floatatom 124 425 4 0 0;
+#X msg 28 481 cis \$1 \, boom \$2 \, bah \$3;
+#X text 124 455 <-- creation arguments to "pack" set the number of
+inlets.;
+#X connect 1 0 14 0;
+#X connect 2 0 14 0;
+#X connect 3 0 23 0;
+#X connect 4 0 3 0;
+#X connect 5 0 0 0;
+#X connect 6 0 5 0;
+#X connect 7 0 21 0;
+#X connect 8 0 28 0;
+#X connect 9 0 28 1;
+#X connect 12 0 11 0;
+#X connect 13 0 10 0;
+#X connect 16 0 14 0;
+#X connect 28 0 30 0;
+#X connect 29 0 28 2;
+#X connect 30 0 27 0;
diff --git a/pd/doc/2.control.examples/11.review.pd b/pd/doc/2.control.examples/11.review.pd
new file mode 100644
index 00000000..9e5b6c95
--- /dev/null
+++ b/pd/doc/2.control.examples/11.review.pd
@@ -0,0 +1,42 @@
+#N canvas 255 248 675 539 12;
+#X text 406 511 updated for Pd version 0.34;
+#X obj 39 232 receive;
+#X obj 39 203 send;
+#X obj 39 289 pack;
+#X obj 111 233 r;
+#X obj 82 203 s;
+#X obj 40 348 timer;
+#X obj 40 60 float;
+#X obj 39 175 select;
+#X obj 40 89 +;
+#X obj 40 117 >=;
+#X obj 39 146 print;
+#X obj 39 260 trigger;
+#X obj 95 61 f;
+#X obj 100 176 sel;
+#X obj 111 259 t;
+#X obj 39 318 unpack;
+#X obj 40 435 pipe;
+#X obj 40 377 delay;
+#X obj 40 406 metro;
+#X text 20 8 So far we've seen the following objects \, some of which
+have abbreviations. Right click on any one to get reference documentation:
+;
+#X text 150 205 wireless message send;
+#X text 151 229 wireless message receive;
+#X text 145 175 test for two equal numbers;
+#X text 151 258 control message order and format;
+#X text 148 293 combine atoms (e.g. \, numbers) into a list;
+#X text 146 319 take a list apart into atoms;
+#X text 146 146 printout;
+#X text 147 90 arithmetic;
+#X text 75 89 (etc.);
+#X text 74 117 (etc.);
+#X text 145 117 comparison;
+#X text 145 60 store a number;
+#X text 145 348 measure elapsed time;
+#X text 145 379 pass a message after delay;
+#X text 145 437 multiple delay;
+#X text 143 409 repeated message;
+#X text 38 473 There are many others... you can see a complete list
+in INTRO.txt in the reference patches (../5.reference).;
diff --git a/pd/doc/2.control.examples/12.PART2.subpatch.pd b/pd/doc/2.control.examples/12.PART2.subpatch.pd
new file mode 100644
index 00000000..5bd40306
--- /dev/null
+++ b/pd/doc/2.control.examples/12.PART2.subpatch.pd
@@ -0,0 +1,72 @@
+#N canvas 84 47 648 623 12;
+#X msg 29 318 bang;
+#X floatatom 432 341 0 0 0;
+#X text 32 14 You can nest entire windows inside Pd boxes (and so on
+\, as deep as you wish.) There are two different ways to do it. First
+\, if you just want to add a room to the house \, so to speak \, type
+;
+#N canvas 344 151 422 119 sample-subpatch 1;
+#X text 39 43 this is a subpatch of the main patch.;
+#X restore 29 85 pd sample-subpatch;
+#X text 201 85 <-- you can give the window a name as an argument;
+#N canvas 0 0 654 340 eager-adder 0;
+#X obj 62 73 inlet;
+#X obj 118 73 inlet;
+#X obj 62 188 outlet;
+#X obj 118 101 t b f;
+#X obj 62 156 +;
+#X text 197 23 this is a sample subpatch which maintains the sum of
+two inputs \, doing the computation when either input changes. IF it's
+the left input \, the "+" object takes care if it \; if the right \,
+the "trigger" object first gives the "+" the new value \, then "bangs"
+the right inlet to make "+" do the computation.;
+#X text 55 232 Aside: this shows why \, in Pd and Max \, objects such
+as "+" only trigger on their left inlets: it's easy to build up from
+there \, but if more than one inlet were "hot" \, you wouldn't be able
+to change both of them without firing the calculation twice.;
+#X text 197 112 Because of the two inlets and the one outlet \, the
+containing box (int eh parent patch) has two inlets and one outlet.
+They respect the left-to-right order of the inlet and outlet objects
+in the subpatch.;
+#X connect 0 0 4 0;
+#X connect 1 0 3 0;
+#X connect 3 0 4 0;
+#X connect 3 1 4 1;
+#X connect 4 0 2 0;
+#X restore 135 185 pd eager-adder;
+#X floatatom 135 158 0 0 0;
+#X floatatom 256 158 0 0 0;
+#X floatatom 135 213 0 0 0;
+#X text 26 235 There is also a facility for making many copies of a
+patch which track any changes you make in the original. The subpatches
+are called abstractions. For example \, here's a simple abstraction
+that sends a number to a "receive" on command:;
+#X obj 29 342 sendnumber 45 cookies;
+#X msg 226 314 bang;
+#X obj 226 341 sendnumber 67 pretzels;
+#X floatatom 519 341 0 0 0;
+#X text 27 553 note that "$1" \, etc \, has a different meaning in
+object boxes (open one of the "sendnumber" abstractions for comments.)
+;
+#X text 26 470 If you change one copy of an abstraction the change
+isn't automatically made on any other copies. You must keep track \,
+save the changes \, and cause Pd to reload the other copies (for example
+\, by closing and reopening the containing patch.);
+#X obj 432 314 r cookies;
+#X obj 519 314 r pretzels;
+#X text 31 107 If you click on the box (in run mode) the subwindow
+appears. Click on the one below to see how you give a subpatch inlets
+and outlets.;
+#X text 332 594 updated for Pd version 0.34;
+#X text 27 372 There is a separate file in this directory named "sendnumber.pd"
+which is loaded every time you type "sendnumber" in a box. Click on
+a "sendnumber" box above to see it. You can make changes in the subpatch
+and save them. The changes will be saved back to sendnumber.pd and
+not as part of this (containing) patch.;
+#X connect 0 0 10 0;
+#X connect 5 0 8 0;
+#X connect 6 0 5 0;
+#X connect 7 0 5 1;
+#X connect 11 0 12 0;
+#X connect 16 0 1 0;
+#X connect 17 0 13 0;
diff --git a/pd/doc/2.control.examples/13.locality.pd b/pd/doc/2.control.examples/13.locality.pd
new file mode 100644
index 00000000..6203ad98
--- /dev/null
+++ b/pd/doc/2.control.examples/13.locality.pd
@@ -0,0 +1,27 @@
+#N canvas 24 192 606 297 12;
+#X floatatom 38 223 0 0 0;
+#X floatatom 191 221 0 0 0;
+#X text 356 264 updated for Pd version 0.34;
+#X text 32 14 You can use dollarsigns in abstractions to get local
+sends and receives as shown here.;
+#X obj 29 85 dollarsign one;
+#X obj 167 86 dollarsign two;
+#X obj 38 196 r one-a;
+#X obj 191 194 r two-a;
+#X floatatom 110 225 0 0 0;
+#X floatatom 264 220 0 0 0;
+#X obj 110 197 r one-b;
+#X obj 264 191 r two-b;
+#X text 26 112 Open both copies to see what's happening...;
+#X floatatom 29 59 0 0 0;
+#X floatatom 124 58 0 0 0;
+#X floatatom 167 60 0 0 0;
+#X floatatom 264 58 0 0 0;
+#X connect 6 0 0 0;
+#X connect 7 0 1 0;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 13 0 4 0;
+#X connect 14 0 4 1;
+#X connect 15 0 5 0;
+#X connect 16 0 5 1;
diff --git a/pd/doc/2.control.examples/14.dollarsigns.pd b/pd/doc/2.control.examples/14.dollarsigns.pd
new file mode 100644
index 00000000..c4b6eee3
--- /dev/null
+++ b/pd/doc/2.control.examples/14.dollarsigns.pd
@@ -0,0 +1,5 @@
+#N canvas 12 363 561 155 12;
+#X text 303 114 updated for Pd version 0.34;
+#X obj 34 68 dollarsign2 three 4;
+#X text 32 14 An abstraction's creation arguments may be either numbers
+or symbols. Gory details are inside:;
diff --git a/pd/doc/2.control.examples/15.array.pd b/pd/doc/2.control.examples/15.array.pd
new file mode 100644
index 00000000..da054b1e
--- /dev/null
+++ b/pd/doc/2.control.examples/15.array.pd
@@ -0,0 +1,70 @@
+#N canvas 268 28 1030 744 12;
+#X text 204 19 ARRAYS;
+#N canvas 0 0 450 300 graph1 0;
+#X array array99 100 float 0;
+#X coords 0 1 99 -1 400 300 1;
+#X restore 614 49 graph;
+#X msg 179 325 \; array99 resize \$1;
+#X floatatom 179 292 0 0 0;
+#X floatatom 21 260 0 0 0;
+#X obj 21 294 / 100;
+#X msg 21 324 \; array99 const \$1;
+#X text 22 233 You can send messages to an array object:;
+#X msg 341 325 \; array99 print;
+#X text 64 262 <-- set to a constant value;
+#X text 221 291 resize;
+#X text 342 286 print size;
+#X text 22 487 read a text file;
+#X text 23 558 write a text file;
+#X text 271 559 write a WAV format soundfile;
+#X obj 104 714 tabread;
+#X obj 255 714 tabwrite;
+#X text 20 665 Objects are provided for reading and writing the contents
+of arrays via control messages:;
+#X obj 602 654 tabread4~;
+#X obj 602 679 tabwrite~;
+#X obj 695 654 tabreceive~;
+#X text 593 601 ...and audio signals:;
+#X obj 695 630 tabsend~;
+#X msg 381 400 \; array99 normalize;
+#X msg 382 442 \; array99 normalize 0.5;
+#X text 375 378 normalize to 1 or otherwise;
+#X obj 266 537 soundfiler;
+#X obj 812 631 tabosc4~;
+#X msg 19 402 \; array99 sinesum 64 0.2 0.2 0.2 0.2;
+#X msg 19 444 \; array99 cosinesum 64 0.2 0.2 0.2 0.2;
+#X text 23 378 Fourier synthesis (resizes table);
+#X text 257 484 read a soundfile;
+#X text 735 698 last updated for release 0.34;
+#X obj 175 715 tabread4;
+#X obj 602 628 tabread~;
+#X msg 267 511 read ../sound/voice2.wav array99;
+#X text 19 47 Arrays in Pd provide a unified way to deal with lists
+of numbers \, treating them as either audio samples or for "control"
+uses. To make one \, select "array" on the "new" menu. Dialogs appear
+to help you choose the name \, number of elements \, and various flags.
+;
+#X text 17 134 You can also change the array size using the "resize"
+message shown below. Arrays live in graphs and graphs may hold more
+than one array--however \, graphs containing more than one array won't
+know how to readjust themselves automatically when the arrays are resized.
+;
+#X msg 15 507 \; array99 read 15.file.txt;
+#X obj 26 581 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 26 600 savepanel;
+#X msg 26 623 \; array99 write \$1;
+#X obj 270 577 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 270 596 savepanel;
+#X obj 270 642 soundfiler;
+#X msg 270 619 write \$1 array99;
+#X connect 3 0 2 0;
+#X connect 4 0 5 0;
+#X connect 5 0 6 0;
+#X connect 35 0 26 0;
+#X connect 39 0 40 0;
+#X connect 40 0 41 0;
+#X connect 42 0 43 0;
+#X connect 43 0 45 0;
+#X connect 45 0 44 0;
diff --git a/pd/doc/2.control.examples/15.file.txt b/pd/doc/2.control.examples/15.file.txt
new file mode 100644
index 00000000..6fc963dd
--- /dev/null
+++ b/pd/doc/2.control.examples/15.file.txt
@@ -0,0 +1,2 @@
+0.3
+-0.6 -0.2 0.8 0
diff --git a/pd/doc/2.control.examples/16.more.arrays.pd b/pd/doc/2.control.examples/16.more.arrays.pd
new file mode 100644
index 00000000..cf9eacc1
--- /dev/null
+++ b/pd/doc/2.control.examples/16.more.arrays.pd
@@ -0,0 +1,23 @@
+#N canvas 19 83 830 601 12;
+#X graph graph1 0 -1 5 1 569 304 769 154;
+#X array array99 5 float;
+#X array array98 7 float;
+#X pop;
+#X text 135 18 MORE ON ARRAYS;
+#X msg 17 229 \; array99 rename george;
+#X msg 221 229 \; george rename array99;
+#X msg 317 166 \; array99 3 -0.5 0.5;
+#X text 17 207 renaming an array:;
+#X text 16 276 setting the bounds rectangle:;
+#X msg 18 296 \; array99 bounds 0 -2 10 2;
+#X msg 245 294 \; array99 bounds 0 -1 5 1;
+#X msg 19 395 \; array99 xticks 0 1 1;
+#X msg 212 394 \; array99 yticks 0 0.1 5;
+#X text 15 342 adding x and y labels: give a point to put a tick \, the interval between ticks \, and the number of ticks overall per large tick.;
+#X msg 15 472 \; array99 xlabel -1.1 0 1 2 3 4 5;
+#X text 12 436 adding labels. Give a y value and a bunch of x values or vice versa:;
+#X msg 17 166 \; array98 0 -1 1 -1 1 -1 1 -1 1 -1;
+#X msg 305 472 \; array99 ylabel 5.15 -1 0 1;
+#X text 556 575 last updated for release 0.33;
+#X text 10 39 Arrays have methods to set their values explicitly \; to set their "bounds" rectangles \, to rename them (but if you have two with the same name this won't necessarily do what you want) and to add markings. To set values by message \, send a list whise first element gives the index to start at. The second example sets two values starting at index three. Indices count up from zero.;
+#X text 11 522 You can also change x and y range and size in the "properties" dialog. Note that information about size and ranges is saved \, but ticks \, labels \, and the actual data are lost between Pd sessions.;
diff --git a/pd/doc/2.control.examples/17.PART3.midi.pd b/pd/doc/2.control.examples/17.PART3.midi.pd
new file mode 100644
index 00000000..b2467cd9
--- /dev/null
+++ b/pd/doc/2.control.examples/17.PART3.midi.pd
@@ -0,0 +1,35 @@
+#N canvas 47 52 517 445 12;
+#X floatatom 108 89 0 0 0;
+#X floatatom 72 89 0 0 0;
+#X obj 36 62 notein;
+#X floatatom 36 88 0 0 0;
+#X floatatom 228 91 0 0 0;
+#X floatatom 192 91 0 0 0;
+#X floatatom 156 90 0 0 0;
+#X obj 156 64 ctlin;
+#X floatatom 319 90 0 0 0;
+#X floatatom 283 89 0 0 0;
+#X obj 283 63 bendin;
+#X floatatom 329 128 0 0 0;
+#X floatatom 285 127 0 0 0;
+#X obj 285 157 bendout;
+#X text 23 18 Pd offers input and output objects for MIDI:;
+#X text 358 154 ... ad nauseam.;
+#X text 254 417 updated for Pd version 0.34;
+#X obj 39 321 midiout;
+#X obj 244 368 sysexin;
+#X msg 39 291 240 \, 45 \, 93 \, 3 \, 65 \, 1 \, 2 \, 3 \, 4 \, 247
+;
+#X text 32 252 You can format your own SYSEX messages as shown:;
+#X text 28 366 and receive SYSEX via:;
+#X connect 2 0 3 0;
+#X connect 2 1 1 0;
+#X connect 2 2 0 0;
+#X connect 7 0 6 0;
+#X connect 7 1 5 0;
+#X connect 7 2 4 0;
+#X connect 10 0 9 0;
+#X connect 10 1 8 0;
+#X connect 11 0 13 1;
+#X connect 12 0 13 0;
+#X connect 19 0 17 0;
diff --git a/pd/doc/2.control.examples/18.conditional.pd b/pd/doc/2.control.examples/18.conditional.pd
new file mode 100644
index 00000000..6bde3747
--- /dev/null
+++ b/pd/doc/2.control.examples/18.conditional.pd
@@ -0,0 +1,59 @@
+#N canvas 538 239 665 516 12;
+#X text 395 489 updated for Pd version 0.26;
+#X obj 87 148 select 1 2;
+#X floatatom 87 120 0 0 0;
+#X obj 87 214 print select-1;
+#X obj 119 194 print select-2;
+#X obj 152 171 print select-3;
+#X floatatom 313 122 0 0 0;
+#X obj 313 155 pack;
+#X obj 313 182 route 1 2;
+#X obj 353 131 t b f;
+#X floatatom 353 107 0 0 0;
+#X obj 371 210 unpack;
+#X floatatom 313 210 0 0 0;
+#X floatatom 342 210 0 0 0;
+#X floatatom 371 233 0 0 0;
+#X floatatom 409 234 0 0 0;
+#X text 30 20 Pd provides at least four objects for doing conditioonal
+computations. The "select" object tests its input against its argumt(s)
+\, and outputs "bang" when they match. The "route" object works similarly
+but also copies data. In other wors \, "route" takes a list \, tests
+its first element \, and conditionally passes on the rest of the list.
+;
+#X text 56 262 You also get "spigot" which turns a flow of messages
+on and off (like the Gate object in Max \, but with the inputs reversed):
+;
+#X floatatom 125 316 0 0 0;
+#X obj 125 341 spigot;
+#X floatatom 162 316 0 0 0;
+#X floatatom 125 365 0 0 0;
+#X text 192 317 <-- nonzero to open;
+#X text 157 365 if open \, messages coming in at left are sent to output.
+;
+#X text 55 396 And finally \, "moses" sends numbers to the left if
+they're less than the argument \, right otherwise:;
+#X floatatom 125 427 0 0 0;
+#X floatatom 125 476 0 0 0;
+#X obj 125 452 moses 5;
+#X floatatom 169 476 0 0 0;
+#X connect 1 0 3 0;
+#X connect 1 1 4 0;
+#X connect 1 2 5 0;
+#X connect 2 0 1 0;
+#X connect 6 0 7 0;
+#X connect 7 0 8 0;
+#X connect 8 0 12 0;
+#X connect 8 1 13 0;
+#X connect 8 2 11 0;
+#X connect 9 0 7 0;
+#X connect 9 1 7 1;
+#X connect 10 0 9 0;
+#X connect 11 0 14 0;
+#X connect 11 1 15 0;
+#X connect 18 0 19 0;
+#X connect 19 0 21 0;
+#X connect 20 0 19 1;
+#X connect 25 0 27 0;
+#X connect 27 0 26 0;
+#X connect 27 1 28 0;
diff --git a/pd/doc/2.control.examples/19.random.pd b/pd/doc/2.control.examples/19.random.pd
new file mode 100644
index 00000000..928be29f
--- /dev/null
+++ b/pd/doc/2.control.examples/19.random.pd
@@ -0,0 +1,39 @@
+#N canvas 47 52 722 449 12;
+#X text 460 422 updated for Pd version 0.26;
+#X text 35 28 Use the "random" object to make pseudo-random integers.
+To get continuously variable random numbers \, make a random number
+in a large range and divide:;
+#X obj 103 121 random 5;
+#X msg 103 95 bang;
+#X floatatom 103 147 0 0 0;
+#X text 137 147 outputs from 0 to 4;
+#X msg 337 87 bang;
+#X floatatom 336 165 0 0 0;
+#X obj 337 113 random 1000;
+#X obj 336 141 / 1000;
+#X text 402 166 from 0 to 0.999;
+#X obj 71 324 random 5;
+#X msg 162 255 bang;
+#X floatatom 71 350 0 0 0;
+#X obj 71 244 loadbang;
+#X obj 71 274 timer;
+#X text 204 255 <-- click to seed;
+#X msg 71 299 seed \$1;
+#X msg 163 299 bang;
+#X text 204 300 <-- click to get random numbers;
+#X text 24 382 If you give two randoms the same seed they give the
+same sequence. If you never seed them \, you'll get different sequences
+out of each one.;
+#X text 34 197 If you don't want the same behavior every time you run
+the patch \, use the time from load to first click as a seed:;
+#X connect 2 0 4 0;
+#X connect 3 0 2 0;
+#X connect 6 0 8 0;
+#X connect 8 0 9 0;
+#X connect 9 0 7 0;
+#X connect 11 0 13 0;
+#X connect 12 0 15 1;
+#X connect 14 0 15 0;
+#X connect 15 0 17 0;
+#X connect 17 0 11 0;
+#X connect 18 0 11 0;
diff --git a/pd/doc/2.control.examples/20.weighted-random.pd b/pd/doc/2.control.examples/20.weighted-random.pd
new file mode 100644
index 00000000..ed964a06
--- /dev/null
+++ b/pd/doc/2.control.examples/20.weighted-random.pd
@@ -0,0 +1,44 @@
+#N canvas 161 46 660 441 12;
+#X msg 103 95 bang;
+#X text 389 414 updated for Pd version 0.35;
+#X text 44 19 You can generate weighted random numbers from uniformly
+distributed ones. If you just want two possible outcomes with a varying
+probability for each one \, you can do as shown:;
+#X obj 103 121 random 100;
+#X obj 102 174 bng 20 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 169 174 bng 20 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X floatatom 205 148 3 0 100;
+#X text 250 148 <-- change probablilty;
+#X obj 103 149 moses 80;
+#X text 152 93 <-- click to test;
+#X text 61 219 This outputs a number at left 80% of the time \, otherwise
+at right \, unless you override the "80" using the number box. You
+may extend this to more than two possible outcomes \, for instance
+like this:;
+#X msg 106 305 bang;
+#X obj 106 331 random 100;
+#X obj 105 384 bng 20 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 195 387 bng 20 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X text 155 303 <-- click to test;
+#X obj 106 359 moses 10;
+#X obj 196 360 moses 30;
+#X obj 263 387 bng 20 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X text 103 409 10%;
+#X text 193 410 20%;
+#X text 265 409 70%;
+#X connect 0 0 3 0;
+#X connect 3 0 8 0;
+#X connect 6 0 8 1;
+#X connect 8 0 4 0;
+#X connect 8 1 5 0;
+#X connect 11 0 12 0;
+#X connect 12 0 16 0;
+#X connect 16 0 13 0;
+#X connect 16 1 17 0;
+#X connect 17 0 14 0;
+#X connect 17 1 18 0;
diff --git a/pd/doc/2.control.examples/21.markov.chain.pd b/pd/doc/2.control.examples/21.markov.chain.pd
new file mode 100644
index 00000000..36ca0db8
--- /dev/null
+++ b/pd/doc/2.control.examples/21.markov.chain.pd
@@ -0,0 +1,105 @@
+#N canvas 296 90 662 442 12;
+#X obj 84 251 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X obj 81 336 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X obj 162 335 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X obj 199 337 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 81 358 1;
+#X msg 162 360 2;
+#X msg 199 361 3;
+#X obj 81 386 s state;
+#X obj 66 173 bng 20 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X obj 105 164 r state;
+#X obj 83 225 sel 1 2 3;
+#X obj 255 253 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X obj 252 338 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X obj 334 340 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X obj 373 343 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 252 361 1;
+#X msg 329 366 2;
+#X msg 373 367 3;
+#X obj 252 394 s state;
+#X obj 419 254 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X obj 419 339 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X obj 499 338 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X obj 538 341 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 419 362 1;
+#X msg 499 363 2;
+#X msg 538 364 3;
+#X obj 418 395 s state;
+#X msg 236 186 \; state 1;
+#X obj 83 199 f 1;
+#X obj 84 279 random 100;
+#X obj 83 308 moses 30;
+#X obj 162 309 moses 60;
+#X obj 255 280 random 100;
+#X obj 255 310 moses 10;
+#X obj 334 311 moses 60;
+#X obj 419 281 random 100;
+#X obj 419 310 moses 70;
+#X obj 499 310 moses 80;
+#X floatatom 134 188 3 0 0;
+#X text 236 166 reset;
+#X text 49 152 STEP;
+#X text 34 20 Here is how to construct a simple \, three-valued Markov
+chain using "random." Each time you click on "step" the previous output
+("state") determines which of three random networks to invoke \, each
+having a different probability distribution for the next value of "state."
+For instance if the state was 3 \, the next state will be 1 70% of
+the time \, state 2 10% \, and state 3 20%.;
+#X text 408 422 updated for Pd version 0.35;
+#X connect 0 0 29 0;
+#X connect 1 0 4 0;
+#X connect 2 0 5 0;
+#X connect 3 0 6 0;
+#X connect 4 0 7 0;
+#X connect 5 0 7 0;
+#X connect 6 0 7 0;
+#X connect 8 0 28 0;
+#X connect 9 0 28 1;
+#X connect 9 0 38 0;
+#X connect 10 0 0 0;
+#X connect 10 1 11 0;
+#X connect 10 2 19 0;
+#X connect 11 0 32 0;
+#X connect 12 0 15 0;
+#X connect 13 0 16 0;
+#X connect 14 0 17 0;
+#X connect 15 0 18 0;
+#X connect 16 0 18 0;
+#X connect 17 0 18 0;
+#X connect 19 0 35 0;
+#X connect 20 0 23 0;
+#X connect 21 0 24 0;
+#X connect 22 0 25 0;
+#X connect 23 0 26 0;
+#X connect 24 0 26 0;
+#X connect 25 0 26 0;
+#X connect 28 0 10 0;
+#X connect 29 0 30 0;
+#X connect 30 0 1 0;
+#X connect 30 1 31 0;
+#X connect 31 0 2 0;
+#X connect 31 1 3 0;
+#X connect 32 0 33 0;
+#X connect 33 0 12 0;
+#X connect 33 1 34 0;
+#X connect 34 0 13 0;
+#X connect 34 1 14 0;
+#X connect 35 0 36 0;
+#X connect 36 0 20 0;
+#X connect 36 1 37 0;
+#X connect 37 0 21 0;
+#X connect 37 1 22 0;
diff --git a/pd/doc/2.control.examples/22.sequencing.pd b/pd/doc/2.control.examples/22.sequencing.pd
new file mode 100644
index 00000000..efddcb21
--- /dev/null
+++ b/pd/doc/2.control.examples/22.sequencing.pd
@@ -0,0 +1,20 @@
+#N canvas 47 52 679 466 12;
+#X text 465 442 updated for Pd version 0.26;
+#X text 35 28 You can use "qlist" or "textfile" objects for sequencing. Qlist is simpler to use than the (more versatile) textfile.;
+#X obj 355 146 r receive1;
+#X obj 441 146 r receive2;
+#X msg 205 88 clear \, add receive1 1 \, add 1000 receive1 0 \, add receive2 2 \, add 1000 receive2 0 \, add receive1 3 \, bang;
+#X obj 205 129 qlist;
+#X floatatom 355 172;
+#X floatatom 441 171;
+#X text 23 210 The "add" messages add lines to the qlist \, so that it contains:;
+#X text 155 238 receive1 1;
+#X text 121 259 1000 receive1 0;
+#X text 155 279 receive2 2;
+#X text 120 299 1000 receive2 0;
+#X text 155 317 receive1 3;
+#X text 17 342 and the "bang" instructs qlist to play the sequence by sending messages to "receive" objects. MEssages starting with numbers request that amount of delay.;
+#X text 16 391 If you have more than 5 lines or so wou will probably want to store them as a separate file and have qlist read it. You can also write files \, set tempo \, and single step... see the help patch for details.;
+#X connect 2 0 6 0;
+#X connect 3 0 7 0;
+#X connect 4 0 5 0;
diff --git a/pd/doc/2.control.examples/dollarsign.pd b/pd/doc/2.control.examples/dollarsign.pd
new file mode 100644
index 00000000..0697a570
--- /dev/null
+++ b/pd/doc/2.control.examples/dollarsign.pd
@@ -0,0 +1,35 @@
+#N canvas 62 73 586 361 12;
+#X obj 207 44 inlet;
+#X obj 207 71 s \$1-a;
+#X obj 302 72 s \$1-b;
+#X text 331 337 updated for Pd version 0.34;
+#X text 63 7 This is an abstraction used in example 12 \, "locality".
+;
+#X obj 302 47 inlet;
+#X obj 62 249 s \$1-c;
+#X obj 62 279 r \$1-c;
+#X floatatom 62 218 5 0 0;
+#X floatatom 62 307 5 0 0;
+#X obj 164 250 s here's-what-happens-if-you-dont;
+#X obj 163 280 r here's-what-happens-if-you-dont;
+#X floatatom 163 308 5 0 0;
+#X floatatom 164 220 5 0 0;
+#X floatatom 487 224 5 0 0;
+#X floatatom 488 307 5 0 0;
+#X obj 487 251 s \$0-d;
+#X obj 488 281 r \$0-d;
+#X text 47 94 The sends above get named "one-a" \, etc. The window
+title bar tells you the creation arguments for this particular instance.
+You can use this to make internal local connections as shown below.
+The "$1-c" boxes act locally whereas the middle boxes get crosstalk
+between the windows. The boxes at right also get unique names but in
+this case you don't have to secify "$0" \, it's just something unique.
+;
+#X connect 0 0 1 0;
+#X connect 5 0 2 0;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 11 0 12 0;
+#X connect 13 0 10 0;
+#X connect 14 0 16 0;
+#X connect 17 0 15 0;
diff --git a/pd/doc/2.control.examples/dollarsign2.pd b/pd/doc/2.control.examples/dollarsign2.pd
new file mode 100644
index 00000000..c3d149f6
--- /dev/null
+++ b/pd/doc/2.control.examples/dollarsign2.pd
@@ -0,0 +1,54 @@
+#N canvas 22 54 588 671 12;
+#X text 324 642 updated for Pd version 0.34;
+#X text 34 6 This is an abstraction used in example 13 \, "dollarsigns".
+;
+#X obj 88 107 send \$1;
+#X obj 199 106 + \$2;
+#X floatatom 303 88 0 0 0;
+#X obj 303 139 print;
+#X msg 303 113 blah \$1;
+#X text 36 163 This may sound inconsistant \, but it's not--object
+and message boxes are both actually messages \, but in the case of
+the Object box the message is passed at creation time \, and for the
+Message box \, at message time.;
+#X msg 188 272 bang;
+#X obj 188 300 symbol \$1;
+#X msg 98 272 bang;
+#X obj 98 300 float \$2;
+#X floatatom 98 327 5 0 0;
+#X symbolatom 188 329 10 0 0;
+#X text 36 233 So how do you put creation arguments in messages? Use
+"float" and "symbol" as shown:;
+#X msg 97 383 bang;
+#X obj 97 407 float \$2;
+#X msg 97 434 five \$1;
+#X text 41 357 Then if you wish \, connect to a message box as in:
+;
+#X obj 97 459 print;
+#X msg 143 512 bang;
+#X obj 143 564 symbol \$1;
+#X obj 237 560 f \$2;
+#X obj 143 540 t b b;
+#X obj 142 587 pack symbol float;
+#X msg 142 613 six \$1 \$2;
+#X obj 142 640 print;
+#X text 31 485 For messages combining more than one creation argument
+try:;
+#X text 37 50 In Object boxes \, dollar signs refer to the abstraction's
+creation arguments. In Messages \, they change dynamically:;
+#X connect 4 0 6 0;
+#X connect 6 0 5 0;
+#X connect 8 0 9 0;
+#X connect 9 0 13 0;
+#X connect 10 0 11 0;
+#X connect 11 0 12 0;
+#X connect 15 0 16 0;
+#X connect 16 0 17 0;
+#X connect 17 0 19 0;
+#X connect 20 0 23 0;
+#X connect 21 0 24 0;
+#X connect 22 0 24 1;
+#X connect 23 0 21 0;
+#X connect 23 1 22 0;
+#X connect 24 0 25 0;
+#X connect 25 0 26 0;
diff --git a/pd/doc/2.control.examples/sendnumber.pd b/pd/doc/2.control.examples/sendnumber.pd
new file mode 100644
index 00000000..00f2eb04
--- /dev/null
+++ b/pd/doc/2.control.examples/sendnumber.pd
@@ -0,0 +1,20 @@
+#N canvas 171 73 718 283 12;
+#X obj 34 60 inlet;
+#X obj 34 88 float \$1;
+#X obj 34 116 send \$2;
+#X text 26 225 For obvious reasons you might not want to call a patch
+as an abstraction from itself.;
+#X text 151 183 In this case \$1 is a number you can specify and \$2
+is a "send" destination.;
+#X text 461 260 updated for Pd version 0.26;
+#X text 154 103 When you call an abstraction by typing \, say \, "sendnumber
+1 x" in an object box. the subpatch can access the values of the creation
+arguments (1 and x) as "$1" and "$2" innside object boxes. Typing \$1
+inside a message box has a different meaning (see the message box help
+window.);
+#X text 155 31 This window is used by 11.subpatch.pd to demonstrate
+the abstraction mechanism in Pd. If you've opened this window directly
+\, you might also want to open the other one to see how it's used.
+;
+#X connect 0 0 1 0;
+#X connect 1 0 2 0;
diff --git a/pd/doc/3.audio.examples/00.INTRO.txt b/pd/doc/3.audio.examples/00.INTRO.txt
new file mode 100644
index 00000000..af9e3c96
--- /dev/null
+++ b/pd/doc/3.audio.examples/00.INTRO.txt
@@ -0,0 +1,43 @@
+This is the second of three tutorial series on Pd. The first one shows how to
+use Pd to do "control" computations. This one shows the time-domain audio
+processing features.
+
+Here is an approximate table of contents...
+
+1. sinusoids
+ oscillators
+ amplitudes
+ frequency and pitch
+ FM
+
+2. wavetable synthesis
+
+3. synthetic waveforms, classic and modern
+ pulses
+ rectangles and sawtooth waves
+ additive synthesis
+
+4. sampling
+
+5. envelopes
+ two flavors of ADSR envelope
+ log/linear conversion
+
+6. control structures
+ analog-style sequencing
+ monophonic keyboard synthesizer
+ sample and hold
+ envelope following
+
+7. processing
+ filters
+ modulation
+ delays
+
+8. formant synthesis
+
+9. strategies
+ quartic curves in envelopes
+ triggering notes from qlists
+ order of execution and block size
+
diff --git a/pd/doc/3.audio.examples/01.PART1.sinewave.pd b/pd/doc/3.audio.examples/01.PART1.sinewave.pd
new file mode 100644
index 00000000..091d7f6b
--- /dev/null
+++ b/pd/doc/3.audio.examples/01.PART1.sinewave.pd
@@ -0,0 +1,20 @@
+#N canvas 6 2 628 515 12;
+#X obj 108 109 osc~ 440;
+#X obj 108 168 dac~;
+#X text 176 110 <-- 440 Hz. sine wave at full blast;
+#X text 175 138 <-- reduce volume;
+#X obj 108 138 *~ 0.05;
+#X text 174 168 <-- send to the audio output device;
+#X text 202 3 MAKING A SINE WAVE;
+#X text 32 195 Audio computation can be turned on and off by sending messages to the global "pd" object as follows:;
+#X msg 98 239 \; pd dsp 1;
+#X msg 202 239 \; pd dsp 0;
+#X text 113 276 ON;
+#X text 222 276 OFF;
+#X text 29 297 You should see the Pd window change to reflect whether audio is on or off. You can also turn audio on and off using the "audio" menu \, but the buttons are provided as a shortcut.;
+#X text 30 368 When DSP is on \, you should hear a tone whose pitch is A 440 and whose amplitude is 0.05. If instead you are greeted with silence \, you might want to read the HTML documentation on setting up audio.;
+#X text 28 434 In general when you start a work session with Pd \, you will want to choose "test audio and MIDI" from the help window \, which opens a more comprehensive test patch than this one.;
+#X text 32 23 Audio computation in Pd is done using "tilde objects" such as the three below. THey use continuous audio streams to intercommunicate. They can be controlled by sending them messages. A few analysis modules take audio streams in and put control messages back out.;
+#X text 378 491 updated for Pd version 0.33;
+#X connect 0 0 4 0;
+#X connect 4 0 1 0;
diff --git a/pd/doc/3.audio.examples/02.amplitude.pd b/pd/doc/3.audio.examples/02.amplitude.pd
new file mode 100644
index 00000000..814d7d7c
--- /dev/null
+++ b/pd/doc/3.audio.examples/02.amplitude.pd
@@ -0,0 +1,37 @@
+#N canvas 73 190 702 512 12;
+#X obj 64 65 osc~ 440;
+#X obj 64 283 dac~;
+#X text 145 66 <-- 440 Hz. sine wave at full blast;
+#X msg 431 7 \; pd dsp 1;
+#X msg 514 7 \; pd dsp 0;
+#X text 456 45 ON;
+#X text 534 43 OFF;
+#X text 164 18 CONTROLLING AMPLITUDE;
+#X text 35 327 Amplitudes of audio signals can have any reasonable
+range \, but when you output a signal via the dac~ object \, the samples
+should range between -1 and +1. Values out of that range will be "clipped."
+;
+#X obj 64 202 *~ 0;
+#X floatatom 103 163 0 0 0;
+#X obj 91 130 dbtorms;
+#X floatatom 91 98 0 0 80;
+#X text 137 98 <-- set amplitude here in dB;
+#X text 211 133 <-- this converts dB to linear units;
+#X text 114 282 <-- and out. We'resending to both channels now.;
+#X text 210 164 <-- this shows the linear gain;
+#X text 116 204 <-- multiply the sine wave by the gain \, reducing
+its amplitude. You can also use the "*~" object to multiply two signals.
+The "0" argument here instructs it that we'll just send it messages
+to set the multiplier.;
+#X text 35 396 Here we calculate a gain for the multiplier (*~) using
+a "dbtorms" object (acronym for "dB to RMS"). 100 dB is normalized
+to one \, and zero dB artificially outputs a true 0;
+#X text 34 452 Pd assumes you have a two channel audio system unless
+you tell it otherwise.;
+#X text 440 486 updated for Pd version 0.33;
+#X connect 0 0 9 0;
+#X connect 9 0 1 0;
+#X connect 9 0 1 1;
+#X connect 11 0 9 1;
+#X connect 11 0 10 0;
+#X connect 12 0 11 0;
diff --git a/pd/doc/3.audio.examples/03.line.pd b/pd/doc/3.audio.examples/03.line.pd
new file mode 100644
index 00000000..535152f2
--- /dev/null
+++ b/pd/doc/3.audio.examples/03.line.pd
@@ -0,0 +1,50 @@
+#N canvas 30 68 683 481 12;
+#X obj 56 79 osc~ 440;
+#X obj 56 309 dac~;
+#X msg 446 79 \; pd dsp 1;
+#X msg 538 79 \; pd dsp 0;
+#X text 467 112 ON;
+#X text 555 112 OFF;
+#X obj 56 269 *~;
+#X obj 72 235 line~;
+#X text 129 235 <--- ramp generator;
+#X text 124 78 <-- sine wave;
+#X msg 72 103 0.1 2000;
+#X msg 72 169 0 2000;
+#X msg 72 125 0.1 50;
+#X msg 72 191 0 50;
+#X msg 72 147 0.1;
+#X msg 72 213 0;
+#X text 274 124 ON;
+#X text 154 105 <-- slow;
+#X text 144 126 <-- fast;
+#X text 111 146 <-- instantly;
+#X text 271 189 OFF;
+#X text 136 170 <-- slow;
+#X text 129 191 <-- fast;
+#X text 109 211 <-- instantly;
+#X text 135 159 ----------------------;
+#X text 93 268 <-- multiply the sine wave by the ramp. There's no longer
+a "0" argument;
+#X text 97 308 <-- out;
+#X text 103 7 CONTROLLING AMPLITUDE USING LINE~;
+#X text 38 342 Line~'s left inlet is a target value \; it reaches that
+target in the time specified (in milliseconds) to its right inlet.
+;
+#X text 39 396 The line~ object (and its control brother \, line) treat
+their right inlet specially. The inlets don't retain values the way
+other inlets do but revert to zero whenever a target is received.;
+#X text 14 27 In this patch \, the multiplier is configured to multiply
+two signals. The amplitude is now a signal computed by the line~ object.
+;
+#X text 415 457 updated for Pd version 0.33;
+#X connect 0 0 6 0;
+#X connect 6 0 1 0;
+#X connect 6 0 1 1;
+#X connect 7 0 6 1;
+#X connect 10 0 7 0;
+#X connect 11 0 7 0;
+#X connect 12 0 7 0;
+#X connect 13 0 7 0;
+#X connect 14 0 7 0;
+#X connect 15 0 7 0;
diff --git a/pd/doc/3.audio.examples/04.line2.pd b/pd/doc/3.audio.examples/04.line2.pd
new file mode 100644
index 00000000..c6dd1679
--- /dev/null
+++ b/pd/doc/3.audio.examples/04.line2.pd
@@ -0,0 +1,59 @@
+#N canvas 30 68 949 754 12;
+#X obj 67 77 osc~ 440;
+#X obj 67 329 dac~;
+#X obj 67 242 *~;
+#X obj 86 180 line~;
+#X text 116 330 <-- out;
+#X text 124 9 LINES GRAPHED;
+#X text 24 33 Here again is a line~ controlling the amplitude of an
+osc~ \, but with the outputs graphed:;
+#X obj 149 89 r graphit;
+#X obj 151 179 r graphit;
+#X obj 151 246 r graphit;
+#X obj 86 149 r to-line;
+#X graph graph1 0 -1.02 44100 1.02 631 480 831 350;
+#X array product 44100 float 0;
+#X pop;
+#X graph graph1 0 -1.02 44100 1.02 631 150 831 20;
+#X array osc-output 44100 float 0;
+#X pop;
+#X graph graph1 0 -1.02 44100 1.02 631 315 831 185;
+#X array line-output 44100 float 0;
+#X pop;
+#X obj 149 119 tabwrite~ osc-output;
+#X obj 67 299 *~ 0.1;
+#X msg 38 401 \; pd dsp 1 \; to-line 0 \, 1 500 \; graphit bang;
+#X msg 210 401 \; pd dsp 1 \; to-line 1 \, 0 500 \; graphit bang;
+#X obj 151 209 tabwrite~ line-output;
+#X obj 151 276 tabwrite~ product;
+#X text 70 379 ramp up;
+#X text 235 378 ramp down;
+#X text 406 376 to 1/2;
+#X msg 375 400 \; pd dsp 1 \; to-line 0.5 1000 \; graphit bang;
+#X text 634 491 ------ 1 second ------;
+#X text 38 485 Click the message boxes above to try it. Note that in
+the first two boxes \, the line~ objects get two messages. The first
+one \, with no time value \, causes the line~ to jump immediately to
+the value. The third box takes line~'s previous value as a point of
+departure. What you see will depend on which box you last clicked and
+how long you waited between the two.;
+#X text 662 727 updated for Pd version 0.33;
+#X text 41 600 On most machines \, you will hear an interruption in
+the sound one second after you click on the first or third box. This
+is because the graphical updates are likely to eat more CPU time than
+your audio buffer has pre-buffered for. You can avoid this if you keep
+your graphs in sub-windows and open them only when you need them. In
+some future version of Pd this behavior will be improved. Until then
+\, you'll have to avoid having arrays getting re-drawn during music
+performances.;
+#X connect 0 0 2 0;
+#X connect 0 0 14 0;
+#X connect 2 0 15 0;
+#X connect 2 0 19 0;
+#X connect 3 0 2 1;
+#X connect 3 0 18 0;
+#X connect 7 0 14 0;
+#X connect 8 0 18 0;
+#X connect 9 0 19 0;
+#X connect 10 0 3 0;
+#X connect 15 0 1 0;
diff --git a/pd/doc/3.audio.examples/05.output.subpatch.pd b/pd/doc/3.audio.examples/05.output.subpatch.pd
new file mode 100644
index 00000000..ab0dc724
--- /dev/null
+++ b/pd/doc/3.audio.examples/05.output.subpatch.pd
@@ -0,0 +1,97 @@
+#N canvas 68 39 635 486 12;
+#X floatatom 70 181 0 0 100;
+#N canvas 331 136 786 621 output 0;
+#X obj 455 510 t b;
+#X obj 455 450 f;
+#X obj 455 390 inlet;
+#X obj 455 540 f;
+#X msg 566 532 0;
+#X msg 455 420 bang;
+#X obj 455 480 moses 1;
+#X obj 566 502 t b f;
+#X obj 535 460 moses 1;
+#X obj 107 121 dbtorms;
+#X obj 535 430 r master-lvl;
+#X obj 107 28 r master-lvl;
+#X obj 455 570 s master-lvl;
+#X obj 36 228 inlet~;
+#X obj 250 258 inlet;
+#X obj 268 283 s master-lvl;
+#X msg 119 57 set \$1;
+#X obj 119 87 outlet;
+#X msg 250 309 \; pd dsp 1;
+#X obj 107 181 line~;
+#X obj 36 258 *~;
+#X obj 36 288 dac~;
+#X obj 107 151 pack 0 50;
+#X text 23 205 audio in;
+#X text 2 313 out both channels;
+#X text 273 182 Level input. Send to master-lvl and start DSP (we infer
+that if you're changing the level you want to hear the network.) If
+you start DSP when it's already running there's no effect.;
+#X text 59 542 here is the previous nonzero master-lvl -->;
+#X text 98 451 recall previous value of master-lvl -->;
+#X text 239 482 test if less than 1 -->;
+#X text 203 510 if true convert to bang -->;
+#X text 218 351 Mute control. If the master level is zero \, restore
+to the last nonzero one \, otherwise zero it.;
+#X text 182 86 <-- update the number box to show new level;
+#X text 178 120 <-- convert from dB to linear units;
+#X text 196 150 <-- make a smooth ramp to avoid clicks or zipper noise
+;
+#X text 333 318 <-- automatically start DSP;
+#X connect 0 0 3 0;
+#X connect 1 0 6 0;
+#X connect 2 0 5 0;
+#X connect 3 0 12 0;
+#X connect 4 0 12 0;
+#X connect 5 0 1 0;
+#X connect 6 0 0 0;
+#X connect 6 1 7 0;
+#X connect 7 0 4 0;
+#X connect 8 1 3 1;
+#X connect 9 0 22 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 16 0;
+#X connect 13 0 20 0;
+#X connect 14 0 15 0;
+#X connect 14 0 18 0;
+#X connect 16 0 17 0;
+#X connect 19 0 20 1;
+#X connect 20 0 21 0;
+#X connect 20 0 21 1;
+#X connect 22 0 19 0;
+#X restore 32 214 pd output;
+#X msg 108 182 MUTE;
+#X obj 32 27 osc~ 440;
+#X obj 54 55 osc~ 550;
+#X obj 54 116 osc~ 660;
+#X obj 32 88 +~;
+#X obj 32 142 +~;
+#X text 122 216 <-- this is a subwindow--click on it to see inside.
+;
+#X text 158 182 <-- output amplitude and mute control;
+#X text 383 463 updated for Pd version 0.34;
+#X text 24 408 The output control automatically starts DSP whenever
+you touch the level control. "MUTE" toggles between the current level
+and zero.;
+#X text 157 115 <-- Here we make a simple triad as a test signal.;
+#X text 261 20 CONTROLLING OUTPUT AMPLITUDE;
+#X text 25 263 In this and subsequent patches \, we'll use a subwindow
+\, "output" \, to control overall amplitude. The amplitudes are in
+decibels \, with 100 being full blast. In this example \, you can't
+actually push the output amplitude past 90 or so without clipping.
+You'll know you're clipping if \, instead of an A major chord \, you
+hear a single \, distorted tone two octaves down. The clipping happens
+at Pd's last stage of audio output. All audio signals internal to Pd
+have essentially no level limit.;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 3 0 6 0;
+#X connect 4 0 6 1;
+#X connect 5 0 7 1;
+#X connect 6 0 7 0;
+#X connect 7 0 1 0;
diff --git a/pd/doc/3.audio.examples/06.frequency.pd b/pd/doc/3.audio.examples/06.frequency.pd
new file mode 100644
index 00000000..5bc94801
--- /dev/null
+++ b/pd/doc/3.audio.examples/06.frequency.pd
@@ -0,0 +1,123 @@
+#N canvas 8 17 693 642 12;
+#X graph graph1 0 -1.02 4410 1.02 473 297 673 167;
+#X array osc-output 4410 float 0;
+#X pop;
+#X obj 128 259 tabwrite~ osc-output;
+#X floatatom 53 294 0 0 100;
+#N canvas 331 136 786 621 output 0;
+#X obj 455 510 t b;
+#X obj 455 450 f;
+#X obj 455 390 inlet;
+#X obj 455 540 f;
+#X msg 566 532 0;
+#X msg 455 420 bang;
+#X obj 455 480 moses 1;
+#X obj 566 502 t b f;
+#X obj 521 458 moses 1;
+#X obj 107 174 dbtorms;
+#X obj 521 428 r master-lvl;
+#X obj 107 56 r master-lvl;
+#X obj 455 570 s master-lvl;
+#X obj 36 228 inlet~;
+#X obj 251 229 inlet;
+#X obj 269 257 s master-lvl;
+#X msg 119 85 set \$1;
+#X obj 119 115 outlet;
+#X msg 251 283 \; pd dsp 1;
+#X obj 107 236 line~;
+#X obj 36 258 *~;
+#X obj 36 290 dac~;
+#X obj 107 204 pack 0 50;
+#X text 23 205 audio in;
+#X text 2 313 out both channels;
+#X text 246 157 Level input. Send to master-lvl and start DSP (we infer
+that if you're changing the level you want to hear the network.) If
+you start DSP when it's already running there's no effect.;
+#X text 59 542 here is the previous nonzero master-lvl -->;
+#X text 98 451 recall previous value of master-lvl -->;
+#X text 239 482 test if less than 1 -->;
+#X text 203 510 if true convert to bang -->;
+#X text 218 351 Mute control. If the master level is zero \, restore
+to the last nonzero one \, otherwise zero it.;
+#X text 182 114 <-- update the number box to show new level;
+#X connect 0 0 3 0;
+#X connect 1 0 6 0;
+#X connect 2 0 5 0;
+#X connect 3 0 12 0;
+#X connect 4 0 12 0;
+#X connect 5 0 1 0;
+#X connect 6 0 0 0;
+#X connect 6 1 7 0;
+#X connect 7 0 4 0;
+#X connect 8 1 3 1;
+#X connect 9 0 22 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 16 0;
+#X connect 13 0 20 0;
+#X connect 14 0 15 0;
+#X connect 14 0 18 0;
+#X connect 16 0 17 0;
+#X connect 19 0 20 1;
+#X connect 20 0 21 0;
+#X connect 20 0 21 1;
+#X connect 22 0 19 0;
+#X restore 15 327 pd output;
+#X msg 91 295 MUTE;
+#X msg 128 230 bang;
+#X floatatom 280 66 0 0 0;
+#X text 177 229 <-- click to graph;
+#X obj 15 206 r frequency;
+#X obj 15 232 osc~ 0;
+#X msg 280 37 set \$1;
+#X floatatom 6 66 0 0 0;
+#X obj 6 8 r frequency;
+#X msg 6 37 set \$1;
+#X obj 19 90 s frequency;
+#X obj 280 8 r pitch;
+#X obj 289 90 s pitch;
+#X obj 280 116 mtof;
+#X obj 280 145 s frequency;
+#X obj 6 145 s pitch;
+#X obj 6 116 ftom;
+#X text 105 66 <-- set frequency;
+#X text 372 65 <-- set MIDI pitch;
+#X text 15 429 Frequency and pitch are converted using the "ftom" and
+"mtof" objects. Frequency refers to the number of cycles per second.
+Pitch is "60" for Middle C \, 61 for C sharp \, 72 for the next C up
+\, and so on.;
+#X text 476 308 ---- 0.1 seconds ----;
+#X text 447 6 FREQUENCY AND PITCH;
+#X text 16 363 The osc~ object \, if you give it an argument \, expects
+floating-point messages to set its frequency. Without arguments \,
+its frequency is controlled by connecting an audio signal to its input.
+;
+#X text 14 496 Mtof and ftom work fine for microtones (non-integral
+"MIDI pitch" ) and don't have MIDI's range restriction-- for example
+\, MIDI -36 is about 1 Hz.;
+#X text 15 553 Note also the "set" messages going to the number boxes
+so that they can each update the other without bringing on an infinite
+loop. (get help on number boxes for details.);
+#X text 437 619 updated for Pd version 0.34;
+#X text 141 295 <-- output level;
+#X text 51 116 <-- convert frequency;
+#X text 106 134 to "MIDI" pitch;
+#X text 327 117 <-- convert "MIDI" pitch to frequency;
+#X connect 2 0 3 1;
+#X connect 3 0 2 0;
+#X connect 4 0 3 2;
+#X connect 5 0 1 0;
+#X connect 6 0 16 0;
+#X connect 6 0 17 0;
+#X connect 8 0 9 0;
+#X connect 9 0 1 0;
+#X connect 9 0 3 0;
+#X connect 10 0 6 0;
+#X connect 11 0 14 0;
+#X connect 11 0 20 0;
+#X connect 12 0 13 0;
+#X connect 13 0 11 0;
+#X connect 15 0 10 0;
+#X connect 17 0 18 0;
+#X connect 20 0 19 0;
diff --git a/pd/doc/3.audio.examples/07.frequency.mod.pd b/pd/doc/3.audio.examples/07.frequency.mod.pd
new file mode 100644
index 00000000..a7bab032
--- /dev/null
+++ b/pd/doc/3.audio.examples/07.frequency.mod.pd
@@ -0,0 +1,105 @@
+#N canvas 52 144 760 640 12;
+#X obj 256 180 *~;
+#X floatatom 256 95 0 0 0;
+#X floatatom 166 130 0 0 0;
+#X obj 166 200 +~;
+#X graph graph1 0 -1.02 440 1.02 527 170 727 40;
+#X array fm-output 441 float 0;
+#X pop;
+#X floatatom 204 300 0 0 100;
+#N canvas 159 26 516 274 output 1;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 396 182 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 391 110 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 391 85 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 20 182 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 104 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 90 outlet;
+#X msg 214 65 \; pd dsp 1;
+#X obj 83 198 line~;
+#X obj 20 207 *~;
+#X obj 20 232 dac~;
+#X obj 83 173 pack 0 50;
+#X text 20 159 audio;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 21 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X restore 166 328 pd output;
+#X msg 242 301 MUTE;
+#X text 283 301 <-- output amplitude;
+#X msg 242 248 bang;
+#X text 284 248 <-- click to graph;
+#X obj 242 272 tabwrite~ fm-output;
+#X floatatom 278 150 0 0 0;
+#X text 163 87 carrier;
+#X text 162 105 frequency;
+#X text 241 71 frequency;
+#X text 242 54 modulation;
+#X text 33 8 FREQUENCY MODULATION ("FM") USING TWO OSCILLATORS;
+#X obj 166 252 osc~;
+#X text 50 234 "carrier";
+#X text 32 252 oscillator -->;
+#X text 40 161 add modulator;
+#X text 39 179 to carrier;
+#X text 37 198 frequency -->;
+#X text 317 162 index;
+#X text 319 143 modulation;
+#X text 477 601 updated for Pd version 0.34;
+#X obj 256 120 osc~;
+#X text 52 363 This shows the classical FM synthesis technique developed
+by John Chowning. It's nothing but an oscillator with vibrato controlled
+by another "modulation" oscillator. First \, to understand the patch
+\, set carrier frequency to 400 or so \, modulation frequency between
+5 and 10 \, and try modulation index values between 0 and 400 \, say.
+You'll hear a sine wave with vibrato.;
+#X text 531 172 --- 0.01 seconds ----;
+#X text 51 478 To get the FM sound \, set all three of carrier frequency
+\, modulation frequency \, and modulation index in the hundreds. Note
+that you get a timbral change as you sweep modulation index \, because
+this changes the amplitudes of the components of the output sound but
+not their frequencies.;
+#X text 48 564 The component frequencies are equal to the carrier frequency
+\, plus or minus multiples of the modulator frequency.;
+#X connect 0 0 3 1;
+#X connect 1 0 27 0;
+#X connect 2 0 3 0;
+#X connect 3 0 18 0;
+#X connect 5 0 6 1;
+#X connect 6 0 5 0;
+#X connect 7 0 6 2;
+#X connect 9 0 11 0;
+#X connect 12 0 0 1;
+#X connect 18 0 11 0;
+#X connect 18 0 6 0;
+#X connect 27 0 0 0;
diff --git a/pd/doc/3.audio.examples/08.phase.mod.pd b/pd/doc/3.audio.examples/08.phase.mod.pd
new file mode 100644
index 00000000..716ba6ab
--- /dev/null
+++ b/pd/doc/3.audio.examples/08.phase.mod.pd
@@ -0,0 +1,246 @@
+#N canvas 36 68 722 738 12;
+#X obj 216 145 *~;
+#X floatatom 216 88 0 0 0;
+#X obj 297 125 line~;
+#X floatatom 128 108 0 0 0;
+#X obj 128 222 cos~;
+#X obj 128 178 +~;
+#X floatatom 166 299 0 0 100;
+#N canvas 159 26 495 270 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 182 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 159 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 128 326 pd output;
+#X msg 204 299 MUTE;
+#X obj 216 113 osc~ 0;
+#X obj 297 99 pack 0 50;
+#X floatatom 297 46 0 0 0;
+#X obj 297 73 / 100;
+#X text 271 8 modulation index;
+#X text 271 23 in hundredths;
+#X text 125 65 carrier;
+#X text 124 83 frequency;
+#X text 201 64 frequency;
+#X text 202 47 modulation;
+#X text 33 119 carrier;
+#X text 33 134 phase -->;
+#X text 6 162 phase;
+#X text 5 177 modulation-->;
+#X text 12 204 output;
+#X text 11 221 waveform -->;
+#X text 527 6 PHASE MODULATION;
+#X text 417 703 updated for Pd version 0.34;
+#X text 13 377 Most implementations of "FM" actually use phase \, not
+frequency \, modulation \, because it extends in a more natural way
+to "multi-operator FM" with three or more oscillators.;
+#X text 15 437 To do phase modulation \, we split the "carrier oscillator"
+into its phase calculation (phasor~) and its waveform lookup (cos~).
+These together would be equivalent to an osc~ object \, but the "+~"
+between them adds the modulating oscillator's output to the phase.
+;
+#X text 18 587 The units of the "modulation" index change--it is now
+dimensionless and relative to the modulation frequency \, and "good"
+values tend to be between 0 and 1 In this patch it's in hundredths.
+;
+#X text 19 658 We also have to use a line~ to smooth changes in the
+modulation index \, which wasn't necessary in the previous patch.;
+#X obj 128 135 phasor~;
+#X obj 117 557 cos~;
+#X obj 117 529 phasor~;
+#X text 60 539 this:;
+#X text 219 532 is the same;
+#X text 220 551 as this:;
+#X obj 335 544 osc~;
+#X graph graph2 0 -1 440 1 509 330 709 190;
+#X array phase-out 441 float 1;
+#A 0 0.43245 0.433463 0.434452 0.435418 0.43636 0.43728 0.438178 0.439056
+0.439912 0.440749 0.441567 0.442366 0.443148 0.443912 0.444659 0.445391
+0.446107 0.446809 0.447497 0.448172 0.448834 0.449484 0.450122 0.450751
+0.451369 0.451978 0.452579 0.453172 0.453758 0.454338 0.454911 0.45548
+0.456045 0.456606 0.457164 0.457719 0.458274 0.458827 0.45938 0.459934
+0.460489 0.461046 0.461605 0.462168 0.462735 0.463306 0.463883 0.464466
+0.465056 0.465654 0.466259 0.466873 0.467497 0.468131 0.468776 0.469433
+0.470102 0.470783 0.471479 0.472188 0.472913 0.473653 0.474409 0.475182
+0.475973 0.476782 0.477611 0.478459 0.479327 0.480216 0.481127 0.48206
+0.483015 0.483994 0.484997 0.486024 0.487077 0.488156 0.48926 0.490392
+0.491552 0.49274 0.493957 0.495203 0.496479 0.497785 0.499123 0.500493
+0.501896 0.503332 0.504801 0.506304 0.507842 0.509415 0.511023 0.512667
+0.514348 0.516066 0.517821 0.519615 0.521447 0.523318 0.525228 0.527179
+0.52917 0.531202 0.533275 0.53539 0.537547 0.539747 0.541992 0.54428
+0.546613 0.548989 0.551411 0.553877 0.556389 0.558947 0.561551 0.564202
+0.5669 0.569645 0.572437 0.575278 0.578166 0.581104 0.58409 0.587125
+0.59021 0.593345 0.596529 0.599764 0.603051 0.606389 0.609778 0.613219
+0.61671 0.620254 0.62385 0.627497 0.631197 0.634949 0.638754 0.642612
+0.646522 0.650486 0.654503 0.658573 0.662696 0.666873 0.671104 -0.324611
+-0.320273 -0.315881 -0.311434 -0.306931 -0.302375 -0.297764 -0.2931
+-0.288381 -0.283608 -0.278781 -0.2739 -0.268964 -0.263975 -0.258931
+-0.253833 -0.248682 -0.243476 -0.238217 -0.232904 -0.227537 -0.222116
+-0.216642 -0.211115 -0.205534 -0.1999 -0.194211 -0.18847 -0.182675
+-0.176829 -0.170929 -0.164978 -0.158975 -0.152919 -0.146813 -0.140655
+-0.134446 -0.128186 -0.121875 -0.115514 -0.109103 -0.102642 -0.0961313
+-0.0895714 -0.0829625 -0.0763049 -0.0695988 -0.0628447 -0.056041 -0.0491896
+-0.0422913 -0.0353461 -0.0283546 -0.0213171 -0.0142339 -0.00710538
+6.80089e-05 0.00728586 0.0145478 0.0218535 0.0292025 0.0365944 0.0440286
+0.0515049 0.0590228 0.0665818 0.0741815 0.0818213 0.0895009 0.0972198
+0.104978 0.112776 0.120611 0.128484 0.136393 0.144339 0.15232 0.160337
+0.168388 0.176473 0.184592 0.192744 0.200929 0.209146 0.217393 0.225672
+0.233981 0.24232 0.250688 0.259084 0.267509 0.27596 0.284439 0.292944
+0.301475 0.310031 0.318611 0.327215 0.335842 0.344491 0.353162 0.361854
+0.370566 0.379298 0.38805 0.39682 0.405608 0.414413 0.423234 0.432072
+0.440924 0.449792 0.458673 0.467567 0.476474 0.485394 0.494324 0.503265
+0.512216 0.521176 0.530145 0.539121 0.548104 0.557094 0.566089 0.575089
+0.584094 0.593102 0.602113 0.611126 0.620141 0.629157 0.638173 0.647189
+0.656203 0.665215 0.674225 0.683232 0.692234 0.701231 0.710224 0.71921
+0.728189 0.737161 0.746124 0.755079 0.764024 0.772959 0.781884 0.790796
+0.799697 0.808584 0.817458 0.826318 0.835162 0.843992 0.852804 0.861601
+0.870379 0.879139 0.887879 0.8966 0.905301 0.913981 0.92264 0.931276
+0.93989 0.94848 0.957046 0.965588 0.974105 0.982595 0.99106 0.999497
+1.00791 1.01629 1.02464 1.03296 1.04126 1.04952 1.05775 1.06595 1.07412
+1.08225 1.09035 1.09841 1.10645 1.11444 1.1224 1.13033 1.13822 1.14607
+1.15388 1.16166 1.1694 1.17709 1.18475 1.19237 1.19995 1.20749 1.21498
+1.22244 1.22985 1.23722 1.24454 1.25182 1.25906 1.26625 1.2734 0.280502
+0.287559 0.29457 0.301536 0.308454 0.315325 0.32215 0.328926 0.335655
+0.342335 0.348967 0.35555 0.362084 0.368568 0.375003 0.381388 0.387722
+0.394004 0.400235 0.406415 0.412544 0.418621 0.424647 0.430621 0.436542
+0.442412 0.448228 0.453993 0.459704 0.465363 0.470969 0.476521 0.48202
+0.487466 0.492858 0.498196 0.503481 0.508712 0.513889 0.519011 0.524077
+0.52909 0.534049 0.538953 0.543804 0.5486 0.553342 0.55803 0.562664
+0.567243 0.571769 0.57624 0.580658 0.585021 0.589331 0.593587 0.597789
+0.601938 0.606033 0.610075 0.614064 0.617999 0.621879 0.625706 0.629481
+0.633203 0.636873 0.640491 0.644057 0.647571 0.651033 0.654445 0.657805
+0.661114 0.664372 0.66758 0.670739 0.673847 0.676905 0.679914 0.682875
+;
+#X array cos-out 441 float 1;
+#A 0 -0.911256 -0.913872 -0.916365 -0.918789 -0.921097 -0.923342 -0.925486
+-0.927564 -0.92956 -0.931483 -0.93335 -0.935129 -0.936867 -0.938528
+-0.940137 -0.941707 -0.943197 -0.944657 -0.946072 -0.947426 -0.948755
+-0.95004 -0.951276 -0.952491 -0.953672 -0.954806 -0.955924 -0.957024
+-0.958071 -0.959106 -0.960132 -0.961119 -0.962086 -0.963047 -0.963993
+-0.964903 -0.965811 -0.966718 -0.967595 -0.968461 -0.969329 -0.970192
+-0.971025 -0.971863 -0.972707 -0.973527 -0.974343 -0.975168 -0.975986
+-0.976786 -0.977597 -0.978414 -0.979202 -0.980003 -0.980816 -0.981596
+-0.982391 -0.983194 -0.983968 -0.984757 -0.985543 -0.98631 -0.987093
+-0.987851 -0.98861 -0.98937 -0.990102 -0.990852 -0.991557 -0.992275
+-0.99296 -0.993642 -0.994295 -0.994935 -0.995544 -0.996137 -0.996687
+-0.997227 -0.997705 -0.998173 -0.998575 -0.998944 -0.999273 -0.999527
+-0.999743 -0.999894 -0.999966 -0.999981 -0.999927 -0.999765 -0.999526
+-0.999202 -0.998785 -0.99824 -0.997586 -0.996816 -0.995923 -0.994897
+-0.99373 -0.992413 -0.990934 -0.989283 -0.987458 -0.985449 -0.983247
+-0.980843 -0.978222 -0.975372 -0.972288 -0.968961 -0.965377 -0.96153
+-0.95741 -0.952995 -0.948265 -0.943231 -0.937881 -0.932182 -0.926128
+-0.919728 -0.912938 -0.905762 -0.898197 -0.890198 -0.881799 -0.87293
+-0.863637 -0.853855 -0.843612 -0.832873 -0.821629 -0.809886 -0.797593
+-0.784764 -0.771394 -0.757466 -0.742953 -0.727864 -0.712189 -0.695918
+-0.679041 -0.661549 -0.643437 -0.624697 -0.605324 -0.585314 -0.564664
+-0.543374 -0.521441 -0.498869 -0.475658 -0.451811 -0.427334 -0.402219
+-0.376482 -0.350131 -0.323174 -0.295626 -0.267508 -0.238824 -0.2096
+-0.179853 -0.149603 -0.118875 -0.0876953 -0.0560885 -0.0240874 0.00827867
+0.040974 0.0739643 0.107208 0.140666 0.174297 0.208056 0.241893 0.275757
+0.309603 0.343385 0.377036 0.410498 0.443712 0.476616 0.509148 0.541242
+0.572835 0.603858 0.634244 0.66391 0.692797 0.720837 0.747961 0.774084
+0.799131 0.823051 0.845758 0.867169 0.887248 0.90588 0.923037 0.938628
+0.952605 0.964885 0.975433 0.984153 0.991037 0.995988 0.998986 0.999997
+0.998938 0.995807 0.990576 0.983211 0.973668 0.961961 0.948075 0.932007
+0.913757 0.893328 0.87073 0.845995 0.81915 0.790227 0.759268 0.726324
+0.691452 0.654714 0.616181 0.57593 0.534037 0.490599 0.445716 0.399491
+0.352033 0.303459 0.253886 0.203441 0.152259 0.100478 0.0482368 -0.00432081
+-0.057045 -0.109786 -0.162386 -0.214696 -0.266564 -0.317814 -0.368301
+-0.417864 -0.466337 -0.513584 -0.559424 -0.603732 -0.646344 -0.687125
+-0.725934 -0.762631 -0.797101 -0.829205 -0.858847 -0.8859 -0.91028
+-0.931885 -0.950636 -0.966466 -0.97929 -0.989092 -0.995773 -0.999358
+-0.999773 -0.997042 -0.991152 -0.982099 -0.969941 -0.954655 -0.936332
+-0.915008 -0.890737 -0.863625 -0.833713 -0.801132 -0.765979 -0.728348
+-0.688394 -0.646219 -0.601975 -0.555817 -0.507869 -0.45832 -0.407319
+-0.355036 -0.301652 -0.247328 -0.192258 -0.136617 -0.0805873 -0.0243537
+0.0319027 0.0879985 0.143752 0.198986 0.253526 0.307194 0.359834 0.411262
+0.461345 0.509907 0.556823 0.601935 0.645131 0.686266 0.725245 0.761935
+0.796272 0.828125 0.857461 0.884158 0.9082 0.929508 0.948044 0.9638
+0.976698 0.986778 0.99402 0.998404 0.999981 0.998763 0.994751 0.988022
+0.978621 0.966582 0.95197 0.93487 0.915357 0.893512 0.869405 0.843139
+0.814817 0.784538 0.752408 0.718535 0.68303 0.646007 0.607581 0.567872
+0.526996 0.485074 0.442227 0.398573 0.354231 0.30932 0.263957 0.218256
+0.172326 0.126282 0.0802317 0.0342922 -0.0114471 -0.0568861 -0.101931
+-0.146492 -0.190474 -0.233802 -0.276394 -0.318171 -0.359072 -0.399015
+-0.437958 -0.475821 -0.512573 -0.548149 -0.582512 -0.615632 -0.647446
+-0.677951 -0.707119 -0.734897 -0.761296 -0.786293 -0.80988 -0.83204
+-0.852773 -0.872085 -0.88998 -0.906462 -0.921542 -0.935229 -0.94754
+-0.95849 -0.968102 -0.976397 -0.9834 -0.989136 -0.993613 -0.996882
+-0.998976 -0.99993 -0.999748 -0.998484 -0.996188 -0.99286 -0.988563
+-0.983336 -0.977186 -0.970195 -0.962347 -0.953732 -0.944344 -0.934249
+-0.923482 -0.912051 -0.900028 -0.887441 -0.874296 -0.860659 -0.846562
+-0.832035 -0.817101 -0.801792 -0.786149 -0.770201 -0.753977 -0.737507
+-0.720826 -0.703952 -0.686912 -0.669731 -0.652436 -0.635043 -0.617572
+-0.600055 -0.582513 -0.564966 -0.547418 -0.529898 -0.512429 -0.495016
+-0.477676 -0.460438 -0.443287 -0.426265 -0.409364;
+#X pop;
+#X obj 251 241 tabwrite~ phase-out;
+#X obj 251 268 tabwrite~ cos-out;
+#X msg 251 216 bang;
+#X text 298 215 <-- graph them;
+#X text 386 99 <-- change smoothly to avoid clicks;
+#X connect 0 0 5 1;
+#X connect 1 0 9 0;
+#X connect 2 0 0 1;
+#X connect 3 0 31 0;
+#X connect 4 0 7 0;
+#X connect 4 0 40 0;
+#X connect 5 0 4 0;
+#X connect 5 0 39 0;
+#X connect 6 0 7 1;
+#X connect 7 0 6 0;
+#X connect 8 0 7 2;
+#X connect 9 0 0 0;
+#X connect 10 0 2 0;
+#X connect 11 0 12 0;
+#X connect 12 0 10 0;
+#X connect 31 0 5 0;
+#X connect 33 0 32 0;
+#X connect 41 0 39 0;
+#X connect 41 0 40 0;
diff --git a/pd/doc/3.audio.examples/09.review.pd b/pd/doc/3.audio.examples/09.review.pd
new file mode 100644
index 00000000..4e683e3c
--- /dev/null
+++ b/pd/doc/3.audio.examples/09.review.pd
@@ -0,0 +1,43 @@
+#N canvas 36 68 701 588 12;
+#X text 444 567 updated for Pd version 0.34;
+#X text 39 14 PART 1 REVIEW;
+#X obj 66 131 tabwrite~;
+#X obj 66 105 line~;
+#X obj 54 298 +;
+#X obj 66 79 +~;
+#X obj 66 209 cos~;
+#X obj 66 157 osc~;
+#X obj 66 183 phasor~;
+#X obj 54 324 pack;
+#X obj 52 536 r;
+#X obj 53 512 s;
+#X obj 54 433 inlet;
+#X obj 53 487 f;
+#X obj 53 461 t;
+#X obj 54 350 moses;
+#X obj 54 403 dbtorms;
+#X obj 97 376 mtof;
+#X obj 54 375 ftom;
+#X obj 105 433 outlet;
+#X obj 66 235 dac~;
+#X text 26 52 So far we've seen these audio ("tilde") objects:;
+#X text 123 104 -- ramp generator;
+#X text 157 131 -- sampler (which we've only used for graphing so far)
+;
+#X text 111 157 -- a cosine wave oscillator;
+#X text 139 183 -- phase generator for making your own oscillator;
+#X text 112 209 -- cosine waveshape lookup;
+#X text 112 236 -- audio output ("digital/analog converter" -- a misnomer)
+;
+#X text 31 266 ... and these "control" objects:;
+#X text 145 374 -- frequency to pitch conversion;
+#X text 126 403 -- decibel to amplitude conversion;
+#X text 167 434 -- input and output to a subpatch;
+#X text 90 462 ("trigger") -- message ordering and conversion;
+#X text 93 487 ("float") -- store a (floating point) number;
+#X text 90 513 ("send") -- wireless message sending;
+#X text 91 538 ("receive") ... and receiving;
+#X text 106 78 (etc.) -- arithmetic on audio signals;
+#X text 92 296 (etc.) -- arithmetic;
+#X text 99 323 -- combine two or more values in a single message;
+#X text 107 350 -- arithmetic "if";
diff --git a/pd/doc/3.audio.examples/10.PART2.wavetables.pd b/pd/doc/3.audio.examples/10.PART2.wavetables.pd
new file mode 100644
index 00000000..925988ad
--- /dev/null
+++ b/pd/doc/3.audio.examples/10.PART2.wavetables.pd
@@ -0,0 +1,104 @@
+#N canvas 19 22 722 608 12;
+#X floatatom 164 43 0 0 0;
+#X graph graph1 0 -1.02 258 1.02 445 177 703 47;
+#X array table10 259 float 1;
+#A 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0.612 0.612 0.612 0.612 0.612 0.627692 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 -0.470769 -0.470769 -0.470769 -0.470769 -0.470769
+-0.470769 -0.470769 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0.627692 0.627692 0.627692 0.643385 0.643385 0.643385
+0.659077 0 -0.502154 -0.502154 -0.502154 -0.486462 -0.486462 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0.580615 0.596308 0.596308 0.596308 0.596308
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
+#X pop;
+#X floatatom 202 171 0 0 100;
+#N canvas 159 26 532 285 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 426 180 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 397 110 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 85 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 20 182 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 105 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 90 outlet;
+#X msg 214 65 \; pd dsp 1;
+#X obj 83 198 line~;
+#X obj 20 207 *~;
+#X obj 20 232 dac~;
+#X obj 83 173 pack 0 50;
+#X text 20 159 audio;
+#X text 96 114 show level;
+#X obj 426 155 t b;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 26 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 21 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 5 0;
+#X restore 164 199 pd output;
+#X msg 240 172 MUTE;
+#X text 30 123 oscillator -->;
+#X text 456 587 updated for Pd version 0.34;
+#X text 33 8 WAVETABLE OSCILLATORS;
+#X text 36 106 wavetable;
+#X obj 164 70 mtof;
+#X floatatom 164 97 0 0 0;
+#X obj 164 123 tabosc4~ table10;
+#X text 94 42 pitch->;
+#X text 35 309 Note that I selected "save contents" in the properties
+dialog for table10 (right click on the table to see.) If this isn't
+set \, the waveform won't be remembered as part of the patch but will
+be reinitialized to zero when the patch is reopened.;
+#X msg 35 549 \; table10 cosinesum 256 0.2 -0.2 0.2 -0.2 0.2 -0.2 0.2
+;
+#X msg 578 240 \; table10 const 0;
+#X text 597 217 CLEAR TABLE;
+#X text 35 395 For efficiency's sake tabosc4~ requires that the table
+have a power of two plus three points (64+3=67 \, 128+3=131 \, 256+3=259
+\, etc.) If you want wraparound to work smoothly \, you should make
+the last three points copies of the first three. This is done because
+tabread4~ does 4-point interpolation.;
+#X text 38 494 If you want a specific sinusoidal composition \, you
+can send table10 a message \, as below (see 11.arrays in the control
+examples):;
+#X text 36 240 Here \, in place of the "osc~" cosine wave oscillator
+\, we introduce the tabosc4~ oscillator which produces an arbitrary
+waveform. You can draw in the waveform with the mouse.;
+#X connect 0 0 9 0;
+#X connect 2 0 3 1;
+#X connect 3 0 2 0;
+#X connect 4 0 3 2;
+#X connect 9 0 10 0;
+#X connect 10 0 11 0;
+#X connect 11 0 3 0;
diff --git a/pd/doc/3.audio.examples/11.wavetable.FM.pd b/pd/doc/3.audio.examples/11.wavetable.FM.pd
new file mode 100644
index 00000000..c4cc6d60
--- /dev/null
+++ b/pd/doc/3.audio.examples/11.wavetable.FM.pd
@@ -0,0 +1,147 @@
+#N canvas 74 98 749 466 12;
+#X graph graph1 0 -1.02 258 1.02 475 298 733 168;
+#X array waveform11 259 float 1;
+#A 0 -0.0896033 0 0.0896033 0.178356 0.265425 0.350007 0.431348 0.508756
+0.58161 0.649372 0.711597 0.767935 0.818137 0.862053 0.89963 0.930912
+0.956028 0.975187 0.988669 0.996811 1 0.998655 0.993223 0.984158 0.971919
+0.956953 0.939691 0.920538 0.899867 0.878018 0.85529 0.831945 0.808204
+0.784252 0.760239 0.736284 0.712477 0.688888 0.665568 0.642553 0.619872
+0.59755 0.575607 0.554066 0.532953 0.512296 0.49213 0.472491 0.453419
+0.434957 0.417147 0.400027 0.383632 0.367992 0.353126 0.339046 0.32575
+0.313227 0.301453 0.290394 0.280002 0.270224 0.260995 0.252248 0.24391
+0.235908 0.22817 0.220628 0.213219 0.205888 0.198586 0.191278 0.183936
+0.176545 0.169098 0.1616 0.154063 0.146505 0.138954 0.131437 0.123987
+0.116636 0.109415 0.102354 0.0954784 0.0888083 0.08236 0.0761442 0.0701659
+0.0644253 0.0589178 0.0536354 0.0485669 0.0436994 0.0390194 0.0345135
+0.0301695 0.0259776 0.0219306 0.0180245 0.0142591 0.0106377 0.00716724
+0.00385775 0.000722025 -0.00222511 -0.0049675 -0.00748845 -0.00977153
+-0.0118014 -0.0135644 -0.0150493 -0.0162479 -0.0171551 -0.0177693 -0.0180928
+-0.0181312 -0.0178936 -0.017392 -0.0166417 -0.0156601 -0.0144666 -0.0130822
+-0.0115294 -0.00983114 -0.0080113 -0.00609396 -0.0041034 -0.00206402
+-2.23572e-07 0.00206358 0.00410297 0.00609353 0.00801089 0.00983075
+0.011529 0.0130819 0.0144663 0.0156599 0.0166416 0.0173919 0.0178935
+0.0181312 0.0180929 0.0177695 0.0171552 0.0162481 0.0150496 0.0135647
+0.0118018 0.009772 0.00748897 0.00496807 0.00222573 -0.000721367 -0.00385706
+-0.00716651 -0.010637 -0.0142583 -0.0180237 -0.0219297 -0.0259767 -0.0301686
+-0.0345125 -0.0390184 -0.0436984 -0.0485658 -0.0536343 -0.0589167 -0.0644241
+-0.0701647 -0.0761429 -0.0823587 -0.0888069 -0.0954769 -0.102353 -0.109414
+-0.116634 -0.123985 -0.131435 -0.138952 -0.146504 -0.154061 -0.161598
+-0.169097 -0.176543 -0.183935 -0.191276 -0.198584 -0.205886 -0.213218
+-0.220627 -0.228169 -0.235906 -0.243908 -0.252246 -0.260993 -0.270222
+-0.28 -0.290392 -0.301451 -0.313224 -0.325747 -0.339043 -0.353123 -0.367989
+-0.383629 -0.400023 -0.417143 -0.434954 -0.453415 -0.472486 -0.492125
+-0.512292 -0.532948 -0.554062 -0.575602 -0.597545 -0.619868 -0.642548
+-0.665563 -0.688883 -0.712472 -0.736279 -0.760234 -0.784247 -0.808199
+-0.83194 -0.855285 -0.878013 -0.899863 -0.920533 -0.939687 -0.956949
+-0.971916 -0.984156 -0.993221 -0.998655 -1 -0.996813 -0.988671 -0.975191
+-0.956033 -0.930918 -0.899638 -0.862061 -0.818147 -0.767947 -0.71161
+-0.649386 -0.581625 -0.508772 -0.431366 -0.350025 -0.265443 -0.178375
+-0.0896226 -1.94061e-05 0.089584;
+#X pop;
+#X floatatom 202 171 0 0 100;
+#N canvas 159 26 532 285 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 426 180 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 397 110 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 85 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 20 155 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 105 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 90 outlet;
+#X msg 214 65 \; pd dsp 1;
+#X obj 83 198 line~;
+#X obj 20 207 *~;
+#X obj 20 232 dac~;
+#X obj 83 173 pack 0 50;
+#X text 20 132 audio;
+#X text 96 114 show level;
+#X obj 426 155 t b;
+#X obj 20 181 hip~ 1;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 26 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 27 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 5 0;
+#X connect 27 0 21 0;
+#X restore 164 199 pd output;
+#X msg 240 172 MUTE;
+#X text 30 123 oscillator -->;
+#X text 485 445 updated for Pd version 0.34;
+#X text 33 8 WAVETABLE OSCILLATORS;
+#X text 36 106 wavetable;
+#X text 88 54 pitch->;
+#X graph graph2 0 0 258 1000 475 155 734 15;
+#X array pitch11 259 float 1;
+#A 0 757.143 757.143 735.714 700 671.429 650 621.429 600 571.429 550
+521.429 507.143 485.714 464.286 442.857 428.571 414.286 400 378.571
+364.286 342.857 328.571 928.571 921.429 921.429 914.286 907.143 892.857
+885.714 878.571 864.286 850 828.571 807.143 792.857 785.714 775 764.286
+753.571 742.857 735.714 728.571 721.429 714.286 703.571 692.857 682.143
+671.429 650 628.571 617.857 607.143 596.429 585.714 575 564.286 553.571
+542.857 532.143 521.429 510.714 500 485.714 478.571 464.286 450 435.714
+428.571 400 392.857 385.714 378.571 357.143 350 342.857 335.714 328.571
+314.286 292.857 285.714 271.429 264.286 571.429 571.429 571.429 571.429
+571.429 564.286 564.286 278.571 271.429 271.429 278.571 278.571 278.571
+278.571 571.429 571.429 571.429 575 578.571 578.571 278.571 278.571
+285.714 285.714 278.571 278.571 278.571 878.571 878.571 878.571 878.571
+878.571 321.429 325 328.571 328.571 328.571 328.571 885.714 885.714
+885.714 885.714 207.143 207.143 207.143 200 207.143 207.143 207.143
+214.286 214.286 221.429 228.571 228.571 242.857 250 257.143 264.286
+278.571 292.857 307.143 321.429 335.714 350 371.429 392.857 421.429
+435.714 471.429 500 542.857 571.429 628.571 664.286 700 728.571 757.143
+792.857 828.571 885.714 928.571 978.571 1000 1007.14 1007.14 1000 1000
+992.857 985.714 885.714 914.286 671.429 671.429 671.429 671.429 671.429
+671.429 671.429 671.429 671.429 671.429 678.571 635.714 635.714 678.571
+714.286 714.286 678.571 635.714 635.714 635.714 742.857 742.857 685.714
+685.714 635.714 621.429 685.714 792.857 792.857 678.571 521.429 521.429
+521.429 864.286 857.143 857.143 471.429 471.429 471.429 471.429 921.429
+921.429 385.714 385.714 385.714 964.286 964.286 964.286 328.571 328.571
+328.571 328.571 885.714 885.714 885.714 685.714 214.286 214.286 207.143
+207.143 921.429 921.429 921.429 921.429 207.143 207.143 200 200 957.143
+957.143 950 214.286 214.286 207.143 207.143 957.143 957.143 950 200
+207.143 207.143 942.857 942.857 942.857 950 950;
+#X pop;
+#X obj 164 87 tabosc4~ pitch11;
+#X obj 164 123 tabosc4~ waveform11;
+#X obj 164 55 sig~ 0.5;
+#X text 13 319 Here's a tabosc4~ controlling the frequency of another
+one. If you get properties on the two arrays \, you'll see that the
+top graph has a vertical scale from 0 to 1000 \; we're looping through
+that at a frequency of 0.5 Hz. and the output is used as the frequency
+input of the second tabosc4~. I've detected Klingons \, Captain Kirk...
+;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 10 0 11 0;
+#X connect 11 0 2 0;
+#X connect 12 0 10 0;
diff --git a/pd/doc/3.audio.examples/12.tabread4.pd b/pd/doc/3.audio.examples/12.tabread4.pd
new file mode 100644
index 00000000..b045485c
--- /dev/null
+++ b/pd/doc/3.audio.examples/12.tabread4.pd
@@ -0,0 +1,128 @@
+#N canvas 55 137 820 651 12;
+#X graph graph1 0 -1.02 130 1.02 462 160 720 30;
+#X array waveform12 131 float 1;
+#A 0 -0.172615 -0.172615 -0.172615 -0.172615 -0.172615 -0.141231 -0.109846
+-0.0941538 -0.0627692 -0.0470769 0.0156923 0.0784615 0.125538 0.188308
+0.235385 0.298154 0.360923 0.392308 0.470769 0.533538 0.596308 0.643385
+0.674769 0.721846 0.753231 0.784615 0.816 0.831692 0.847385 0.878769
+0.894462 0.910154 0.910154 0.910154 0.910154 0.910154 0.894462 0.894462
+0.894462 0.894462 0.878769 0.863077 0.816 0.800308 0.768923 0.737538
+0.706154 0.674769 0.643385 0.596308 0.564923 0.533538 0.470769 0.423692
+0.376615 0.313846 0.266769 0.204 0.172615 0.109846 0.0627692 0.0156923
+0 -0.0313846 -0.0627692 -0.0784615 -0.0941538 -0.109846 -0.141231 -0.156923
+-0.172615 -0.204 -0.219692 -0.219692 -0.235385 -0.235385 -0.235385
+-0.219692 -0.219692 -0.219692 -0.204 -0.156923 -0.125538 -0.0784615
+0 0.172615 0.313846 0.470769 0.564923 0.627692 0.690462 0.721846 0.737538
+0.753231 0.768923 0.768923 0.753231 0.737538 0.706154 0.674769 0.612
+0.580615 0.549231 0.517846 0.486462 0.423692 0.392308 0.360923 0.282462
+0.219692 0.109846 -0.0156923 -0.0941538 -0.109846 -0.141231 -0.156923
+-0.172615 -0.188308 -0.204 -0.204 -0.219692 -0.204 -0.204 -0.219692
+-0.219692 -0.204 -0.204 -0.204 -0.204 -0.204 -0.188308;
+#X pop;
+#X floatatom 194 299 0 0 100;
+#N canvas 159 26 532 285 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 426 180 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 397 110 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 85 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 20 155 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 105 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 90 outlet;
+#X msg 214 65 \; pd dsp 1;
+#X obj 83 198 line~;
+#X obj 20 207 *~;
+#X obj 20 232 dac~;
+#X obj 83 173 pack 0 50;
+#X text 20 132 audio;
+#X text 96 114 show level;
+#X obj 426 155 t b;
+#X obj 20 181 hip~ 1;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 26 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 27 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 5 0;
+#X connect 27 0 21 0;
+#X restore 156 327 pd output;
+#X msg 232 300 MUTE;
+#X text 554 624 updated for Pd version 0.34;
+#X text 33 8 WAVETABLE OSCILLATORS;
+#X obj 156 95 phasor~;
+#X obj 156 184 tabread4~ waveform12;
+#X obj 156 157 +~ 1;
+#X floatatom 156 66 4 0 0;
+#X floatatom 250 59 4 0 1000;
+#X obj 250 111 pack 0 50;
+#X obj 250 137 line~;
+#X obj 156 131 *~;
+#X text 21 81 phase;
+#X text 20 96 generation -->;
+#X text 25 117 range;
+#X text 24 132 adjustment -->;
+#X text 250 38 squeeze;
+#X text 133 40 frequency;
+#X graph graph3 0 -1 440 1 481 330 781 190;
+#X array wave-out12 441 float 0;
+#X pop;
+#X obj 177 247 tabwrite~ wave-out12;
+#X msg 177 216 bang;
+#X text 223 217 <--click to graph;
+#X obj 250 84 + 128;
+#X text 25 360 The tabread4~ module is available for situations requiring
+more control than tabosc4~ offers. The relationship between the two
+is the same as between cos~ and osc~ \, although the units are different
+between cos~ and tabread4~. Cos~ assumes input is normalized from 0
+to 1 (and will wrap around as needed.) Tabread4~ takes values from
+1 to n-2 where n is the number of points in the table-- for a 259-point
+table such as we have here \, it's 1 to 129 (so the "good" segment
+is 128 samples long.);
+#X text 30 508 You would use tabread4~ (as opposed to tabosc4~) if
+you need direct control of the phase \, for instance if you to advance
+nonlinearly through the table. In the case shown here \, the "squeeze"
+factor makes the phase grow to a value at least \, and possibly much
+graeater than \, 129 (to which tabread4~ then limits it). So the resulting
+waveform is compressed in time.;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 6 0 13 0;
+#X connect 7 0 2 0;
+#X connect 7 0 21 0;
+#X connect 8 0 7 0;
+#X connect 9 0 6 0;
+#X connect 10 0 24 0;
+#X connect 11 0 12 0;
+#X connect 12 0 13 1;
+#X connect 13 0 8 0;
+#X connect 22 0 21 0;
+#X connect 24 0 11 0;
diff --git a/pd/doc/3.audio.examples/13.tabread4.interpolation.pd b/pd/doc/3.audio.examples/13.tabread4.interpolation.pd
new file mode 100644
index 00000000..18aef089
--- /dev/null
+++ b/pd/doc/3.audio.examples/13.tabread4.interpolation.pd
@@ -0,0 +1,44 @@
+#N canvas 137 102 781 520 12;
+#X graph graph1 0 -1.02 10 1.02 468 159 648 29;
+#X array waveform13 11 float 1;
+#A 0 1 1 1 1 1 1 1 -1 -1 -1 -1;
+#X pop;
+#X text 533 502 updated for Pd version 0.34;
+#X obj 156 157 +~ 1;
+#X text 21 81 phase;
+#X text 20 96 generation -->;
+#X text 25 117 range;
+#X text 24 132 adjustment -->;
+#X graph graph3 0 -1.02 440 1.02 469 362 769 222;
+#X array wave-out13 441 float 0;
+#X pop;
+#X msg 177 216 bang;
+#X text 223 217 <--click to graph;
+#N canvas 11 418 523 216 other-stuff 0;
+#X obj 41 49 loadbang;
+#X msg 39 81 \; waveform13 0 1 1 1 1 1 1 1 -1 -1 -1 -1 \; waveform13
+xlabel -1.2 0 1 2 3 4 5 6 7 8 9 10 \; pd dsp 1;
+#X connect 0 0 1 0;
+#X restore 626 426 pd other-stuff;
+#X obj 156 247 tabwrite~ wave-out13;
+#X obj 156 184 tabread4~ waveform13;
+#X obj 156 131 *~ 8;
+#X obj 156 95 phasor~ 220;
+#X text 36 22 4-POINT INTERPOLATION IN DETAIL;
+#X obj 216 316 sig~ 220;
+#X obj 216 346 tabosc4~ waveform13;
+#X text 35 293 (this would be;
+#X text 36 313 equivalent to the;
+#X text 110 333 above) -->;
+#X text 18 409 This patch demonstrates 4-point interpolation in tabread4~.
+The 11-point table \, waveform13 \, contains a transition from from
+1 to -1 \, which is "smoothed" as seen in wave-out13. There's no such
+transition at the wraparoind point--the interpolation always happens
+between 4 consccutive samples of the table \, disregarding wraparound.
+;
+#X connect 2 0 12 0;
+#X connect 8 0 11 0;
+#X connect 12 0 11 0;
+#X connect 13 0 2 0;
+#X connect 14 0 13 0;
+#X connect 16 0 17 0;
diff --git a/pd/doc/3.audio.examples/14.more.tabread.pd b/pd/doc/3.audio.examples/14.more.tabread.pd
new file mode 100644
index 00000000..22ff2846
--- /dev/null
+++ b/pd/doc/3.audio.examples/14.more.tabread.pd
@@ -0,0 +1,106 @@
+#N canvas 55 137 777 467 12;
+#X graph graph1 0 -1.02 130 1.02 462 160 720 30;
+#X array pitchmod14 131 float 1;
+#A 0 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385
+0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.863077
+0.863077 0.863077 0.863077 0.863077 0.863077 0.863077 0.863077 0.863077
+0.863077 0.863077 0.863077 0.863077 0.863077 0.863077 0.863077 0.863077
+0.863077 0.863077 0.863077 0.863077 0.863077 0.831692 0.847385 0.847385
+0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385
+0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385
+0.847385 0.863077 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385
+-0.800308 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.768923 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.768923 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.800308 -0.800308
+-0.800308 -0.800308 -0.800308 -0.800308 -0.800308;
+#X pop;
+#X floatatom 194 299 0 0 100;
+#N canvas 159 26 532 285 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 426 180 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 397 110 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 85 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 20 155 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 105 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 90 outlet;
+#X msg 214 65 \; pd dsp 1;
+#X obj 83 198 line~;
+#X obj 20 207 *~;
+#X obj 20 232 dac~;
+#X obj 83 173 pack 0 50;
+#X text 20 132 audio;
+#X text 96 114 show level;
+#X obj 426 155 t b;
+#X obj 20 181 hip~ 1;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 26 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 27 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 5 0;
+#X connect 27 0 21 0;
+#X restore 156 327 pd output;
+#X msg 232 300 MUTE;
+#X text 521 439 updated for Pd version 0.34;
+#X floatatom 153 95 4 0 0;
+#X text 153 69 frequency;
+#X text 33 8 MORE ON FREQUENCY MODULATION;
+#X floatatom 195 206 4 0 0;
+#X text 155 50 modulation;
+#X obj 152 157 *~;
+#X text 255 150 modulation;
+#X text 253 169 depth;
+#X floatatom 201 157 4 0 0;
+#X obj 152 205 +~;
+#X text 250 212 frequency;
+#X obj 152 237 osc~;
+#X obj 153 122 tabosc4~ pitchmod14;
+#X text 254 194 carrier;
+#X text 44 379 This patch is like the original FM patch except in having
+a settable wavetable for the modulation oscillator. Try changing the
+waveform as well as the three familiar parameters.;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 5 0 17 0;
+#X connect 8 0 14 1;
+#X connect 10 0 14 0;
+#X connect 13 0 10 1;
+#X connect 14 0 16 0;
+#X connect 16 0 2 0;
+#X connect 17 0 10 0;
diff --git a/pd/doc/3.audio.examples/15.table.switching.pd b/pd/doc/3.audio.examples/15.table.switching.pd
new file mode 100644
index 00000000..558f91c4
--- /dev/null
+++ b/pd/doc/3.audio.examples/15.table.switching.pd
@@ -0,0 +1,127 @@
+#N canvas 55 137 835 504 12;
+#X graph graph1 0 -1.02 130 1.02 565 153 823 23;
+#X array waveshape15a 131 float 1;
+#A 0 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385
+0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.863077
+0.863077 0.863077 0.863077 0.863077 0.863077 0.863077 0.863077 0.863077
+0.863077 0.863077 0.863077 0.863077 0.863077 0.863077 0.863077 0.863077
+0.863077 0.863077 0.863077 0.863077 0.863077 0.831692 0.847385 0.847385
+0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385
+0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385
+0.847385 0.863077 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385
+-0.800308 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.768923 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.768923 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.800308 -0.800308
+-0.800308 -0.800308 -0.800308 -0.800308 -0.800308;
+#X pop;
+#X floatatom 194 299 0 0 100;
+#N canvas 159 26 532 285 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 426 180 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 397 110 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 85 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 20 155 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 105 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 90 outlet;
+#X msg 214 65 \; pd dsp 1;
+#X obj 83 198 line~;
+#X obj 20 207 *~;
+#X obj 20 232 dac~;
+#X obj 83 173 pack 0 50;
+#X text 20 132 audio;
+#X text 96 114 show level;
+#X obj 426 155 t b;
+#X obj 20 181 hip~ 1;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 26 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 27 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 5 0;
+#X connect 27 0 21 0;
+#X restore 156 327 pd output;
+#X msg 232 300 MUTE;
+#X text 581 481 updated for Pd version 0.34;
+#X text 33 8 SWITCHING BETWEEN TABLES;
+#X graph graph1 0 -1.02 130 1.02 565 308 823 178;
+#X array waveshape15b 131 float 1;
+#A 0 -0.659077 -0.643385 -0.643385 -0.627692 -0.612 -0.612 -0.596308
+-0.596308 -0.580615 -0.580615 -0.580615 -0.580615 -0.580615 -0.580615
+-0.580615 -0.596308 -0.596308 -0.596308 -0.596308 -0.596308 -0.596308
+-0.596308 -0.596308 -0.580615 -0.580615 -0.580615 -0.580615 -0.580615
+-0.580615 -0.580615 -0.580615 -0.564923 -0.549231 -0.549231 -0.533538
+-0.517846 -0.517846 -0.517846 -0.517846 -0.517846 -0.517846 -0.517846
+-0.517846 -0.533538 -0.549231 -0.580615 -0.580615 0.847385 0.847385
+0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 0.863077
+0.847385 0.847385 0.847385 0.847385 0.847385 0.847385 -0.800308 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.768923 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.768923 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615 -0.784615
+-0.784615 -0.784615 -0.784615 -0.800308 -0.800308 -0.800308 -0.800308
+-0.800308 -0.800308 -0.800308;
+#X pop;
+#X obj 156 274 tabosc4~ waveshape15a;
+#X obj 156 186 sig~ 110;
+#X msg 181 215 set waveshape15a;
+#X msg 182 244 set waveshape15b;
+#X text 20 51 During a performance you're unlikely to want to draw
+or recalculate wavetables on the fly \, because you don't want to give
+Pd computationally intensive atomic tasks that could make Pd miss a
+DAC deadline. Instead \, use "set" mesages to switch tabosc~ or tabread4~
+between pre-prepared tables. Indeed \, you will eventually want to
+save screen space by throwing all your wavetables in a subpatch somewhere.
+;
+#X obj 161 401 table waveshape15c 131;
+#X text 41 362 There's also a "text object" hook so that you can have
+arrays with parametrizable names and sizes:;
+#X text 31 431 You would use this if you want to include one or more
+arrays in an abstraction. In this invocation you can't save the state
+of the array--instead \, juts read it in from a file or calculate it
+at startup.;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 7 0 2 0;
+#X connect 8 0 7 0;
+#X connect 9 0 7 0;
+#X connect 10 0 7 0;
diff --git a/pd/doc/3.audio.examples/16.table.spectrum.pd b/pd/doc/3.audio.examples/16.table.spectrum.pd
new file mode 100644
index 00000000..65ccccde
--- /dev/null
+++ b/pd/doc/3.audio.examples/16.table.spectrum.pd
@@ -0,0 +1,143 @@
+#N canvas 227 120 801 403 12;
+#X graph graph3 0 0 126 50 496 276 796 136;
+#X array spectrum-tab 127 float 1;
+#A 0 42.8571 42.5 43.2143 43.2143 43.2143 43.2143 43.2143 42.8571 42.8571
+42.8571 42.8571 42.8571 42.8571 42.5 42.5 42.5 42.5 42.5 42.5 42.5
+42.5 42.5 42.5 42.5 42.5 42.5 42.5 42.5 42.5 42.5 42.5 42.5 42.5 42.5
+42.5 42.5 42.1429 42.1429 41.7857 41.0714 40.3571 39.6429 39.2857 38.2143
+37.5 37.1429 36.0714 35.3571 33.9286 33.2143 32.8571 31.4286 31.0714
+30.3571 28.9286 28.2143 27.5 26.4286 25.7143 23.9286 23.2143 21.7857
+21.0714 20.7143 20 19.6429 19.6429 23.2143 28.2143 31.4286 33.5714
+36.4286 37.8571 38.9286 43.9286 45.7143 47.8571 47.8571 47.8571 47.8571
+47.5 47.1429 43.2143 40.3571 36.4286 33.9286 32.1429 29.2857 18.2143
+16.7857 16.7857 17.5 19.6429 22.1429 28.2143 33.9286 33.9286 33.9286
+33.5714 22.5 18.5714 16.7857 4.64286 4.64286 18.2143 17.1429 8.92857
+4.28571 11.4286 10 7.5 6.42857 5.71429 5.35714 5 4.64286 4.28571 3.92857
+3.92857 3.57143 3.57143 2.85714 2.5 2.14286 1.78571 0.714286 0.357143
+;
+#X pop;
+#X floatatom 57 351 0 0 0;
+#N canvas 159 26 526 286 output 0;
+#X obj 345 163 t b;
+#X obj 345 112 f;
+#X obj 345 61 inlet;
+#X text 351 30 mute;
+#X obj 345 189 f;
+#X msg 434 182 0;
+#X msg 345 87 bang;
+#X obj 345 138 moses 1;
+#X obj 405 119 moses 1;
+#X obj 85 151 dbtorms;
+#X obj 405 94 r master-lvl;
+#X obj 85 43 r master-lvl;
+#X obj 345 214 s master-lvl;
+#X obj 22 185 inlet~;
+#X obj 203 42 inlet;
+#X text 203 18 level;
+#X obj 203 102 s master-lvl;
+#X msg 98 67 set \$1;
+#X obj 98 91 outlet;
+#X msg 218 65 \; pd dsp 1;
+#X obj 85 198 line~;
+#X obj 22 216 *~;
+#X obj 22 246 dac~;
+#X obj 85 175 pack 0 50;
+#X text 20 162 audio;
+#X obj 434 155 t b;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 25 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 21 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 25 0 5 0;
+#X restore 19 376 pd output;
+#X msg 95 350 MUTE;
+#N canvas 98 16 694 474 oscbank 0;
+#X obj 36 53 spectrum-partial 1;
+#X obj 36 79 spectrum-partial 2;
+#X obj 36 105 spectrum-partial 3;
+#X obj 36 131 spectrum-partial 4;
+#X obj 36 157 spectrum-partial 5;
+#X obj 36 183 spectrum-partial 6;
+#X obj 36 209 spectrum-partial 7;
+#X obj 36 235 spectrum-partial 8;
+#X obj 36 261 spectrum-partial 9;
+#X obj 36 287 spectrum-partial 10;
+#X obj 216 53 spectrum-partial 11;
+#X obj 122 382 loadbang;
+#X obj 122 407 metro 30;
+#X obj 122 433 s poll-table;
+#X text 107 21 This is the bank of oscillators--open one to see:;
+#X text 72 345 And here we send bangs to "poll-table" needed by the
+abstraction.;
+#X obj 216 79 spectrum-partial 12;
+#X obj 216 105 spectrum-partial 13;
+#X obj 216 131 spectrum-partial 14;
+#X obj 216 157 spectrum-partial 15;
+#X obj 216 183 spectrum-partial 16;
+#X obj 216 209 spectrum-partial 17;
+#X obj 216 235 spectrum-partial 18;
+#X obj 215 261 spectrum-partial 19;
+#X obj 215 287 spectrum-partial 20;
+#X obj 395 53 spectrum-partial 21;
+#X obj 395 78 spectrum-partial 22;
+#X obj 395 104 spectrum-partial 23;
+#X obj 395 130 spectrum-partial 24;
+#X obj 395 156 spectrum-partial 25;
+#X obj 395 182 spectrum-partial 26;
+#X obj 395 207 spectrum-partial 27;
+#X obj 396 234 spectrum-partial 28;
+#X obj 395 260 spectrum-partial 29;
+#X obj 395 287 spectrum-partial 30;
+#X connect 11 0 12 0;
+#X connect 12 0 13 0;
+#X restore 17 251 pd oscbank;
+#X obj 19 321 catch~ sum-bus;
+#X obj 16 153 s pitch;
+#X floatatom 16 125 4 0 0;
+#X text 43 18 DRAWABLE SPECTRA;
+#X floatatom 14 183 4 0 0;
+#X obj 14 211 s whammybar;
+#N canvas 0 0 650 341 table-setup 0;
+#X obj 39 227 loadbang;
+#X msg 39 261 \; spectrum-tab xlabel -5 0 12 24 36 48 60 72 84 96 108
+120;
+#X text 82 60 comment;
+#X connect 0 0 1 0;
+#X restore 17 283 pd table-setup;
+#X msg 596 65 \; spectrum-tab const 0;
+#X text 555 381 Updated for Pd version 0.34;
+#X text 26 42 In this array \, you can draw a spectral envelope that
+will be synthesized by an oscillator bank. Each oscillator in the bank
+computes its own frequency and uses it to look up amplitude from the
+array.;
+#X text 113 254 <-- the oscillator bank;
+#X text 71 128 <-- pitch;
+#X text 61 185 <-- left or right shift (normally 0);
+#X text 157 318 <-- here we just collect the sum of all the partials
+which are computed in "oscbank".;
+#X text 662 44 CLEAR;
+#X text 148 283 <-- make the number labels;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 5 0 2 0;
+#X connect 7 0 6 0;
+#X connect 9 0 10 0;
diff --git a/pd/doc/3.audio.examples/17.foldover.pd b/pd/doc/3.audio.examples/17.foldover.pd
new file mode 100644
index 00000000..81757423
--- /dev/null
+++ b/pd/doc/3.audio.examples/17.foldover.pd
@@ -0,0 +1,112 @@
+#N canvas 17 89 590 637 12;
+#X graph graph1 0 -1.02 130 1.02 295 415 553 285;
+#X array table17 131 float 1;
+#A 0 -0.399997 0 0.399997 0.107489 0.0789648 0.218247 0.115563 0.169861
+0.178655 0.138352 0.235708 0.164533 0.125264 0.214359 0.169042 0.134156
+0.0997789 0.118172 0.270954 0.293566 0.289833 0.12888 -0.215992 -0.0145419
+0.203984 -0.159792 -0.11901 0.135321 -0.0665301 -0.0776689 0.0247374
+-0.0222149 0.0755675 4.97363e-06 -0.049046 0.232851 0.2132 -0.0357245
+-0.101696 -0.125624 -0.0530428 0.0608632 0.111596 0.0910138 -0.0326553
+0.100844 0.22303 -0.0649953 0.00678476 0.247437 -0.0319972 -0.064833
+0.141408 0.00354245 -0.0891558 -0.227284 -0.293046 0.100474 0.173878
+-0.071401 0.0482414 0.0773852 -0.0590095 0.00509727 0.0421473 2.40106e-06
+-0.0421444 -0.00510817 0.0590137 -0.0773699 -0.0482621 0.0714097 -0.173852
+-0.100516 0.293024 0.227308 0.0891607 -0.0035225 -0.141411 0.0648073
+0.0320313 -0.247433 -0.00682219 0.0650219 -0.223015 -0.100872 0.0326609
+-0.0910026 -0.1116 -0.0608712 0.0530287 0.125624 0.101698 0.035742
+-0.213174 -0.232876 0.0490274 1.49209e-05 -0.0755759 0.0222125 -0.024735
+0.0776516 0.066554 -0.135321 0.118972 0.159827 -0.203967 0.0144949
+0.216008 -0.128842 -0.289832 -0.293563 -0.270967 -0.118185 -0.0997734
+-0.134155 -0.169034 -0.214362 -0.125272 -0.164519 -0.235713 -0.138359
+-0.178646 -0.169872 -0.115556 -0.218244 -0.0789868 -0.107456 -0.399993
+-7.20319e-05 0.4;
+#X pop;
+#X floatatom 73 385 0 0 100;
+#N canvas 159 26 532 285 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 426 180 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 397 110 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 85 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 20 182 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 105 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 90 outlet;
+#X msg 214 65 \; pd dsp 1;
+#X obj 83 198 line~;
+#X obj 20 207 *~;
+#X obj 20 232 dac~;
+#X obj 83 173 pack 0 50;
+#X text 20 159 audio;
+#X text 96 114 show level;
+#X obj 426 155 t b;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 26 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 21 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 5 0;
+#X restore 35 413 pd output;
+#X msg 111 386 MUTE;
+#X text 343 617 updated for Pd version 0.34;
+#X msg 310 531 \; table17 const 0;
+#X text 362 513 CLEAR;
+#X obj 35 356 tabosc4~ table17;
+#X obj 35 329 line~;
+#X text 229 3 FOLDOVER;
+#X msg 28 532 \; table17 const 0 \, 0 1 1 1 1 1;
+#X msg 28 443 \; table17 sinesum 128 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0
+1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 \, normalize
+0.4;
+#X msg 35 278 500 \, 1423 4000;
+#X text 26 26 WARNING: THIS IS REALLY OBNOXIOUS--TURN YOUR AMP DOWN!
+;
+#X floatatom 43 303 5 0 0;
+#X text 25 125 Foldover occurs when you synthesize frequencies greater
+than the Nyquist frequency (half the sample rate). In this example
+\, the fundamental only reaches 1423 \, but the tables contain high
+partials. As the partials sweep upward you hear them reflect off the
+Nyquist frequency. Also \, partials can come into contact with each
+other causing beating. The value of 1423 was chosen to make the beating
+effect especially strong \, but it's clearly audible even for a 440-Hz.
+sawtooth wave \, for example.;
+#X text 25 58 Use this patch sparingly... it's probably bad for your
+ears. Don't amuse yourself by playing this as part of your club act
+unless you want your country's health department to inquire.;
+#X text 16 572 Synthesis techniques vary in their tendency to make
+foldover. For higher pitched sounds you'll want to try out relatively
+folvover-resistant ones.;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 7 0 2 0;
+#X connect 8 0 7 0;
+#X connect 12 0 8 0;
+#X connect 14 0 8 0;
diff --git a/pd/doc/3.audio.examples/18.PART3.pulse.pd b/pd/doc/3.audio.examples/18.PART3.pulse.pd
new file mode 100644
index 00000000..8efe6390
--- /dev/null
+++ b/pd/doc/3.audio.examples/18.PART3.pulse.pd
@@ -0,0 +1,126 @@
+#N canvas 15 126 821 582 12;
+#X obj 285 163 line~;
+#X floatatom 66 64 0 0 0;
+#X obj 43 315 cos~;
+#X graph graph1 0 -1.02 882 1.02 599 472 799 342;
+#X array pulse-output 882 float 0;
+#X pop;
+#X floatatom 72 407 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 43 435 pd output;
+#X msg 111 405 MUTE;
+#X text 158 406 <-- output amplitude;
+#X obj 66 91 phasor~ 0;
+#X obj 285 139 pack 0 50;
+#X floatatom 285 54 0 0 0;
+#X text 63 43 frequency;
+#X obj 66 115 -~ 0.5;
+#X obj 66 207 *~;
+#X obj 285 78 / 10;
+#X obj 43 265 clip~ -0.5 0.5;
+#X obj 43 371 hip~ 5;
+#X graph graph1 0 -1.02 882 1.02 599 168 799 108;
+#X array phase-output 882 float 0;
+#X pop;
+#X graph graph1 0 -1.02 882 1.02 599 335 799 205;
+#X array clip-output 882 float 0;
+#X pop;
+#X text 280 34 bandwidth;
+#X text 130 114 phase -1/2 to 1/2;
+#X text 152 91 phase 0 to 1;
+#X text 132 5 PULSE GENERATOR;
+#X obj 32 234 tabwrite~ phase-output;
+#X obj 32 346 tabwrite~ pulse-output;
+#X text 116 372 high pass filter to cut DC;
+#X msg 32 147 bang;
+#X text 332 79 fix range;
+#X text 337 100 force;
+#X text 337 117 nonnegative;
+#X text 339 164 smooth it;
+#X text 327 187 add 1;
+#X text 78 148 <-- click to graph;
+#X text 96 209 increase amplitude;
+#X text 177 264 clip back to range -1/2 to 1/2;
+#X text 103 316 cosine wave lookup (-1/2 and 1/2 give -1);
+#X text 24 470 This patch computes a pulse train \, with a "bandwidth"
+control that essentually squeezes the pulses. If "bandwidth" is zero
+you get a pure cosine wave \, and for larger values of the bandwidth
+\, the cosine wave is squeezed to fill smaller portions of the waveform.
+;
+#X obj 285 188 +~ 1;
+#X obj 32 292 tabwrite~ clip-output;
+#X text 601 478 ---- 0.02 seconds ----;
+#X obj 285 102 max 0;
+#X text 544 551 updated for Pd version 0.34;
+#X connect 0 0 37 0;
+#X connect 1 0 8 0;
+#X connect 2 0 16 0;
+#X connect 2 0 24 0;
+#X connect 4 0 5 1;
+#X connect 5 0 4 0;
+#X connect 6 0 5 2;
+#X connect 8 0 12 0;
+#X connect 9 0 0 0;
+#X connect 10 0 14 0;
+#X connect 12 0 13 0;
+#X connect 13 0 15 0;
+#X connect 13 0 23 0;
+#X connect 14 0 40 0;
+#X connect 15 0 2 0;
+#X connect 15 0 38 0;
+#X connect 16 0 5 0;
+#X connect 26 0 23 0;
+#X connect 26 0 24 0;
+#X connect 26 0 38 0;
+#X connect 37 0 13 1;
+#X connect 40 0 9 0;
diff --git a/pd/doc/3.audio.examples/19.just.say.pd b/pd/doc/3.audio.examples/19.just.say.pd
new file mode 100644
index 00000000..b82b4953
--- /dev/null
+++ b/pd/doc/3.audio.examples/19.just.say.pd
@@ -0,0 +1,152 @@
+#N canvas 32 67 900 421 12;
+#X obj 39 247 cos~;
+#X graph graph1 0 -1.02 44100 1.02 452 206 652 76;
+#X array env-output 44100 float 0;
+#X pop;
+#X floatatom 71 305 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 39 333 pd output;
+#X msg 115 306 MUTE;
+#X msg 162 93 bang;
+#X text 203 93 <-- click to graph;
+#X obj 39 168 -~ 0.5;
+#X obj 39 192 *~;
+#X obj 39 219 clip~ -0.5 0.5;
+#X obj 39 274 hip~ 5;
+#X obj 126 60 *~;
+#X floatatom 205 142 0 0 0;
+#X floatatom 205 168 0 0 0;
+#X obj 126 27 phasor~ -4;
+#X obj 126 191 +~ 0.5;
+#X obj 162 117 tabwrite~ env-output;
+#X text 451 211 --------- 1 second ---------;
+#X floatatom 205 194 0 0 0;
+#X obj 126 142 lop~ 130;
+#N canvas 168 232 351 420 freq 0;
+#X obj 180 176 t f f;
+#X obj 181 202 *;
+#X obj 60 320 line 0 30;
+#X obj 90 132 t b b;
+#X obj 90 107 metro 100;
+#X obj 61 287 pack;
+#X obj 60 376 outlet;
+#X floatatom 89 82 0 0 0;
+#X floatatom 54 243 0 0 0;
+#X floatatom 94 248 0 0 0;
+#X obj 60 348 pack 0 30;
+#X obj 55 202 + 150;
+#X obj 88 34 loadbang;
+#X msg 89 58 1;
+#X obj 56 175 random 300;
+#X obj 181 226 + 100;
+#X obj 179 152 random 35;
+#X connect 0 0 1 0;
+#X connect 0 1 1 1;
+#X connect 1 0 15 0;
+#X connect 2 0 10 0;
+#X connect 3 0 14 0;
+#X connect 3 1 16 0;
+#X connect 4 0 3 0;
+#X connect 5 0 2 0;
+#X connect 7 0 4 0;
+#X connect 8 0 5 0;
+#X connect 9 0 5 1;
+#X connect 10 0 6 0;
+#X connect 11 0 8 0;
+#X connect 12 0 13 0;
+#X connect 13 0 7 0;
+#X connect 14 0 11 0;
+#X connect 15 0 4 1;
+#X connect 15 0 9 0;
+#X connect 16 0 0 0;
+#X restore 38 94 pd freq;
+#X obj 39 119 line~;
+#X obj 39 144 phasor~;
+#X text 225 19 negative frequency;
+#X text 226 35 makes falling sawtooth;
+#X text 155 59 square it to make a curve;
+#X text 245 152 you can;
+#X text 243 170 adjust these;
+#X text 247 189 values;
+#X text 334 250 We interrupt this series of patches to bring you an
+important message from Nancy Reagan. If \, anywhere \, at any time
+\, someone offers you an illicit drug \, just say one word in reply...
+;
+#X text 334 313 Now that I'm sure you've heard this important message
+\, we can return to the essentially frivolous occupation of making
+turn-of-the-millenium western art music.;
+#X obj 126 165 *~ 6;
+#X text 561 384 updated for Pd version 0.34;
+#X text 156 305 <-- output;
+#X connect 0 0 10 0;
+#X connect 2 0 3 1;
+#X connect 3 0 2 0;
+#X connect 4 0 3 2;
+#X connect 5 0 16 0;
+#X connect 7 0 8 0;
+#X connect 8 0 9 0;
+#X connect 9 0 0 0;
+#X connect 10 0 3 0;
+#X connect 11 0 16 0;
+#X connect 11 0 19 0;
+#X connect 12 0 19 1;
+#X connect 13 0 31 1;
+#X connect 14 0 11 0;
+#X connect 14 0 11 1;
+#X connect 15 0 8 1;
+#X connect 18 0 15 1;
+#X connect 19 0 31 0;
+#X connect 20 0 21 0;
+#X connect 21 0 22 0;
+#X connect 22 0 7 0;
+#X connect 31 0 15 0;
diff --git a/pd/doc/3.audio.examples/20.pulse.spectrum.pd b/pd/doc/3.audio.examples/20.pulse.spectrum.pd
new file mode 100644
index 00000000..49d21cbd
--- /dev/null
+++ b/pd/doc/3.audio.examples/20.pulse.spectrum.pd
@@ -0,0 +1,136 @@
+#N canvas 15 126 887 588 12;
+#X obj 189 166 line~;
+#X obj 42 187 cos~;
+#X graph graph1 0 -1.02 882 1.02 633 508 833 378;
+#X array pulse-output 882 float 0;
+#X pop;
+#X floatatom 71 317 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 42 345 pd output;
+#X msg 118 319 MUTE;
+#X obj 189 142 pack 0 50;
+#X floatatom 189 41 0 0 0;
+#X text 598 545 updated for Pd version 0.26;
+#X obj 43 114 -~ 0.5;
+#X obj 43 140 *~;
+#X obj 189 67 / 10;
+#X obj 189 91 moses 0;
+#X msg 189 115 0;
+#X obj 42 163 clip~ -0.5 0.5;
+#X obj 42 289 hip~ 5;
+#X graph graph1 0 0 128 500 503 285 759 155;
+#X array spectrum 128 float 0;
+#X pop;
+#X text 184 23 bandwidth;
+#X obj 115 267 tabwrite~ pulse-output;
+#X msg 105 229 bang;
+#X text 143 226 <-- click to graph;
+#X obj 189 191 +~ 1;
+#N canvas 204 17 358 238 fft 0;
+#X obj 46 48 inlet~;
+#X obj 159 181 tabwrite~ spectrum;
+#X obj 159 145 inlet;
+#X obj 46 78 rfft~;
+#X obj 46 111 *~;
+#X obj 77 111 *~;
+#X obj 46 141 sqrt~;
+#X obj 191 45 block~ 1024 1;
+#X connect 0 0 3 0;
+#X connect 2 0 1 0;
+#X connect 3 0 4 0;
+#X connect 3 0 4 1;
+#X connect 3 1 5 0;
+#X connect 3 1 5 1;
+#X connect 4 0 6 0;
+#X connect 5 0 6 0;
+#X connect 6 0 1 0;
+#X restore 53 267 pd fft;
+#X obj 43 90 phasor~ 172.266;
+#X obj 42 211 +~ 1;
+#X text 63 1 PULSE SPECTRUM MEASUREMENT;
+#X text 16 377 Here is a measured amplitude spectrum for the pulse
+train. Nutice that \, other than a smallish spillover \, the energy
+sits in one "lobe" whose changing width justifies our calling the squeeze
+factor the "bandwidth.";
+#X text 16 442 The spectrum is in units of amplitude. THe sidelobes
+\, although they look small \, are actually only about 34 dB down.
+You can design more complicated pulse trains \, little Blackman window
+functions \, which control the sidelobes much better.;
+#X text 17 518 The spectrum measurement is done in the "pd fft" subwindow
+\, but see the "FFT examples" for information about that.;
+#X text 501 291 0;
+#X text 749 288 5512;
+#X text 633 511 ---- 0.02 seconds ----;
+#X text 160 319 <-- output;
+#X connect 0 0 21 0;
+#X connect 1 0 24 0;
+#X connect 3 0 4 1;
+#X connect 4 0 3 0;
+#X connect 5 0 4 2;
+#X connect 6 0 0 0;
+#X connect 7 0 11 0;
+#X connect 9 0 10 0;
+#X connect 10 0 14 0;
+#X connect 11 0 12 0;
+#X connect 12 0 13 0;
+#X connect 12 1 6 0;
+#X connect 13 0 6 0;
+#X connect 14 0 1 0;
+#X connect 15 0 4 0;
+#X connect 19 0 18 0;
+#X connect 19 0 22 1;
+#X connect 21 0 10 1;
+#X connect 23 0 9 0;
+#X connect 24 0 22 0;
+#X connect 24 0 15 0;
+#X connect 24 0 18 0;
diff --git a/pd/doc/3.audio.examples/21.more.pulses.pd b/pd/doc/3.audio.examples/21.more.pulses.pd
new file mode 100644
index 00000000..1aa97555
--- /dev/null
+++ b/pd/doc/3.audio.examples/21.more.pulses.pd
@@ -0,0 +1,138 @@
+#N canvas 15 126 902 581 12;
+#X obj 220 171 line~;
+#X msg 350 15 \; pd dsp 1;
+#X msg 434 16 \; pd dsp 0;
+#X text 371 46 ON;
+#X text 451 47 OFF;
+#X floatatom 68 303 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 39 331 pd output;
+#X msg 106 303 MUTE;
+#X text 150 302 <-- output amplitude;
+#X obj 220 147 pack 0 50;
+#X floatatom 220 46 0 0 0;
+#X text 640 544 updated for Pd version 0.26;
+#X obj 70 108 *~;
+#X obj 220 72 / 10;
+#X obj 220 96 moses 0;
+#X msg 220 120 0;
+#X obj 39 275 hip~ 5;
+#X graph graph1 0 0 128 300 620 491 876 361;
+#X array spectrum 128 float 0;
+#X pop;
+#X text 215 28 bandwidth;
+#X msg 135 235 bang;
+#X text 177 234 <-- click to graph;
+#N canvas 204 17 358 238 fft 0;
+#X obj 46 48 inlet~;
+#X obj 159 181 tabwrite~ spectrum;
+#X obj 159 145 inlet;
+#X obj 46 78 rfft~;
+#X obj 46 111 *~;
+#X obj 77 111 *~;
+#X obj 46 141 sqrt~;
+#X obj 191 45 block~ 1024 1;
+#X connect 0 0 3 0;
+#X connect 2 0 1 0;
+#X connect 3 0 4 0;
+#X connect 3 0 4 1;
+#X connect 3 1 5 0;
+#X connect 3 1 5 1;
+#X connect 4 0 6 0;
+#X connect 5 0 6 0;
+#X connect 6 0 1 0;
+#X restore 68 237 pd fft;
+#X text 618 497 0;
+#X text 851 492 5512;
+#X obj 78 141 *~;
+#X obj 18 141 sig~ 1;
+#X obj 39 194 /~;
+#X obj 54 168 +~;
+#X obj 70 79 osc~ 86.1328;
+#X text 103 107 call this X;
+#X text 111 141 X^2;
+#X text 84 171 1+X^2;
+#X text 71 196 1/(1+X^2);
+#X text 10 357 This is the form of pulse train used in the Phase Aligned
+Formant (PAF) algorithm. It has the neat property that its amplitude
+spectrum drops off as a perfectly exponential function of frequency.
+This algorithm is protected by French and US patents. contact Vincent
+Puig to learn what restrictions may apply.;
+#X text 11 457 On the other hand \, there are rumors that exp(-X*X)
+actually sounds better than 1/(1+X*X). To compute exp(-X*X) efficiently
+you will want to employ tabread4~ with a stored bell curve. I don't
+want to know you're doing this. However \, the first Pd user who e-mails
+me the correct formula for the output spectrum wins a free CO2 fire
+extinguisher.;
+#X text 28 4 ANOTHER PULSE WIDTH MOD ALGORITHM;
+#X connect 0 0 12 1;
+#X connect 5 0 6 1;
+#X connect 6 0 5 0;
+#X connect 7 0 6 2;
+#X connect 9 0 0 0;
+#X connect 10 0 13 0;
+#X connect 12 0 24 0;
+#X connect 12 0 24 1;
+#X connect 13 0 14 0;
+#X connect 14 0 15 0;
+#X connect 14 1 9 0;
+#X connect 15 0 9 0;
+#X connect 16 0 6 0;
+#X connect 19 0 21 1;
+#X connect 24 0 27 1;
+#X connect 25 0 26 0;
+#X connect 25 0 27 0;
+#X connect 26 0 16 0;
+#X connect 26 0 21 0;
+#X connect 27 0 26 1;
+#X connect 28 0 12 0;
diff --git a/pd/doc/3.audio.examples/22.pulse.width.mod.pd b/pd/doc/3.audio.examples/22.pulse.width.mod.pd
new file mode 100644
index 00000000..214d250a
--- /dev/null
+++ b/pd/doc/3.audio.examples/22.pulse.width.mod.pd
@@ -0,0 +1,98 @@
+#N canvas 27 355 931 532 12;
+#X floatatom 86 104 0 0 0;
+#X graph graph1 0 -1.02 882 1.02 669 456 869 326;
+#X array difference-output 882 float 0;
+#X pop;
+#X floatatom 123 324 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 86 351 pd output;
+#X msg 162 324 MUTE;
+#X obj 86 137 phasor~ 0;
+#X text 83 86 frequency;
+#X graph graph1 0 -1.02 882 1.02 668 179 868 49;
+#X array phasor1-output 882 float 0;
+#X pop;
+#X msg 191 77 bang;
+#X text 231 76 <-- click to graph;
+#X text 57 9 CLASSICAL PULSE WIDTH MODULATION;
+#X obj 102 196 phasor~ 0;
+#X obj 102 172 + 0.2;
+#X obj 86 246 -~;
+#X graph graph1 0 -1.02 882 1.02 668 316 868 186;
+#X array phasor2-output 882 float 0;
+#X pop;
+#X obj 191 164 tabwrite~ phasor1-output;
+#X obj 191 222 tabwrite~ phasor2-output;
+#X obj 191 276 tabwrite~ difference-output;
+#X text 12 386 This patch demonstrates pulse width modulation \, which
+is accomplished simply by subtracting two sawtooth waves at a varying
+phase difference. Here their frequencies are set to differ by 1/5 Hz.
+so that the relative phase wanders continuously.;
+#X text 669 459 ---- 0.02 seconds ----;
+#X text 665 498 updated for Pd version 0.34;
+#X text 203 325 <-- output;
+#X connect 0 0 5 0;
+#X connect 0 0 12 0;
+#X connect 2 0 3 1;
+#X connect 3 0 2 0;
+#X connect 4 0 3 2;
+#X connect 5 0 13 0;
+#X connect 5 0 15 0;
+#X connect 8 0 15 0;
+#X connect 8 0 16 0;
+#X connect 8 0 17 0;
+#X connect 11 0 13 1;
+#X connect 11 0 16 0;
+#X connect 12 0 11 0;
+#X connect 13 0 17 0;
+#X connect 13 0 3 0;
diff --git a/pd/doc/3.audio.examples/23.stereo.pd b/pd/doc/3.audio.examples/23.stereo.pd
new file mode 100644
index 00000000..1c417df5
--- /dev/null
+++ b/pd/doc/3.audio.examples/23.stereo.pd
@@ -0,0 +1,87 @@
+#N canvas 27 355 553 341 12;
+#X floatatom 59 63;
+#X msg 340 12 \; pd dsp 1;
+#X msg 407 12 \; pd dsp 0;
+#X text 361 45 ON;
+#X text 424 43 OFF;
+#X floatatom 123 196;
+#N canvas 159 26 618 383 output 0;
+#X obj 393 156 t b;
+#X obj 393 106 f;
+#X obj 393 56 inlet;
+#X text 399 25 mute;
+#X obj 393 181 f;
+#X msg 480 174 0;
+#X msg 393 81 bang;
+#X obj 393 131 moses 1;
+#X obj 480 149 t b f;
+#X obj 452 113 moses 1;
+#X obj 138 144 dbtorms;
+#X obj 452 88 r master-lvl;
+#X obj 138 38 r master-lvl;
+#X obj 393 206 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 254 37 inlet;
+#X text 254 14 level;
+#X obj 254 96 s master-lvl;
+#X msg 151 61 set \$1;
+#X obj 151 85 outlet;
+#X msg 269 60 \; pd dsp 1;
+#X obj 138 190 line~;
+#X obj 22 212 *~;
+#X obj 138 167 pack 0 50;
+#X text 34 159 audio;
+#X text 148 106 show level;
+#X obj 73 182 inlet~;
+#X obj 73 213 *~;
+#X obj 22 241 dac~ 1;
+#X obj 73 241 dac~ 2;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 23 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 21 0 27 1;
+#X connect 22 0 28 0;
+#X connect 23 0 21 0;
+#X connect 26 0 27 0;
+#X connect 27 0 29 0;
+#X restore 61 224 pd output;
+#X msg 152 196 MUTE;
+#X text 186 195 <-- output amplitude;
+#X obj 59 111 phasor~ 0;
+#X text 56 45 frequency;
+#X text 331 323 updated for Pd version 0.26;
+#X text 57 9 CLASSICAL PULSE WIDTH MODULATION;
+#X obj 131 110 phasor~ 0;
+#X obj 59 134 -~ 0.5;
+#X obj 131 85 + 0.5;
+#X obj 131 135 -~ 0.5;
+#X obj 131 160 *~ -1;
+#X text 34 262 Here's what happens if you take the previous patch but \, instead of subtracting the two sawtooth waves \, we put them in two speakers with opposite phase.;
+#X connect 0 0 9 0;
+#X connect 0 0 15 0;
+#X connect 5 0 6 2;
+#X connect 6 0 5 0;
+#X connect 7 0 6 3;
+#X connect 9 0 14 0;
+#X connect 13 0 16 0;
+#X connect 14 0 6 0;
+#X connect 15 0 13 0;
+#X connect 16 0 17 0;
+#X connect 17 0 6 1;
diff --git a/pd/doc/3.audio.examples/24.even.odd.pd b/pd/doc/3.audio.examples/24.even.odd.pd
new file mode 100644
index 00000000..bfa950a9
--- /dev/null
+++ b/pd/doc/3.audio.examples/24.even.odd.pd
@@ -0,0 +1,116 @@
+#N canvas 213 27 840 639 12;
+#X floatatom 45 74 0 0 0;
+#X obj 99 164 wrap~;
+#X graph graph1 0 -1.02 882 1.02 624 153 824 23;
+#X array phasor-output 882 float 0;
+#X pop;
+#X obj 45 102 phasor~ 0;
+#X text 81 73 frequency;
+#X obj 45 130 -~ 0.5;
+#X obj 99 192 -~ 0.5;
+#X obj 36 244 -~;
+#X obj 98 244 +~;
+#X graph graph1 0 -1.02 882 1.02 625 288 825 158;
+#X array wrap-output 882 float 0;
+#X pop;
+#X graph graph1 0 -1.02 882 1.02 626 423 826 293;
+#X array sum 882 float 0;
+#X pop;
+#X graph graph1 0 -1.02 882 1.02 626 563 826 433;
+#X array difference 882 float 0;
+#X pop;
+#X obj 172 166 tabwrite~ phasor-output;
+#X obj 172 218 tabwrite~ wrap-output;
+#X obj 172 282 tabwrite~ sum;
+#X obj 172 327 tabwrite~ difference;
+#X msg 172 120 bang;
+#X text 106 13 BUCHLA'S METHOD;
+#X floatatom 119 369 0 0 0;
+#N canvas 159 26 618 383 output 0;
+#X obj 393 156 t b;
+#X obj 393 106 f;
+#X obj 393 56 inlet;
+#X text 399 25 mute;
+#X obj 393 181 f;
+#X msg 480 174 0;
+#X msg 393 81 bang;
+#X obj 393 131 moses 1;
+#X obj 480 149 t b f;
+#X obj 452 113 moses 1;
+#X obj 138 144 dbtorms;
+#X obj 452 88 r master-lvl;
+#X obj 138 38 r master-lvl;
+#X obj 393 206 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 254 37 inlet;
+#X text 254 14 level;
+#X obj 254 96 s master-lvl;
+#X msg 151 61 set \$1;
+#X obj 151 85 outlet;
+#X msg 269 60 \; pd dsp 1;
+#X obj 138 190 line~;
+#X obj 22 212 *~;
+#X obj 138 167 pack 0 50;
+#X text 34 159 audio;
+#X text 148 106 show level;
+#X obj 73 182 inlet~;
+#X obj 73 213 *~;
+#X obj 22 241 dac~ 1;
+#X obj 73 241 dac~ 2;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 23 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 21 0 27 1;
+#X connect 22 0 28 0;
+#X connect 23 0 21 0;
+#X connect 26 0 27 0;
+#X connect 27 0 29 0;
+#X restore 81 392 pd output;
+#X msg 156 369 MUTE;
+#X text 13 430 A patch to split a sawtooth into even and odd harmonics
+ala Buchla. The wrap~ object folds its input into the interval [0 \,
+1) so taht \, for instance \, subtracting 1/5 from a phasor and wrapping
+it gives another phasor a half cycle out of phase frmo the original.
+Adding and subtracting the two give the results shown and heard. (Listen
+to the two outputs separately \, then together.);
+#X text 631 567 ---- 0.02 seconds ----;
+#X text 577 613 updated for Pd version 0.34;
+#X text 193 369 <-- output;
+#X connect 0 0 3 0;
+#X connect 1 0 6 0;
+#X connect 3 0 5 0;
+#X connect 5 0 1 0;
+#X connect 5 0 12 0;
+#X connect 5 0 8 0;
+#X connect 5 0 7 0;
+#X connect 6 0 13 0;
+#X connect 6 0 8 1;
+#X connect 6 0 7 1;
+#X connect 7 0 15 0;
+#X connect 7 0 19 0;
+#X connect 8 0 14 0;
+#X connect 8 0 19 1;
+#X connect 16 0 13 0;
+#X connect 16 0 14 0;
+#X connect 16 0 15 0;
+#X connect 16 0 12 0;
+#X connect 18 0 19 2;
+#X connect 19 0 18 0;
+#X connect 20 0 19 3;
diff --git a/pd/doc/3.audio.examples/25.bandlimited.pd b/pd/doc/3.audio.examples/25.bandlimited.pd
new file mode 100644
index 00000000..f49bcb68
--- /dev/null
+++ b/pd/doc/3.audio.examples/25.bandlimited.pd
@@ -0,0 +1,166 @@
+#N canvas 21 90 540 752 12;
+#X floatatom 183 91;
+#X obj 183 115 mtof;
+#X floatatom 340 183;
+#X obj 164 431 -~;
+#N canvas 391 51 561 806 tables 1;
+#X graph graph1 0 -1 1002 1 99 322 499 22;
+#X array array1 1002 float;
+#X pop;
+#X graph graph1 0 -1 882 1 96 684 496 384;
+#X array array2 882 float;
+#X pop;
+#X text 138 326 ---------------- 1002 samples ---------------;
+#X text 150 693 ---------------- 0.02 sec ---------------;
+#X restore 33 201 pd tables;
+#N canvas 104 390 728 408 make-table 0;
+#X obj 469 146 cos~;
+#X obj 303 146 cos~;
+#X obj 255 141 cos~;
+#X msg 199 210 bang;
+#X obj 255 229 tabwrite~ array1;
+#X text 366 79 period is 2000 samples \, twice the table length;
+#X msg 94 94 \; pd dsp 1;
+#X text 118 286 this network puts a half cycle of a band-limited square wave into the table "array1.";
+#X text 114 335 logically the half-cycle is in samples 1 through 1000 \; samples 0 and 1001 are provided so that the 4-point interpolation will work everywhere.;
+#X text 401 57 back the phase up one sample;
+#X msg 336 56 -0.0005;
+#X obj 171 16 loadbang;
+#X obj 303 120 *~ 3;
+#X obj 468 122 *~ 5;
+#X obj 303 171 *~ 0.33333;
+#X obj 468 172 *~ -0.2;
+#X obj 255 169 *~ -1;
+#X msg 171 38 bang;
+#X obj 254 80 phasor~ 22.05;
+#X obj 255 202 *~ 0.57692;
+#X connect 0 0 15 0;
+#X connect 1 0 14 0;
+#X connect 2 0 16 0;
+#X connect 3 0 4 0;
+#X connect 10 0 18 1;
+#X connect 11 0 17 0;
+#X connect 12 0 1 0;
+#X connect 13 0 0 0;
+#X connect 14 0 19 0;
+#X connect 15 0 19 0;
+#X connect 16 0 19 0;
+#X connect 17 0 10 0;
+#X connect 17 0 6 0;
+#X connect 17 0 3 0;
+#X connect 18 0 2 0;
+#X connect 18 0 12 0;
+#X connect 18 0 13 0;
+#X connect 19 0 4 0;
+#X restore 34 224 pd make-table;
+#X obj 183 163 sig~;
+#X obj 341 283 /~;
+#X obj 357 256 clip~ 1 999999;
+#X obj 183 218 phasor~;
+#X obj 196 301 *~;
+#X obj 196 325 clip~ -0.5 0.5;
+#X floatatom 183 139;
+#X obj 196 397 tabread4~ array1;
+#X floatatom 340 135;
+#X obj 340 206 * 0.33333;
+#X obj 340 159 mtof;
+#X text 374 110 band limit (MIDI units);
+#X text 220 78 pitch;
+#X obj 340 87 loadbang;
+#X msg 340 111 130;
+#X obj 340 230 sig~;
+#X text 246 139 frequency;
+#X obj 196 349 *~ 1000;
+#X obj 196 373 +~ 501;
+#X obj 183 242 -~ 0.5;
+#X floatatom 206 441;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 177 469 pd output;
+#X msg 235 441 MUTE;
+#X text 276 440 <-- output amplitude;
+#X text 27 503 Patch to make an approximately band-limited sawtooth. This is useful if you intend to use sawtooth generators above about 200 Hz. \, perhaps to use any of the techniques shown in the previous four patches.;
+#X text 28 562 We generate a perfect square wave at Nyquist/6 \; this will have partials 1 \, 3 \, and 5 \, but the Nyquist frequency at partial 6 will cut off the rest of the partials. This is stored in array1 using the "make-table" subpatch.;
+#X text 64 34 BAND-LIMITED SAWTOOTH GENERATOR;
+#X obj 43 459 tabwrite~ array2;
+#X msg 44 435 bang;
+#X text 28 632 Now any time we wish to make a discontinuity in the output signal \, we make it look exactly like the bandlimited square wave looks. We do this by reading through the table we recorded \, carefully adding a "digital" \, non-band-limited \, sawtooth to "array1" so that the discontinuities in the two cancel out and what you have left is the transition in the table.;
+#X text 338 737 updated for Pd version 0.26;
+#X text 41 409 graph output;
+#X connect 0 0 1 0;
+#X connect 1 0 12 0;
+#X connect 2 0 15 0;
+#X connect 3 0 27 0;
+#X connect 3 0 33 0;
+#X connect 6 0 8 0;
+#X connect 6 0 9 0;
+#X connect 7 0 10 1;
+#X connect 8 0 7 1;
+#X connect 9 0 25 0;
+#X connect 10 0 11 0;
+#X connect 11 0 23 0;
+#X connect 12 0 6 0;
+#X connect 13 0 3 0;
+#X connect 14 0 16 0;
+#X connect 15 0 21 0;
+#X connect 16 0 2 0;
+#X connect 19 0 20 0;
+#X connect 20 0 14 0;
+#X connect 21 0 7 0;
+#X connect 23 0 24 0;
+#X connect 24 0 13 0;
+#X connect 25 0 10 0;
+#X connect 25 0 3 1;
+#X connect 26 0 27 1;
+#X connect 27 0 26 0;
+#X connect 28 0 27 2;
+#X connect 34 0 33 0;
diff --git a/pd/doc/3.audio.examples/26.additive.pd b/pd/doc/3.audio.examples/26.additive.pd
new file mode 100644
index 00000000..9070b08b
--- /dev/null
+++ b/pd/doc/3.audio.examples/26.additive.pd
@@ -0,0 +1,92 @@
+#N canvas 59 99 696 468 12;
+#X floatatom 81 408 0 0 0;
+#N canvas 159 26 534 281 output 1;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X obj 425 153 t b;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 26 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 21 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 5 0;
+#X restore 43 435 pd output;
+#X msg 119 408 MUTE;
+#X text 153 407 <-- output amplitude;
+#X obj 43 377 catch~ sum;
+#X obj 381 288 s frequency;
+#X floatatom 381 264 0 0 0;
+#X floatatom 495 265 0 0 0;
+#X obj 495 289 s duration;
+#X msg 260 276 \; trigger bang;
+#X obj 45 221 partial 0.67 0.56 0 1;
+#X obj 45 245 partial 1 0.56 1 0.9;
+#X obj 45 269 partial 1 0.92 0 0.65;
+#X obj 45 294 partial 1.8 0.94 0 0.55;
+#X floatatom 495 217 0 0 0;
+#X obj 495 241 * 100;
+#X obj 381 240 mtof;
+#X floatatom 381 216 0 0 0;
+#X text 87 15 ADDITIVE SYNTHESIS;
+#X text 32 73 This patch demonstrates using an abstraction \, "partial"
+\, to make a simple additive synthesis instrument.;
+#X text 28 114 Partial takes as arguments an amplitude \, a relative
+frequency \, a detuning frequency \, and a relative duration. You set
+absolute duration and pitch using the controls below. Hit hte trigger
+to make sound.;
+#X text 460 435 updated for Pd version 0.26;
+#X text 535 207 duration in tenths;
+#X text 535 222 of a second;
+#X text 419 215 pitch;
+#X text 251 253 click to start;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 4 0 1 0;
+#X connect 6 0 5 0;
+#X connect 7 0 8 0;
+#X connect 14 0 15 0;
+#X connect 15 0 7 0;
+#X connect 16 0 6 0;
+#X connect 17 0 16 0;
diff --git a/pd/doc/3.audio.examples/27.PART4.samplers.pd b/pd/doc/3.audio.examples/27.PART4.samplers.pd
new file mode 100644
index 00000000..e7aee0cd
--- /dev/null
+++ b/pd/doc/3.audio.examples/27.PART4.samplers.pd
@@ -0,0 +1,111 @@
+#N canvas 11 3 930 640 12;
+#X msg 306 14 \; pd dsp 1;
+#X msg 391 14 \; pd dsp 0;
+#X text 327 45 ON;
+#X text 408 45 OFF;
+#X floatatom 75 275 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 46 303 pd output;
+#X msg 114 273 MUTE;
+#X text 148 272 <-- output amplitude;
+#X obj 46 239 hip~ 5;
+#X text 102 240 high pass filter to cut DC;
+#X graph graph1 0 -1.02 44103 1.02 594 172 794 42;
+#X array sample-table 44104 float 0;
+#X pop;
+#X obj 46 207 tabread4~ sample-table;
+#X obj 46 172 line~;
+#X obj 46 123 * 441;
+#X floatatom 46 69 0 0 0;
+#X obj 46 147 pack 0 100;
+#X text 102 13 SCRATCH MACHINE;
+#X text 81 70 <-- read point in 100ths of a second;
+#X text 103 123 convert to SAMPLES (441 samples in 0.01 sec);
+#X obj 414 257 loadbang;
+#X text 248 195 read from the table;
+#X text 239 213 (the input is the index in samples);
+#X text 23 516 For more on reading and writing soundfiles to tables
+\, setting their lengths \, etc \, see "arrays" in the "control examples"
+series.;
+#X text 20 385 This patch introduces the "tabread4~" object \, which
+reads audio samples out of a floating-point array \, often called a
+"sample table." The input is the index of the sample to read \, counting
+from zero. The output is calculated using 4-point cubic interpolation
+\, which is adequate for most purposes. Because of the interpolation
+scheme \, tabread4~'s input cannot be less than one or greater than
+the table length minus two.;
+#X text 22 565 Fanatics take note: if you want really high-fidelity
+sampling \, use a high-quality resampling program to up-sample your
+soundfile to 88200 to drastically reduce interpolation error.;
+#X text 600 195 (one second plus three extra;
+#X text 602 214 for 4-point interpolation);
+#X text 407 330 message to read a soundfile into the table (automatically
+sent when you load this patch by the "loadbang" object.);
+#X text 93 172 convert smoothly to audio signal;
+#X text 93 84 (range is 0-100.) YOU ONLY HEAR OUTPUT;
+#X text 94 100 WHEN THIS IS 0-100 AND ACTIVELY CHANGING.;
+#X text 605 611 updated for Pd version 0.33;
+#X text 593 173 --- 44103 samples ---;
+#X msg 414 281 read ../sound/voice.wav sample-table;
+#X obj 414 306 soundfiler;
+#X connect 4 0 5 1;
+#X connect 5 0 4 0;
+#X connect 6 0 5 2;
+#X connect 8 0 5 0;
+#X connect 11 0 8 0;
+#X connect 12 0 11 0;
+#X connect 13 0 15 0;
+#X connect 14 0 13 0;
+#X connect 15 0 12 0;
+#X connect 19 0 33 0;
+#X connect 33 0 34 0;
diff --git a/pd/doc/3.audio.examples/28.sampler.loop.pd b/pd/doc/3.audio.examples/28.sampler.loop.pd
new file mode 100644
index 00000000..5d2687a6
--- /dev/null
+++ b/pd/doc/3.audio.examples/28.sampler.loop.pd
@@ -0,0 +1,124 @@
+#N canvas 143 17 993 674 12;
+#X graph graph1 0 -1.02 44100 1.02 630 370 830 240;
+#X array tabread4-out 44100 float 0;
+#X pop;
+#X msg 354 13 \; pd dsp 1;
+#X msg 437 13 \; pd dsp 0;
+#X text 379 51 ON;
+#X text 457 51 OFF;
+#X graph graph1 0 -1.02 44103 1.02 631 144 831 14;
+#X array sample-table 44103 float 0;
+#X pop;
+#X obj 564 556 loadbang;
+#X obj 92 318 tabwrite~ tabread4-out;
+#X floatatom 90 396 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 406 192 t b;
+#X obj 406 132 f;
+#X obj 406 72 inlet;
+#X text 413 35 mute;
+#X obj 406 222 f;
+#X msg 510 214 0;
+#X msg 406 102 bang;
+#X obj 406 162 moses 1;
+#X obj 510 184 t b f;
+#X obj 476 140 moses 1;
+#X obj 100 178 dbtorms;
+#X obj 476 110 r master-lvl;
+#X obj 100 50 r master-lvl;
+#X obj 406 252 s master-lvl;
+#X obj 26 217 inlet~;
+#X obj 239 49 inlet;
+#X text 239 22 level;
+#X obj 239 120 s master-lvl;
+#X msg 115 78 set \$1;
+#X obj 115 107 outlet;
+#X msg 257 77 \; pd dsp 1;
+#X obj 100 233 line~;
+#X obj 26 254 *~;
+#X obj 26 289 dac~;
+#X obj 100 205 pack 0 50;
+#X text 24 190 audio;
+#X text 112 132 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 55 430 pd output;
+#X msg 125 396 MUTE;
+#X text 166 395 <-- output amplitude;
+#X obj 55 361 hip~ 5;
+#X obj 55 248 tabread4~ sample-table;
+#X floatatom 55 60 0 0 0;
+#X text 254 251 read from the table;
+#X text 110 16 LOOPING SAMPLER;
+#X text 104 60 <-- frequency (Hz.);
+#X floatatom 83 121 0 0 0;
+#X obj 83 154 * 441;
+#X obj 55 181 *~ 0;
+#X obj 55 211 +~ 1;
+#X text 136 286 <-- click to display output;
+#X obj 55 92 phasor~ 0;
+#X msg 91 283 bang;
+#X text 130 120 <-- chunk size (100ths of a second);
+#X text 25 460 This is a looping sampler in which you specify the number
+of loops per second (the frequency) and the size of the chunk to loop.
+If the frequency is less than about 20 \, you will hear repetition
+and the chunk size will sound like transposition. For frequencies above
+50 or so \, you hear a tone whose timbre is controlled by the chunk
+size (best kept below 5 or so.) Remember to use the "shift" key on
+number boxes to make fine adjustments.;
+#X obj 558 426 adc~ 1;
+#X obj 558 455 hip~ 5;
+#X obj 558 515 tabwrite~ sample-table;
+#X msg 576 482 bang;
+#X text 614 483 <-- click here to record your own sample;
+#X text 674 561 v-- re-read the original sample;
+#X text 23 597 In this patch you will frequently hear discontinuities
+at the looping point. If you're working in a studio \, you can sometimes
+find "good" loop points for samples. Another approach \, better for
+live situations \, is shown in the next patch.;
+#X text 101 180 <-- readjust phase for range 0 - (chunk size);
+#X text 100 211 <-- add one to avoid beginning of table;
+#X msg 564 584 read ../sound/voice.wav sample-table;
+#X obj 564 609 soundfiler;
+#X text 748 653 updated for Pd version 0.33;
+#X text 629 153 ---- 44103 samples ----;
+#X text 641 376 ---- 1 second ------;
+#X connect 6 0 36 0;
+#X connect 8 0 9 1;
+#X connect 9 0 8 0;
+#X connect 10 0 9 2;
+#X connect 12 0 9 0;
+#X connect 13 0 12 0;
+#X connect 13 0 7 0;
+#X connect 14 0 23 0;
+#X connect 18 0 19 0;
+#X connect 19 0 20 1;
+#X connect 20 0 21 0;
+#X connect 21 0 13 0;
+#X connect 23 0 20 0;
+#X connect 24 0 7 0;
+#X connect 27 0 28 0;
+#X connect 28 0 29 0;
+#X connect 30 0 29 0;
+#X connect 36 0 37 0;
diff --git a/pd/doc/3.audio.examples/29.sampler.loop.smooth.pd b/pd/doc/3.audio.examples/29.sampler.loop.smooth.pd
new file mode 100644
index 00000000..52551163
--- /dev/null
+++ b/pd/doc/3.audio.examples/29.sampler.loop.smooth.pd
@@ -0,0 +1,148 @@
+#N canvas 75 15 1014 733 12;
+#X graph graph1 0 -1.02 44100 1.02 764 446 964 316;
+#X array cos-output 44100 float 0;
+#X pop;
+#X graph graph1 0 -1.02 44103 1.02 761 148 961 18;
+#X array sample-table 44103 float 0;
+#X pop;
+#X obj 593 579 loadbang;
+#X floatatom 96 489 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 67 517 pd output;
+#X msg 133 490 MUTE;
+#X text 176 490 <-- output amplitude;
+#X obj 67 460 hip~ 5;
+#X obj 47 408 tabread4~ sample-table;
+#X floatatom 46 50 0 0 0;
+#X text 85 49 <-- frequency (Hz.);
+#X floatatom 97 358 0 0 0;
+#X obj 97 385 * 441;
+#X obj 47 358 *~ 0;
+#X obj 47 383 +~ 1;
+#X text 271 274 <-- click to display output;
+#X msg 229 275 bang;
+#X text 136 357 <-- chunk size (100ths of a second);
+#X obj 588 471 adc~ 1;
+#X obj 588 495 hip~ 5;
+#X obj 588 545 tabwrite~ sample-table;
+#X msg 603 518 bang;
+#X text 635 518 <-- click here to record your own sample;
+#X text 704 583 v-- re-read the original sample;
+#X text 40 9 ENVELOPING YOUR LOOPING SAMPLER;
+#X obj 88 101 -~ 0.5;
+#X obj 88 259 clip~ -0.5 0.5;
+#X obj 88 237 *~ 1;
+#X graph graph1 0 -1.02 44100 1.02 763 311 963 181;
+#X array cos-input 44100 float 0;
+#X pop;
+#X obj 229 303 tabwrite~ cos-input;
+#X obj 88 284 cos~;
+#X obj 88 126 wrap~;
+#X obj 88 155 -~ 0.5;
+#X obj 88 332 *~ -0.5;
+#X obj 88 307 -~ 1;
+#X obj 229 329 tabwrite~ cos-output;
+#X floatatom 119 187 0 0 0;
+#X obj 67 433 *~;
+#X text 157 98 subtracting 0.5 and wrapping produces a sawtooth wave
+180 degrees out of phase from the original.;
+#X text 153 150 as before we subtract 1/2 again to center the sawtooth
+from -1/2 to 1/2.;
+#X text 153 188 <-- sharpness (at least 1);
+#X msg 593 608 read ../sound/voice.wav sample-table;
+#X obj 593 633 soundfiler;
+#X text 776 150 -- 44103 samples ---;
+#X text 767 447 ----- 1 second ------;
+#X obj 119 211 max 1;
+#X obj 46 77 phasor~;
+#X text 24 560 Here we apply an amplitude envelope to protect against
+discontinuities at the loop point. The envelope is based on the pulse
+width modulation example \, except that the widening is applied to
+the "1" part of the pulse \, not the "0" part.;
+#X text 23 647 To see the envelope \, put the phasor on 2 Hz \, select
+"sharpness" values between 1 and 3 \, and look at "cos-input" and "cos-output."
+You should both see and hear the effect of the "sharpness" parameter.
+;
+#X text 734 688 updated for Pd version 0.34;
+#X connect 2 0 41 0;
+#X connect 3 0 4 1;
+#X connect 4 0 3 0;
+#X connect 5 0 4 2;
+#X connect 7 0 4 0;
+#X connect 8 0 37 0;
+#X connect 9 0 46 0;
+#X connect 11 0 12 0;
+#X connect 12 0 13 1;
+#X connect 13 0 14 0;
+#X connect 14 0 8 0;
+#X connect 16 0 29 0;
+#X connect 16 0 35 0;
+#X connect 18 0 19 0;
+#X connect 19 0 20 0;
+#X connect 21 0 20 0;
+#X connect 25 0 31 0;
+#X connect 26 0 30 0;
+#X connect 26 0 29 0;
+#X connect 27 0 26 0;
+#X connect 30 0 34 0;
+#X connect 31 0 32 0;
+#X connect 32 0 27 0;
+#X connect 33 0 35 0;
+#X connect 33 0 37 1;
+#X connect 34 0 33 0;
+#X connect 36 0 45 0;
+#X connect 37 0 7 0;
+#X connect 41 0 42 0;
+#X connect 45 0 27 1;
+#X connect 46 0 13 0;
+#X connect 46 0 25 0;
diff --git a/pd/doc/3.audio.examples/30.sampler.scratch.pd b/pd/doc/3.audio.examples/30.sampler.scratch.pd
new file mode 100644
index 00000000..d4602050
--- /dev/null
+++ b/pd/doc/3.audio.examples/30.sampler.scratch.pd
@@ -0,0 +1,175 @@
+#N canvas 33 19 1046 749 12;
+#X msg 354 13 \; pd dsp 1;
+#X msg 437 13 \; pd dsp 0;
+#X text 379 51 ON;
+#X text 457 51 OFF;
+#X graph graph1 0 -1.02 44100 1.02 757 142 957 12;
+#X array sample-table 44103 float 0;
+#X pop;
+#X floatatom 71 466 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 406 192 t b;
+#X obj 406 132 f;
+#X obj 406 72 inlet;
+#X text 413 35 mute;
+#X obj 406 222 f;
+#X msg 510 214 0;
+#X msg 406 102 bang;
+#X obj 406 162 moses 1;
+#X obj 510 184 t b f;
+#X obj 476 140 moses 1;
+#X obj 100 178 dbtorms;
+#X obj 476 110 r master-lvl;
+#X obj 100 50 r master-lvl;
+#X obj 406 252 s master-lvl;
+#X obj 26 217 inlet~;
+#X obj 239 49 inlet;
+#X text 239 22 level;
+#X obj 239 120 s master-lvl;
+#X msg 115 78 set \$1;
+#X obj 115 107 outlet;
+#X msg 257 77 \; pd dsp 1;
+#X obj 100 233 line~;
+#X obj 26 254 *~;
+#X obj 26 289 dac~;
+#X obj 100 205 pack 0 50;
+#X text 24 190 audio;
+#X text 112 132 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 36 499 pd output;
+#X msg 106 466 MUTE;
+#X text 145 465 <-- output amplitude;
+#X obj 36 427 hip~ 5;
+#X obj 55 368 tabread4~ sample-table;
+#X floatatom 55 60 0 0 0;
+#X text 102 59 <-- frequency (Hz.);
+#X floatatom 109 152 0 0 0;
+#X obj 109 181 * 441;
+#X obj 55 152 *~ 0;
+#X obj 55 181 +~ 1;
+#X obj 55 89 phasor~ 0;
+#X msg 172 246 bang;
+#X text 156 151 <-- chunk size (100ths of a second);
+#X obj 598 451 adc~ 1;
+#X obj 598 480 hip~ 5;
+#X obj 598 540 tabwrite~ sample-table;
+#X msg 616 508 bang;
+#X text 654 507 <-- click here to record your own sample;
+#X text 64 17 ENVELOPING YOUR LOOPING SAMPLER;
+#X graph graph2 0 0 44100 44100 760 345 960 215;
+#X array table-index 44100 float 0;
+#X pop;
+#X obj 172 308 tabwrite~ table-index;
+#X obj 36 398 *~;
+#N canvas 160 476 591 415 envelope 0;
+#X obj 97 82 -~ 0.5;
+#X obj 100 331 clip~ -0.5 0.5;
+#X obj 100 305 *~ 1;
+#X obj 152 272 moses 1;
+#X msg 115 272 1;
+#X obj 100 361 cos~;
+#X obj 97 112 wrap~;
+#X obj 97 146 -~ 0.5;
+#X obj 100 419 *~ -0.5;
+#X obj 100 389 -~ 1;
+#X floatatom 152 234 0 0 0;
+#X text 162 78 subtracting 0.5 and wrapping produces a sawtooth wave
+180 degrees out of phase from the original.;
+#X text 156 139 as before we subtract 1/2 again to center the sawtooth
+from -1/2 to 1/2.;
+#X text 212 234 <-- sharpness (at least 1);
+#X obj 97 50 inlet~;
+#X obj 100 455 outlet~;
+#X obj 152 208 inlet;
+#X connect 0 0 6 0;
+#X connect 1 0 5 0;
+#X connect 2 0 1 0;
+#X connect 3 0 4 0;
+#X connect 3 1 2 1;
+#X connect 4 0 2 1;
+#X connect 5 0 9 0;
+#X connect 6 0 7 0;
+#X connect 7 0 2 0;
+#X connect 8 0 15 0;
+#X connect 9 0 8 0;
+#X connect 10 0 3 0;
+#X connect 14 0 0 0;
+#X connect 16 0 10 0;
+#X restore 36 122 pd envelope;
+#X floatatom 146 124 0 0 0;
+#X text 187 123 <-- envelope sharpness;
+#X obj 74 304 line~;
+#X obj 74 246 * 441;
+#X floatatom 174 215 0 0 0;
+#X obj 74 275 pack 0 100;
+#X text 216 214 <-- read point in 100ths of a second;
+#X obj 55 338 +~;
+#X text 31 529 In this patch we can loop in any "window" of the input
+sample. The "read point" (0-100) gives the starting point of the window
+and "chunk" is its size (both in 100ths of a second.) Try \, for example
+\, frequency 4 \, sharpness 10 \, chunk size 25 \, and vary the read
+point from -25 to 100 \, listening to the result.;
+#X text 220 246 <-- graph table index;
+#X text 34 627 You should hear some doppler shift as you change the
+read point. To see why \, click on "graph table index" and quickly
+start changing the read point--- you should see entertaining pictures
+in "table-index". THe next patch shows how to prevent this if you wish
+to.;
+#X text 763 356 ----- 1 second ------;
+#X obj 602 583 loadbang;
+#X text 711 597 v-- re-read the original sample;
+#X msg 602 670 \; graph2 ylabel 48000 0 44100;
+#X msg 662 619 read ../sound/voice.wav sample-table;
+#X obj 662 643 soundfiler;
+#X text 766 719 updated for Pd version 0.33;
+#X text 755 151 ---- 44103 samples ---;
+#X connect 5 0 6 1;
+#X connect 6 0 5 0;
+#X connect 7 0 6 2;
+#X connect 9 0 6 0;
+#X connect 10 0 28 1;
+#X connect 11 0 17 0;
+#X connect 13 0 14 0;
+#X connect 14 0 15 1;
+#X connect 15 0 16 0;
+#X connect 16 0 37 0;
+#X connect 17 0 15 0;
+#X connect 17 0 29 0;
+#X connect 18 0 27 0;
+#X connect 20 0 21 0;
+#X connect 21 0 22 0;
+#X connect 23 0 22 0;
+#X connect 28 0 9 0;
+#X connect 29 0 28 0;
+#X connect 30 0 29 1;
+#X connect 32 0 37 1;
+#X connect 33 0 35 0;
+#X connect 34 0 33 0;
+#X connect 35 0 32 0;
+#X connect 37 0 27 0;
+#X connect 37 0 10 0;
+#X connect 42 0 44 0;
+#X connect 42 0 45 0;
+#X connect 45 0 46 0;
diff --git a/pd/doc/3.audio.examples/31.sampler.nodoppler.pd b/pd/doc/3.audio.examples/31.sampler.nodoppler.pd
new file mode 100644
index 00000000..fca63bb9
--- /dev/null
+++ b/pd/doc/3.audio.examples/31.sampler.nodoppler.pd
@@ -0,0 +1,180 @@
+#N canvas 113 57 1004 644 12;
+#X msg 409 9 \; pd dsp 1;
+#X msg 495 10 \; pd dsp 0;
+#X text 430 40 ON;
+#X text 511 41 OFF;
+#X graph graph1 0 -1.02 44100 1.02 631 140 831 10;
+#X array sample-table 44103 float 0;
+#X pop;
+#X obj 586 483 loadbang;
+#X floatatom 60 462 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 31 490 pd output;
+#X msg 89 462 MUTE;
+#X text 124 455 <-- output amplitude;
+#X obj 31 430 hip~ 5;
+#X obj 47 381 tabread4~ sample-table;
+#X floatatom 46 50 0 0 0;
+#X text 85 49 <-- frequency (Hz.);
+#X floatatom 165 122 0 0 0;
+#X obj 164 145 * 441;
+#X obj 46 220 +~ 1;
+#X obj 46 74 phasor~ 0;
+#X msg 151 262 bang;
+#X text 204 121 <-- chunk size (100ths of a second);
+#X obj 580 379 adc~ 1;
+#X obj 580 403 hip~ 5;
+#X obj 580 453 tabwrite~ sample-table;
+#X msg 595 426 bang;
+#X text 627 425 <-- click here to record your own sample;
+#X text 695 497 v-- re-read the original sample;
+#X graph graph2 0 0 44100 44100 633 309 833 179;
+#X array table-index 44100 float 0;
+#X pop;
+#X obj 151 354 tabwrite~ table-index;
+#X obj 31 406 *~;
+#N canvas 160 476 591 415 envelope 0;
+#X obj 81 68 -~ 0.5;
+#X obj 83 276 clip~ -0.5 0.5;
+#X obj 83 254 *~ 1;
+#X obj 127 227 moses 1;
+#X msg 96 227 1;
+#X obj 83 301 cos~;
+#X obj 81 93 wrap~;
+#X obj 81 122 -~ 0.5;
+#X obj 83 349 *~ -0.5;
+#X obj 83 324 -~ 1;
+#X floatatom 127 195 0 0 0;
+#X text 135 65 subtracting 0.5 and wrapping produces a sawtooth wave
+180 degrees out of phase from the original.;
+#X text 130 116 as before we subtract 1/2 again to center the sawtooth
+from -1/2 to 1/2.;
+#X text 177 195 <-- sharpness (at least 1);
+#X obj 81 42 inlet~;
+#X obj 83 379 outlet~;
+#X obj 127 173 inlet;
+#X connect 0 0 6 0;
+#X connect 1 0 5 0;
+#X connect 2 0 1 0;
+#X connect 3 0 4 0;
+#X connect 3 1 2 1;
+#X connect 4 0 2 1;
+#X connect 5 0 9 0;
+#X connect 6 0 7 0;
+#X connect 7 0 2 0;
+#X connect 8 0 15 0;
+#X connect 9 0 8 0;
+#X connect 10 0 3 0;
+#X connect 14 0 0 0;
+#X connect 16 0 10 0;
+#X restore 30 102 pd envelope;
+#X floatatom 149 84 0 0 0;
+#X text 183 83 <-- envelope sharpness;
+#X obj 65 308 line~;
+#X obj 65 260 * 441;
+#X floatatom 150 238 0 0 0;
+#X obj 65 284 pack 0 100;
+#X text 185 237 <-- read point in 100ths of a second;
+#X obj 47 356 +~;
+#X text 191 262 <-- graph table index;
+#X obj 65 332 samphold~;
+#X text 26 522 This example differs from the previous one in having
+samphold~ objects which allow the chunk size and especially the read
+point to change only at points where the phase wraps around. This removes
+signal discontinuities (when the chunk size changes) and doppler shift
+for changing read point.;
+#X obj 99 195 samphold~;
+#X obj 164 170 sig~;
+#X obj 47 196 *~;
+#X text 229 192 explicitly to use samphold~...;
+#X text 200 173 <-- you often have to convert to audio;
+#X text 643 315 ----- 1 second ------;
+#X text 631 144 ---- 44103 samples ---;
+#X msg 586 570 \; graph2 ylabel 48000 0 44100;
+#X text 739 621 updated for Pd version 0.33;
+#X msg 646 519 read ../sound/voice.wav sample-table;
+#X obj 646 543 soundfiler;
+#X text 21 8 SLIDING STABLE LOOPS WITHOUT DOPPLER SHIFT;
+#X connect 5 0 48 0;
+#X connect 5 0 50 0;
+#X connect 6 0 7 1;
+#X connect 7 0 6 0;
+#X connect 8 0 7 2;
+#X connect 10 0 7 0;
+#X connect 11 0 28 1;
+#X connect 12 0 17 0;
+#X connect 14 0 15 0;
+#X connect 15 0 42 0;
+#X connect 16 0 37 0;
+#X connect 17 0 29 0;
+#X connect 17 0 39 1;
+#X connect 17 0 41 1;
+#X connect 17 0 43 0;
+#X connect 18 0 27 0;
+#X connect 20 0 21 0;
+#X connect 21 0 22 0;
+#X connect 23 0 22 0;
+#X connect 28 0 10 0;
+#X connect 29 0 28 0;
+#X connect 30 0 29 1;
+#X connect 32 0 39 0;
+#X connect 33 0 35 0;
+#X connect 34 0 33 0;
+#X connect 35 0 32 0;
+#X connect 37 0 27 0;
+#X connect 37 0 11 0;
+#X connect 39 0 37 1;
+#X connect 41 0 43 1;
+#X connect 42 0 41 0;
+#X connect 43 0 16 0;
+#X connect 50 0 51 0;
diff --git a/pd/doc/3.audio.examples/32.sampler.transpose.pd b/pd/doc/3.audio.examples/32.sampler.transpose.pd
new file mode 100644
index 00000000..02d937f4
--- /dev/null
+++ b/pd/doc/3.audio.examples/32.sampler.transpose.pd
@@ -0,0 +1,207 @@
+#N canvas 50 103 992 682 12;
+#X graph graph1 0 -1.02 44100 1.02 599 426 799 296;
+#X array sample-table 44103 float 0;
+#X pop;
+#X obj 502 568 loadbang;
+#X floatatom 88 616 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 50 641 pd output;
+#X msg 126 614 MUTE;
+#X text 168 613 <-- output amplitude;
+#X obj 50 586 hip~ 5;
+#X obj 50 510 tabread4~ sample-table;
+#X floatatom 16 306 0 0 0;
+#X obj 16 331 * 441;
+#X obj 16 453 +~ 1;
+#X obj 35 249 phasor~ 0;
+#X text 57 303 <-- chunk size (100ths of a second);
+#X obj 497 460 adc~ 1;
+#X obj 497 485 hip~ 5;
+#X obj 497 534 tabwrite~ sample-table;
+#X msg 512 507 bang;
+#X text 544 506 <-- click here to record your own sample;
+#X text 595 575 v-- re-read the original sample;
+#X obj 50 561 *~;
+#X text 599 432 -------- 44103 samples ------;
+#N canvas 160 476 591 415 envelope 0;
+#X obj 81 68 -~ 0.5;
+#X obj 83 276 clip~ -0.5 0.5;
+#X obj 83 254 *~ 1;
+#X obj 127 227 moses 1;
+#X msg 96 227 1;
+#X obj 83 301 cos~;
+#X obj 81 93 wrap~;
+#X obj 81 122 -~ 0.5;
+#X obj 83 349 *~ -0.5;
+#X obj 83 324 -~ 1;
+#X floatatom 127 195 0 0 0;
+#X text 135 65 subtracting 0.5 and wrapping produces a sawtooth wave
+180 degrees out of phase from the original.;
+#X text 130 116 as before we subtract 1/2 again to center the sawtooth
+from -1/2 to 1/2.;
+#X text 177 195 <-- sharpness (at least 1);
+#X obj 81 42 inlet~;
+#X obj 83 379 outlet~;
+#X obj 127 173 inlet;
+#X connect 0 0 6 0;
+#X connect 1 0 5 0;
+#X connect 2 0 1 0;
+#X connect 3 0 4 0;
+#X connect 3 1 2 1;
+#X connect 4 0 2 1;
+#X connect 5 0 9 0;
+#X connect 6 0 7 0;
+#X connect 7 0 2 0;
+#X connect 8 0 15 0;
+#X connect 9 0 8 0;
+#X connect 10 0 3 0;
+#X connect 14 0 0 0;
+#X connect 16 0 10 0;
+#X restore 88 560 pd envelope;
+#X floatatom 179 535 0 0 0;
+#X text 212 534 <-- envelope sharpness;
+#X obj 112 456 line~;
+#X obj 112 406 * 441;
+#X floatatom 112 381 0 0 0;
+#X obj 112 431 pack 0 100;
+#X text 158 383 <-- read point in 100ths of a second;
+#X obj 50 485 +~;
+#X obj 112 481 samphold~;
+#X obj 16 381 samphold~;
+#X obj 16 356 sig~;
+#X obj 16 428 *~;
+#X text 18 5 CALCULATING LOOP FREQUENCY AS FUNCTION OF TRANSPOSITION
+;
+#X obj 88 535 r~ phase;
+#X obj 35 274 s~ phase;
+#X obj 74 356 r~ phase;
+#X obj 32 403 r~ phase;
+#X obj 170 457 r~ phase;
+#X obj 157 351 s chunk-size;
+#X floatatom 22 50 0 0 0;
+#X text 66 49 <-- transposition (10ths of a halftone);
+#X obj 22 75 / 120;
+#X obj 36 149 pow;
+#X obj 22 100 t b f;
+#X msg 22 125 2;
+#X text 86 103 2 to the power (octaves);
+#X text 85 125 gives speed change for the;
+#X text 85 148 desired transposition;
+#X obj 157 326 * 0.01;
+#X text 270 339 chunk size;
+#X text 270 361 in seconds;
+#X obj 46 175 r chunk-size;
+#X obj 46 200 t b f;
+#X obj 35 224 /;
+#X text 105 201 divide speed change by chunk;
+#X text 103 222 size to get loop frequency;
+#X text 402 74 The transposition is frequency in Hz. divided by chunk
+size in seconds. This patch calculates the loop frequency as a function
+of desired transposition;
+#X text 77 75 transposition in octaves;
+#X text 404 124 Notice now that we get Doppler effects when the chunk
+size changes. You can suppress that if you don't want it \, by converting
+the chunk size to an audio signal \, sampling and holding it. But then
+there would be more work to deal with very low frequencies never triggering
+the sample and hold...;
+#X text 403 208 You might also want to have a way to retrigger the
+loop to sync it with some other process. By the time we had all this
+built the patch would be fairly involved. For now \, we'll move on
+to note-oriented samplers instead.;
+#X text 665 650 updated for Pd version 0.33;
+#X msg 502 595 read ../sound/voice.wav sample-table;
+#X obj 502 622 soundfiler;
+#X obj 21 28 loadbang;
+#X connect 1 0 63 0;
+#X connect 2 0 3 1;
+#X connect 3 0 2 0;
+#X connect 4 0 3 2;
+#X connect 6 0 3 0;
+#X connect 7 0 19 0;
+#X connect 8 0 9 0;
+#X connect 8 0 50 0;
+#X connect 9 0 32 0;
+#X connect 10 0 29 0;
+#X connect 11 0 36 0;
+#X connect 13 0 14 0;
+#X connect 14 0 15 0;
+#X connect 16 0 15 0;
+#X connect 19 0 6 0;
+#X connect 21 0 19 1;
+#X connect 22 0 21 1;
+#X connect 24 0 30 0;
+#X connect 25 0 27 0;
+#X connect 26 0 25 0;
+#X connect 27 0 24 0;
+#X connect 29 0 7 0;
+#X connect 30 0 29 1;
+#X connect 31 0 33 0;
+#X connect 32 0 31 0;
+#X connect 33 0 10 0;
+#X connect 35 0 21 0;
+#X connect 37 0 31 1;
+#X connect 38 0 33 1;
+#X connect 39 0 30 1;
+#X connect 41 0 43 0;
+#X connect 43 0 45 0;
+#X connect 44 0 55 0;
+#X connect 45 0 46 0;
+#X connect 45 1 44 1;
+#X connect 46 0 44 0;
+#X connect 50 0 40 0;
+#X connect 53 0 54 0;
+#X connect 54 0 55 0;
+#X connect 54 1 55 1;
+#X connect 55 0 11 0;
+#X connect 63 0 64 0;
+#X connect 65 0 41 0;
diff --git a/pd/doc/3.audio.examples/33.sampler.oneshot.pd b/pd/doc/3.audio.examples/33.sampler.oneshot.pd
new file mode 100644
index 00000000..75ce9153
--- /dev/null
+++ b/pd/doc/3.audio.examples/33.sampler.oneshot.pd
@@ -0,0 +1,143 @@
+#N canvas 34 0 994 773 12;
+#X msg 538 26 \; pd dsp 1;
+#X msg 622 26 \; pd dsp 0;
+#X text 563 64 ON;
+#X text 641 64 OFF;
+#X graph graph1 0 -1.02 176403 1.02 746 290 946 160;
+#X array sample-table 176403 float 0;
+#X pop;
+#X obj 583 520 loadbang;
+#X floatatom 72 407 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 406 192 t b;
+#X obj 406 132 f;
+#X obj 406 72 inlet;
+#X text 413 35 mute;
+#X obj 406 222 f;
+#X msg 510 214 0;
+#X msg 406 102 bang;
+#X obj 406 162 moses 1;
+#X obj 510 184 t b f;
+#X obj 476 140 moses 1;
+#X obj 100 178 dbtorms;
+#X obj 476 110 r master-lvl;
+#X obj 100 50 r master-lvl;
+#X obj 406 252 s master-lvl;
+#X obj 26 217 inlet~;
+#X obj 239 49 inlet;
+#X text 239 22 level;
+#X obj 239 120 s master-lvl;
+#X msg 115 78 set \$1;
+#X obj 115 107 outlet;
+#X msg 257 77 \; pd dsp 1;
+#X obj 100 233 line~;
+#X obj 26 254 *~;
+#X obj 26 289 dac~;
+#X obj 100 205 pack 0 50;
+#X text 24 190 audio;
+#X text 112 132 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 37 440 pd output;
+#X msg 107 407 MUTE;
+#X text 157 406 <-- output amplitude;
+#X obj 37 374 hip~ 5;
+#X obj 37 288 tabread4~ sample-table;
+#X obj 593 379 adc~ 1;
+#X obj 593 409 hip~ 5;
+#X obj 564 473 tabwrite~ sample-table;
+#X msg 564 340 bang;
+#X text 697 525 v-- re-read the original sample;
+#X text 18 15 ONE-SHOT SAMPLER USING LINE~ AS PHASE;
+#X obj 37 258 line~;
+#X obj 77 340 line~;
+#X obj 37 340 *~;
+#X obj 77 313 r cutoff;
+#X obj 37 228 r phase;
+#X msg 130 161 \; phase 1 \, 4.41e+08 1e+07 \; cutoff 1 5;
+#X msg 30 71 bang;
+#X obj 130 126 delay 5;
+#X text 83 71 <-- play the sample;
+#X msg 30 162 \; cutoff 0 5;
+#X text 40 119 cut the;
+#X text 40 138 sound off;
+#X text 210 111 Wait for the;
+#X text 208 131 cutoff to finish;
+#X text 355 155 set the upper line~ to start;
+#X text 355 174 at the first sample and go;
+#X text 354 195 forever (until the next trigger);
+#X text 24 479 In this example we're using a line~ object in place
+of the phaser~-based network that controls read location.;
+#X text 24 520 To start a note \, first we have to mute the output
+in case ther's already something playing---otherwise we'll get a click.
+The "cutoff" line~ then takes 5 msec to get to zero. After that amount
+of delay \, we reset the phase to sample number 1 and set it in motion.
+We want the line~ output to increase by 1 each sample of output \,
+so we ask for it to do 4.41e+08 samples in 1e+07 milliseconds.;
+#X text 24 636 The cutoff mechanism is still safe if we happen to ask
+for two notes in under 5 msec. The second request would reset the delay
+\, so that there's no way the delay can possibly fire without the cutoff
+line~ at zero.;
+#X text 602 339 <-- record;
+#X obj 628 439 line~;
+#X obj 593 444 *~;
+#X text 710 328 ------ 4 seconds ------;
+#X msg 551 418 1;
+#X obj 661 376 del 3990;
+#X msg 661 404 0 10;
+#X text 712 405 <--stop recording;
+#X text 25 706 We avoid clicking at the end of the table by getting
+the table's own contents to go smoothly to zero. To do this we added
+a level control to the recording patch that cuts off just before the
+recording reaches the end of the table.;
+#X text 595 641 this is.;
+#X text 596 615 My apologies to Jonathan Harvey whose bell;
+#X msg 583 550 read ../sound/bell.aiff sample-table;
+#X obj 583 579 soundfiler;
+#X text 725 751 updated for Pd version 0.33;
+#X connect 5 0 49 0;
+#X connect 6 0 7 1;
+#X connect 7 0 6 0;
+#X connect 8 0 7 2;
+#X connect 10 0 7 0;
+#X connect 11 0 20 0;
+#X connect 12 0 13 0;
+#X connect 13 0 40 0;
+#X connect 15 0 14 0;
+#X connect 15 0 43 0;
+#X connect 15 0 42 0;
+#X connect 18 0 11 0;
+#X connect 19 0 20 1;
+#X connect 20 0 10 0;
+#X connect 21 0 19 0;
+#X connect 22 0 18 0;
+#X connect 24 0 27 0;
+#X connect 24 0 25 0;
+#X connect 25 0 23 0;
+#X connect 39 0 40 1;
+#X connect 40 0 14 0;
+#X connect 42 0 39 0;
+#X connect 43 0 44 0;
+#X connect 44 0 39 0;
+#X connect 49 0 50 0;
diff --git a/pd/doc/3.audio.examples/34.sampler.notes.pd b/pd/doc/3.audio.examples/34.sampler.notes.pd
new file mode 100644
index 00000000..9b08a10a
--- /dev/null
+++ b/pd/doc/3.audio.examples/34.sampler.notes.pd
@@ -0,0 +1,321 @@
+#N canvas 12 0 1074 786 12;
+#X msg 862 17 \; pd dsp 1;
+#X msg 946 17 \; pd dsp 0;
+#X text 887 53 ON;
+#X text 965 53 OFF;
+#X floatatom 64 512 0 0 0;
+#N canvas 159 26 495 262 output 0;
+#X obj 406 192 t b;
+#X obj 406 132 f;
+#X obj 406 72 inlet;
+#X text 413 35 mute;
+#X obj 406 222 f;
+#X msg 510 214 0;
+#X msg 406 102 bang;
+#X obj 406 162 moses 1;
+#X obj 510 184 t b f;
+#X obj 476 140 moses 1;
+#X obj 100 178 dbtorms;
+#X obj 476 110 r master-lvl;
+#X obj 100 50 r master-lvl;
+#X obj 406 252 s master-lvl;
+#X obj 26 217 inlet~;
+#X obj 239 49 inlet;
+#X text 239 22 level;
+#X obj 239 120 s master-lvl;
+#X msg 115 78 set \$1;
+#X obj 115 107 outlet;
+#X msg 257 77 \; pd dsp 1;
+#X obj 100 233 line~;
+#X obj 26 254 *~;
+#X obj 26 289 dac~;
+#X obj 100 205 pack 0 50;
+#X text 24 190 audio;
+#X text 112 132 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 29 546 pd output;
+#X msg 98 512 MUTE;
+#X text 143 511 <-- output amplitude;
+#X msg 253 7 bang;
+#X obj 253 35 delay 5;
+#X text 493 269 end of note;
+#X obj 359 35 r note;
+#N canvas 459 46 678 451 samples 0;
+#X graph graph1 0 -1.02 176403 1.02 262 171 462 41;
+#X array sample1 176403 float 0;
+#X pop;
+#X text 264 376 ------ 4 seconds ------;
+#X graph graph1 0 -1.02 176403 1.02 262 356 462 226;
+#X array sample2 176403 float 0;
+#X pop;
+#X restore 29 277 pd samples;
+#N canvas 21 287 947 410 recorder 0;
+#X obj 318 43 inlet;
+#X obj 272 196 adc~ 1;
+#X obj 272 224 hip~ 5;
+#X obj 341 254 line~;
+#X obj 272 253 *~;
+#X msg 341 226 1;
+#X obj 400 191 del 3990;
+#X msg 377 226 0 10;
+#X obj 272 304 tabwrite~ sample1;
+#X obj 124 110 makefilename sample%1;
+#X msg 124 139 set \$1 \, bang;
+#X msg 446 162 stop;
+#X msg 400 162 bang;
+#X obj 557 182 loadbang;
+#X obj 660 137 openpanel;
+#X msg 660 109 bang;
+#X text 702 108 <-- browse for samples;
+#X text 628 233 v-- re-read original samples;
+#X obj 318 72 route record stop reload browse;
+#X obj 557 319 soundfiler;
+#X msg 557 261 read ../sound/bell.aiff sample1 \, read ../sound/voice2.wav
+sample2;
+#X msg 660 164 read \$1 sample1;
+#X obj 660 191 soundfiler;
+#X connect 0 0 18 0;
+#X connect 1 0 2 0;
+#X connect 2 0 4 0;
+#X connect 3 0 4 1;
+#X connect 4 0 8 0;
+#X connect 5 0 3 0;
+#X connect 6 0 7 0;
+#X connect 7 0 3 0;
+#X connect 9 0 10 0;
+#X connect 10 0 8 0;
+#X connect 11 0 6 0;
+#X connect 12 0 6 0;
+#X connect 13 0 20 0;
+#X connect 14 0 21 0;
+#X connect 15 0 14 0;
+#X connect 18 0 9 0;
+#X connect 18 0 12 0;
+#X connect 18 0 5 0;
+#X connect 18 1 7 0;
+#X connect 18 1 11 0;
+#X connect 18 2 20 0;
+#X connect 18 3 15 0;
+#X connect 20 0 19 0;
+#X connect 21 0 22 0;
+#X restore 29 443 pd recorder;
+#X msg 29 305 record 1;
+#X msg 29 360 stop;
+#N canvas 700 402 671 621 playback 0;
+#X obj 37 79 line~;
+#X obj 56 271 line~;
+#X obj 37 302 *~;
+#X obj 56 242 r cutoff;
+#X obj 37 50 r phase;
+#X obj 37 626 outlet~;
+#X obj 37 598 hip~ 5;
+#X obj 49 113 r sample-number;
+#X obj 49 142 makefilename sample%d;
+#X msg 49 170 set \$1;
+#X obj 37 211 tabread4~ sample1;
+#X obj 55 338 r envelope;
+#X obj 55 396 dbtorms;
+#X obj 55 367 unpack;
+#X obj 55 425 sqrt;
+#X obj 55 454 sqrt;
+#X obj 55 482 line~;
+#X obj 37 569 *~;
+#X obj 55 511 *~;
+#X obj 55 540 *~;
+#X text 107 51 messages to the phase generating line~;
+#X text 188 114 setting the sample number.;
+#X text 238 143 compute the name;
+#X text 110 171 and send a "set" message to the tabread4~.;
+#X text 116 270 line~ for de-clicking;
+#X text 156 341 The envelope generator. Rather than sending our message
+straight to the line~ we unpack it in order to fool with the amplitude
+field.;
+#X text 126 397 convert amplitude to linear units.;
+#X text 121 426 take the fourth root. This because we want to raies
+the line~'s output to the 4th power afterward. This is an inexpensive
+way to give the rise and decay a more natural sounding evolution than
+just a straight line.;
+#X text 94 514 square the output twice to get the fourth power.;
+#X connect 0 0 10 0;
+#X connect 1 0 2 1;
+#X connect 2 0 17 0;
+#X connect 3 0 1 0;
+#X connect 4 0 0 0;
+#X connect 6 0 5 0;
+#X connect 7 0 8 0;
+#X connect 8 0 9 0;
+#X connect 9 0 10 0;
+#X connect 10 0 2 0;
+#X connect 11 0 13 0;
+#X connect 12 0 14 0;
+#X connect 13 0 12 0;
+#X connect 13 1 16 1;
+#X connect 14 0 15 0;
+#X connect 15 0 16 0;
+#X connect 16 0 18 0;
+#X connect 16 0 18 1;
+#X connect 17 0 6 0;
+#X connect 18 0 19 0;
+#X connect 18 0 19 1;
+#X connect 19 0 17 1;
+#X restore 29 480 pd playback;
+#X msg 29 332 record 2;
+#X text 641 25 ARGUMENTS FOR NOTES:;
+#X text 662 53 pitch in halftones;
+#X text 662 77 amplitude (dB);
+#X text 662 125 sample number;
+#X text 662 101 duration (msec);
+#X text 662 149 start location (msec);
+#X text 662 173 rise time (msec);
+#X text 662 197 decay time (msec);
+#X obj 359 62 unpack 0 0 0 0 0 0 0;
+#X text 46 6 CHOCOLATE SAMPLER;
+#X obj 517 168 f;
+#X obj 452 142 f;
+#X obj 383 142 f;
+#X obj 346 142 f;
+#X obj 314 142 f;
+#X obj 220 142 f;
+#X obj 220 169 mtof;
+#X obj 220 197 / 261.62;
+#X obj 220 224 * 4.41e+08;
+#X obj 220 252 +;
+#X obj 485 142 delay;
+#X obj 314 312 pack 0 0 0 0 0;
+#X obj 253 62 t b b b;
+#X text 494 346 This starts the note \, sending to "receives" in the
+playback subptach. The new receive "envelope" is an amplitude control
+in parallel with the cutoff control. The "sample-number" switches the
+tabread4~ between tables.;
+#X msg 152 44 \; pd dsp 1 \; cutoff 0 5;
+#X obj 383 197 + 1;
+#X msg 552 467 60 100 10000 1 0 0 0;
+#X obj 552 737 s note;
+#X msg 517 196 \; envelope 0 \$1;
+#X msg 671 691 62;
+#X msg 706 691 64;
+#X msg 637 691 60;
+#X msg 608 691 55;
+#X msg 739 691 72;
+#X msg 576 691 48;
+#X msg 638 734 60.5;
+#X msg 552 494 60 90 10000 1 0 0 0;
+#X msg 552 522 60 100 10000 2 0 0 0;
+#X msg 552 550 60 100 10000 1 3000 0 0;
+#X obj 383 169 * 44.1;
+#X msg 552 605 60 100 100 1 0 0 0;
+#X msg 552 632 60 100 100 1 0 0 1000;
+#X msg 552 577 60 100 10000 1 0 1000 0;
+#X msg 314 340 \; envelope 0 \, \$1 \$2 \; phase \$3 \, \$4 1e+07 \;
+sample-number \$5 \; cutoff 1 5 \;;
+#X text 108 315 <-- record;
+#X msg 29 388 reload;
+#X msg 29 415 browse;
+#X text 6 109 transposition works;
+#X text 6 133 by altering the phase;
+#X text 6 181 The mtof and / 261;
+#X text 6 205 calculate speed change;
+#X text 6 229 considering 60 as unity.;
+#X text 20 43 as before we;
+#X text 11 64 mute and wait;
+#X text 6 157 target ($4 below right.);
+#X text 446 303 combine amplitude \, rise time \, start phase \, end
+phase \, and sample number in one message;
+#X text 16 594 This patch take the same principle as the previous one
+\, but allows you to parametrize sample playback. Since we must wait
+5 msec before starting the playback \, we store all the parameters
+in "f" objects \, and recall them to construct the new note. Transposition
+is done by altering the amount to play back in the (artificial) ten
+thousand seconds (1e+07). The playback segment can be altered to start
+in the middle of the sample instead of the beginning \, and you can
+change the duration and rise and decay times.;
+#X text 744 468 straight playback;
+#X text 749 495 change amplitude;
+#X text 752 523 change sample number;
+#X text 755 550 change start location;
+#X text 754 576 change rise time;
+#X text 754 609 change duration;
+#X text 755 633 ... and decay time;
+#X text 688 736 microtones OK too.;
+#X text 576 667 If you omit values they stay unchanged;
+#X text 799 759 updated for Pd version 0.33;
+#X text 548 426 Here are buttons to demonstrate the effect of varying
+the parameters one by one.;
+#X connect 4 0 5 1;
+#X connect 5 0 4 0;
+#X connect 6 0 5 2;
+#X connect 8 0 9 0;
+#X connect 8 0 42 0;
+#X connect 9 0 40 0;
+#X connect 11 0 26 0;
+#X connect 14 0 13 0;
+#X connect 15 0 13 0;
+#X connect 16 0 5 0;
+#X connect 17 0 13 0;
+#X connect 26 0 33 1;
+#X connect 26 0 8 0;
+#X connect 26 1 32 1;
+#X connect 26 2 38 1;
+#X connect 26 3 31 1;
+#X connect 26 4 30 1;
+#X connect 26 5 29 1;
+#X connect 26 6 28 1;
+#X connect 28 0 46 0;
+#X connect 29 0 39 1;
+#X connect 30 0 57 0;
+#X connect 31 0 39 4;
+#X connect 32 0 39 0;
+#X connect 33 0 34 0;
+#X connect 34 0 35 0;
+#X connect 35 0 36 0;
+#X connect 36 0 37 0;
+#X connect 37 0 39 3;
+#X connect 38 0 28 0;
+#X connect 39 0 61 0;
+#X connect 40 0 32 0;
+#X connect 40 1 33 0;
+#X connect 40 2 29 0;
+#X connect 40 2 30 0;
+#X connect 40 2 31 0;
+#X connect 40 2 38 0;
+#X connect 43 0 39 2;
+#X connect 43 0 37 1;
+#X connect 44 0 45 0;
+#X connect 47 0 45 0;
+#X connect 48 0 45 0;
+#X connect 49 0 45 0;
+#X connect 50 0 45 0;
+#X connect 51 0 45 0;
+#X connect 52 0 45 0;
+#X connect 53 0 45 0;
+#X connect 54 0 45 0;
+#X connect 55 0 45 0;
+#X connect 56 0 45 0;
+#X connect 57 0 43 0;
+#X connect 58 0 45 0;
+#X connect 59 0 45 0;
+#X connect 60 0 45 0;
+#X connect 63 0 13 0;
+#X connect 64 0 13 0;
diff --git a/pd/doc/3.audio.examples/35.qlist.txt b/pd/doc/3.audio.examples/35.qlist.txt
new file mode 100644
index 00000000..0c412767
--- /dev/null
+++ b/pd/doc/3.audio.examples/35.qlist.txt
@@ -0,0 +1,147 @@
+note 60 90 50 2 50 30 30;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+15 note 60;
+100 note 59 90 100;
+comment measure 1;
+100 note 60 90 150 2 0;
+ note 36 90 200 2 50;
+200 note 48 90 250 2 0;
+ note 40 90 200 2 50;
+ note 43 90 200 2 50;
+200 note 48 90 250 2 0;
+ note 31 90 200 2 50;
+200 note 55 90 100;
+ note 41 90 200;
+ note 43 90 200;
+100 note 53 90 100;
+100 note 52 90 100;
+ note 36 90 200;
+100 note 55 90 100;
+100 note 60 90 100;
+ note 40 90 200;
+ note 43 90 200;
+100 note 59 90 100;
+100 note 60 90 100;
+ note 25 90 200;
+100 note 64 90 100;
+100 note 62 90 100;
+ note 39 90 200;
+ note 43 90 200;
+100 note 61 90 100;
+
+comment measure 2;
+100 note 62 90 150 2 0;
+ note 26 90 200;
+200 note 50 90 250 2 50;
+ note 41 90 200;
+ note 42 90 200;
+200 note 50 90 250;
+ note 29 90 200;
+200 note 50 90 100;
+ note 30 90 200;
+ note 44 90 200;
+ note 48 90 200;
+100 note 48 90 100;
+100 note 47 90 100;
+ note 31 90 200;
+ note 43 90 200;
+ note 47 90 200;
+100 note 50 90 100;
+100 note 55 90 100;
+ note 34 90 200;
+ note 42 90 200;
+ note 46 90 200;
+100 note 54 90 100;
+100 note 55 90 200;
+ note 35 90 200;
+ note 42 90 200;
+ note 45 90 200;
+200 note 57 90 100;
+ note 41 90 200;
+ note 47 90 200;
+100 note 59 90 100;
+comment measure 3;
+100 note 60 90 100;
+ note 24 90 200;
+ note 40 90 200;
+ note 48 90 200 2 0;
+
+100 note 59 90 100 2 50;
+100 note 57 90 100;
+100 note 55 90 100;
+
+100 note 57 90 100;
+ note 28 90 200;
+ note 38 90 200;
+ note 46 90 200;
+100 note 55 90 100;
+100 note 53 90 100;
+100 note 52 90 100;
+
+100 note 53 90 100;
+ note 29 90 100;
+ note 36 90 100;
+ note 45 90 100;
+100 note 52 90 100;
+100 note 50 90 100;
+ note 29 90 300;
+ note 36 90 300;
+ note 45 90 300;
+100 note 48 90 100;
+
+100 note 50 90 100;
+100 note 48 90 100;
+100 note 47 90 100;
+ note 29 90 300;
+ note 38 90 300;
+ note 44 90 300 2 0;
+100 note 45 90 100 2 50;
+
+comment measure 4;
+100 note 43 90 100;
+ note 31 90 200;
+ note 38 90 200;
+100 note 48 90 100;
+100 note 47 90 100;
+ note 31 90 300;
+ note 40 90 300;
+ note 43 90 300 2 0;
+100 note 50 90 100 2 50;
+
+100 note 48 90 100;
+100 note 52 90 100;
+100 note 50 90 100;
+ note 31 90 300 2 0;
+ note 41 90 300;
+ note 43 90 300;
+100 note 53 90 100 2 50;
+
+100 note 52 90 200;
+ note 31 90 300 2 50;
+200 note 48 90 200;
+ note 19 90 200 2 50;
+ note 29 90 200 2 50;
+ note 36 90 200 2 50;
+
+200 note 48 90 100 2 50 0 4000;
+ note 12 90 300;
+ note 28 90 300;
+ note 36 90 300;
+
+
+
diff --git a/pd/doc/3.audio.examples/35.sampler.poly.pd b/pd/doc/3.audio.examples/35.sampler.poly.pd
new file mode 100644
index 00000000..e42b586d
--- /dev/null
+++ b/pd/doc/3.audio.examples/35.sampler.poly.pd
@@ -0,0 +1,228 @@
+#N canvas 66 253 1119 674 12;
+#X floatatom 582 562 0 0 0;
+#N canvas 159 26 495 262 output 0;
+#X obj 406 192 t b;
+#X obj 406 132 f;
+#X obj 406 72 inlet;
+#X text 413 35 mute;
+#X obj 406 222 f;
+#X msg 510 214 0;
+#X msg 406 102 bang;
+#X obj 406 162 moses 1;
+#X obj 510 184 t b f;
+#X obj 476 140 moses 1;
+#X obj 100 178 dbtorms;
+#X obj 476 110 r master-lvl;
+#X obj 100 50 r master-lvl;
+#X obj 406 252 s master-lvl;
+#X obj 26 217 inlet~;
+#X obj 239 49 inlet;
+#X text 239 22 level;
+#X obj 239 120 s master-lvl;
+#X msg 115 78 set \$1;
+#X obj 115 107 outlet;
+#X msg 257 77 \; pd dsp 1;
+#X obj 100 233 line~;
+#X obj 26 254 *~;
+#X obj 26 289 dac~;
+#X obj 100 205 pack 0 50;
+#X text 24 190 audio;
+#X text 112 132 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 547 595 pd output;
+#X msg 617 562 MUTE;
+#X text 660 561 <-- output amplitude;
+#N canvas 0 0 600 392 samples 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array sample1 176403 float 0;
+#X coords 0 1.02 176403 -1.02 200 130 1;
+#X restore 262 41 graph;
+#X text 282 385 ------ 4 seconds ------;
+#N canvas 0 0 450 300 graph1 0;
+#X array sample2 176403 float 0;
+#X coords 0 1.02 176403 -1.02 200 130 1;
+#X restore 262 226 graph;
+#X restore 931 97 pd samples;
+#N canvas 52 219 967 340 recorder 0;
+#X obj 220 21 inlet;
+#X obj 174 174 adc~ 1;
+#X obj 174 202 hip~ 5;
+#X obj 243 232 line~;
+#X obj 174 231 *~;
+#X msg 243 204 1;
+#X obj 302 169 del 3990;
+#X msg 279 204 0 10;
+#X obj 174 282 tabwrite~ sample1;
+#X obj 26 88 makefilename sample%1;
+#X msg 26 117 set \$1 \, bang;
+#X msg 348 140 stop;
+#X msg 302 140 bang;
+#X obj 220 50 route record stop reload browse;
+#X obj 411 158 loadbang;
+#X obj 514 113 openpanel;
+#X msg 514 85 bang;
+#X text 556 84 <-- browse for samples;
+#X text 482 209 v-- re-read original samples;
+#X obj 411 295 soundfiler;
+#X msg 411 237 read ../sound/bell.aiff sample1 \, read ../sound/voice2.wav
+sample2;
+#X msg 514 140 read \$1 sample1;
+#X obj 514 167 soundfiler;
+#X connect 0 0 13 0;
+#X connect 1 0 2 0;
+#X connect 2 0 4 0;
+#X connect 3 0 4 1;
+#X connect 4 0 8 0;
+#X connect 5 0 3 0;
+#X connect 6 0 7 0;
+#X connect 7 0 3 0;
+#X connect 9 0 10 0;
+#X connect 10 0 8 0;
+#X connect 11 0 6 0;
+#X connect 12 0 6 0;
+#X connect 13 0 9 0;
+#X connect 13 0 12 0;
+#X connect 13 0 5 0;
+#X connect 13 1 7 0;
+#X connect 13 1 11 0;
+#X connect 13 2 20 0;
+#X connect 13 3 16 0;
+#X connect 14 0 20 0;
+#X connect 15 0 21 0;
+#X connect 16 0 15 0;
+#X connect 20 0 19 0;
+#X connect 21 0 22 0;
+#X restore 931 284 pd recorder;
+#X msg 931 146 record 1;
+#X msg 931 202 stop;
+#X msg 931 174 record 2;
+#X text 19 49 ARGUMENTS FOR NOTES:;
+#X text 19 71 pitch in halftones;
+#X text 19 95 amplitude (dB);
+#X text 19 143 sample number;
+#X text 19 119 duration (msec);
+#X text 19 167 start location (msec);
+#X text 19 191 rise time (msec);
+#X text 19 215 decay time (msec);
+#X msg 931 229 reload;
+#X msg 931 257 browse;
+#X text 47 10 POLYPHONIC SAMPLER;
+#X obj 547 522 sampvoice;
+#X obj 547 494 sampvoice;
+#X obj 547 467 sampvoice;
+#X obj 547 439 sampvoice;
+#X obj 547 412 sampvoice;
+#X obj 547 384 sampvoice;
+#X obj 547 356 sampvoice;
+#X obj 547 329 sampvoice;
+#X obj 631 17 r note;
+#X obj 631 44 unpack 0 0 0 0 0 0 0;
+#X obj 604 76 t b f;
+#X obj 544 109 f;
+#X obj 580 109 + 1;
+#X obj 552 146 mod 1e+06;
+#X obj 544 175 makenote 64;
+#X obj 544 203 poly 8 1;
+#X obj 544 230 stripnote;
+#X obj 617 272 pack 0 0 0 0 0 0 0 0;
+#X obj 617 300 route 1 2 3 4 5 6 7 8;
+#X text 929 124 record \, etc.;
+#X text 206 142 increment mod 1e+06 to make fake pitch;
+#X text 326 177 supply note-off message;
+#X text 336 207 allocate sampler voice;
+#X text 359 232 drop note off again;
+#X obj 736 516 qlist;
+#X obj 870 520 r comment;
+#X text 732 445 sailors to untie him...;
+#X text 735 395 Lashed to the mast of his boat \, Ulysses;
+#X text 735 420 hears beautiful singing. He begs his;
+#X text 19 271 Here we take the previous patch and make it polyphonic
+\, with 8 voices. The single voice which we had before has been made
+into an abstraction \, "sampvoice.pd" \, which we instantiate in 8
+copies. Earlier we used sends and receives to pass messages to "cutoff"
+\, etc \, but here if we did that the copies of sampvoice would be
+sending messages to each other \, so we combine the control and the
+audio computation in the sampvoice abstraction without using send and
+receive. Click on one to see how.;
+#X text 20 421 The "poly" object essentially repeats pitch and velocity
+pairs to its output \, but also sending a voice number from its left
+outlet. To use it \, we unpack the 7 parameters \, calculate the voice
+number \, repack the message as 8 parameters with voice number first
+\, and use "route" to send it to one of the 8 voices.;
+#X text 20 523 There's some bother because poly expects to track note
+on and note off messages separately as they would come from a MIDI
+keyboard. So we assign each note a unique fake "pitch" \, use makenote
+to generate the note-off messages \, and run poly on the resulting
+stream. We then discard both pitch and velocity (using the velocity
+only to strip note-offs) and rebuild the original message adding the
+voice number we just scored.;
+#X text 854 639 updated for Pd version 0.33;
+#X msg 736 486 read 35.qlist.txt \, rewind \, tempo 1 \, bang;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 6 0 5 0;
+#X connect 7 0 5 0;
+#X connect 8 0 5 0;
+#X connect 17 0 5 0;
+#X connect 18 0 5 0;
+#X connect 20 0 1 0;
+#X connect 21 0 20 0;
+#X connect 22 0 21 0;
+#X connect 23 0 22 0;
+#X connect 24 0 23 0;
+#X connect 25 0 24 0;
+#X connect 26 0 25 0;
+#X connect 27 0 26 0;
+#X connect 28 0 29 0;
+#X connect 29 0 30 0;
+#X connect 29 1 37 2;
+#X connect 29 2 34 2;
+#X connect 29 2 37 3;
+#X connect 29 3 37 4;
+#X connect 29 4 37 5;
+#X connect 29 5 37 6;
+#X connect 29 6 37 7;
+#X connect 30 0 31 0;
+#X connect 30 1 37 1;
+#X connect 31 0 32 0;
+#X connect 31 0 34 0;
+#X connect 32 0 33 0;
+#X connect 33 0 31 1;
+#X connect 34 0 35 0;
+#X connect 34 1 35 1;
+#X connect 35 0 36 0;
+#X connect 35 2 36 1;
+#X connect 36 0 37 0;
+#X connect 37 0 38 0;
+#X connect 38 0 27 1;
+#X connect 38 1 26 1;
+#X connect 38 2 25 1;
+#X connect 38 3 24 1;
+#X connect 38 4 23 1;
+#X connect 38 5 22 1;
+#X connect 38 6 21 1;
+#X connect 38 7 20 1;
+#X connect 53 0 44 0;
diff --git a/pd/doc/3.audio.examples/36.PART5.envelopes.pd b/pd/doc/3.audio.examples/36.PART5.envelopes.pd
new file mode 100644
index 00000000..61e7a6d7
--- /dev/null
+++ b/pd/doc/3.audio.examples/36.PART5.envelopes.pd
@@ -0,0 +1,90 @@
+#N canvas 173 105 609 433 12;
+#X floatatom 96 358 0 0 100;
+#N canvas 448 68 641 420 output 0;
+#X obj 430 203 t b;
+#X obj 430 143 f;
+#X obj 430 83 inlet;
+#X obj 430 233 f;
+#X msg 541 225 0;
+#X msg 430 113 bang;
+#X obj 430 173 moses 1;
+#X obj 496 151 moses 1;
+#X obj 107 174 dbtorms;
+#X obj 496 121 r master-lvl;
+#X obj 107 56 r master-lvl;
+#X obj 430 263 s master-lvl;
+#X obj 36 202 inlet~;
+#X obj 251 229 inlet;
+#X obj 269 257 s master-lvl;
+#X msg 119 85 set \$1;
+#X obj 119 115 outlet;
+#X msg 251 283 \; pd dsp 1;
+#X obj 107 236 line~;
+#X obj 36 258 *~;
+#X obj 36 290 dac~;
+#X obj 107 204 pack 0 50;
+#X text 23 179 audio in;
+#X text 2 313 out both channels;
+#X obj 36 231 hip~ 1;
+#X obj 541 194 t b;
+#X connect 0 0 3 0;
+#X connect 1 0 6 0;
+#X connect 2 0 5 0;
+#X connect 3 0 11 0;
+#X connect 4 0 11 0;
+#X connect 5 0 1 0;
+#X connect 6 0 0 0;
+#X connect 6 1 25 0;
+#X connect 7 1 3 1;
+#X connect 8 0 21 0;
+#X connect 9 0 1 1;
+#X connect 9 0 7 0;
+#X connect 10 0 8 0;
+#X connect 10 0 15 0;
+#X connect 12 0 24 0;
+#X connect 13 0 14 0;
+#X connect 13 0 17 0;
+#X connect 15 0 16 0;
+#X connect 18 0 19 1;
+#X connect 19 0 20 0;
+#X connect 19 0 20 1;
+#X connect 21 0 18 0;
+#X connect 24 0 19 0;
+#X connect 25 0 4 0;
+#X restore 58 391 pd output;
+#X msg 134 359 MUTE;
+#X msg 247 180 bang;
+#X msg 328 180 bang;
+#X text 244 152 attack;
+#X text 325 150 release;
+#X obj 247 281 line~;
+#X msg 328 254 0 500;
+#X text 134 10 ENVELOPE GENERATORS;
+#X obj 58 246 phasor~ 50;
+#X obj 58 301 *~;
+#X obj 58 328 wrap~;
+#X msg 247 248 1 2500;
+#X obj 58 275 -~ 0.5;
+#X msg 179 247 10 200;
+#X obj 247 221 del 200;
+#X text 349 405 updated for Pd version 0.34;
+#X text 39 35 This patch uses an envelope generator to control a sound.
+When you hit "attack" two things happen. First \, the line~ object
+rises to 10 in 200 milliseconds. Then after a "delay" of the same 200
+msec \, the second message sends the line~ back down to 1 over another
+2500 msec. The "release" just ramps us down to zero at the end.;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 3 0 15 0;
+#X connect 3 0 16 0;
+#X connect 4 0 8 0;
+#X connect 7 0 11 1;
+#X connect 8 0 7 0;
+#X connect 10 0 14 0;
+#X connect 11 0 12 0;
+#X connect 12 0 1 0;
+#X connect 13 0 7 0;
+#X connect 14 0 11 0;
+#X connect 15 0 7 0;
+#X connect 16 0 13 0;
diff --git a/pd/doc/3.audio.examples/37.adsr.pd b/pd/doc/3.audio.examples/37.adsr.pd
new file mode 100644
index 00000000..06fa21b7
--- /dev/null
+++ b/pd/doc/3.audio.examples/37.adsr.pd
@@ -0,0 +1,34 @@
+#N canvas 141 83 672 516 12;
+#X text 412 489 updated for Pd version 0.26;
+#X graph graph1 0 -1.02 44100 1.02 395 235 595 105;
+#X array adsr-output 44100 float 0;
+#X pop;
+#X text 394 244 ------ 1 second ------;
+#X obj 32 74 r trigger;
+#X obj 32 148 tabwrite~ adsr-output;
+#X obj 54 123 r graphit;
+#X msg 31 176 bang;
+#X text 75 177 <-- attack and delayed release;
+#X obj 42 200 del 500;
+#X text 146 283 <-- attack only;
+#X msg 31 264 \; pd dsp 1 \; trigger 1 \; graphit bang;
+#X text 147 360 <-- release only;
+#X msg 30 334 \; pd dsp 1 \; trigger 0 \; graphit bang;
+#X msg 42 225 \; trigger 0;
+#X text 598 221 -1;
+#X text 600 96 1;
+#X text 35 24 This patch introduces a simple "adsr" abstraction we'll
+use frequently. You can click on the "adsr" object to see what's inside.
+;
+#X text 30 402 The active ingredient of the ADSR envelope generator
+is a single line~ which gets passed messages to make the attack and
+release behavior. You can retrigger the ADSR envelope generator all
+you wish without having to wait for attacks or releases to finish;
+#X text 104 5 ENVELOPE GENERATOR ABSTRACTION;
+#X obj 32 100 adsr 1 100 200 50 300;
+#X connect 3 0 19 0;
+#X connect 5 0 4 0;
+#X connect 6 0 10 0;
+#X connect 6 0 8 0;
+#X connect 8 0 13 0;
+#X connect 19 0 4 0;
diff --git a/pd/doc/3.audio.examples/38.envelope.dB.pd b/pd/doc/3.audio.examples/38.envelope.dB.pd
new file mode 100644
index 00000000..8ec1d1ae
--- /dev/null
+++ b/pd/doc/3.audio.examples/38.envelope.dB.pd
@@ -0,0 +1,158 @@
+#N canvas 112 44 674 673 12;
+#X obj 32 80 r trigger;
+#X text 85 8 USING ADSR'S OUTPUT AS dB;
+#X text 34 28 For natural sounding amplitude control \, you will want
+to use the ADSR's output as log amplitude. In practice this is best
+done using a lookup table:;
+#X obj 32 131 tabread4~ dbtorms;
+#N canvas 0 0 450 300 graph1 0;
+#X array dbtorms 123 float 1;
+#A 0 0 0 1.25893e-05 1.41254e-05 1.58489e-05 1.77828e-05 1.99526e-05
+2.23872e-05 2.51189e-05 2.81838e-05 3.16228e-05 3.54813e-05 3.98107e-05
+4.46684e-05 5.01187e-05 5.62341e-05 6.30957e-05 7.07946e-05 7.94328e-05
+8.91251e-05 1e-04 0.000112202 0.000125893 0.000141254 0.000158489 0.000177828
+0.000199526 0.000223872 0.000251189 0.000281838 0.000316228 0.000354813
+0.000398107 0.000446684 0.000501187 0.000562341 0.000630957 0.000707946
+0.000794328 0.000891251 0.001 0.00112202 0.00125893 0.00141254 0.00158489
+0.00177828 0.00199526 0.00223872 0.00251189 0.00281838 0.00316228 0.00354813
+0.00398107 0.00446684 0.00501187 0.00562341 0.00630957 0.00707946 0.00794328
+0.00891251 0.01 0.0112202 0.0125893 0.0141254 0.0158489 0.0177828 0.0199526
+0.0223872 0.0251189 0.0281838 0.0316228 0.0354813 0.0398107 0.0446684
+0.0501187 0.0562341 0.0630957 0.0707946 0.0794328 0.0891251 0.1 0.112202
+0.125893 0.141254 0.158489 0.177828 0.199526 0.223872 0.251189 0.281838
+0.316228 0.354813 0.398107 0.446684 0.501187 0.562341 0.630957 0.707946
+0.794328 0.891251 1 1.12202 1.25893 1.41254 1.58489 1.77828 1.99526
+2.23872 2.51189 2.81838 3.16228 3.54813 3.98107 4.46684 5.01187 5.62341
+6.30957 7.07946 7.94328 8.91251 10 11.2202 12.5893;
+#X coords 0 10 123 0 200 100 1;
+#X restore 387 83 graph;
+#N canvas 461 495 663 358 make-table 0;
+#X obj 97 195 moses 2;
+#X msg 81 44 bang;
+#X obj 81 73 t b b;
+#X obj 152 134 f;
+#X obj 190 134 + 1;
+#X msg 174 106 0;
+#X obj 81 102 until;
+#X obj 73 162 sel 122;
+#X msg 97 226 0;
+#X obj 141 227 dbtorms;
+#X obj 152 162 t f f;
+#X obj 97 259 tabwrite dbtorms;
+#X floatatom 435 103 0 0 0;
+#X floatatom 435 186 0 0 0;
+#X obj 435 157 tabread4 dbtorms;
+#X floatatom 331 183 0 0 0;
+#X obj 331 154 dbtorms;
+#X text 35 12 bang to recalculate the table;
+#X text 268 62 check accuracy of reading table against;
+#X text 268 81 the "real" dbtorms object.;
+#X connect 0 0 8 0;
+#X connect 0 1 9 0;
+#X connect 1 0 2 0;
+#X connect 2 0 6 0;
+#X connect 2 1 5 0;
+#X connect 3 0 4 0;
+#X connect 3 0 7 0;
+#X connect 3 0 10 0;
+#X connect 4 0 3 1;
+#X connect 5 0 3 1;
+#X connect 6 0 3 0;
+#X connect 7 0 6 1;
+#X connect 8 0 11 0;
+#X connect 9 0 11 0;
+#X connect 10 0 0 0;
+#X connect 10 1 11 1;
+#X connect 12 0 14 0;
+#X connect 12 0 16 0;
+#X connect 14 0 13 0;
+#X connect 16 0 15 0;
+#X restore 288 328 pd make-table;
+#X text 258 299 here's the patch I used to make the table:;
+#X obj 48 156 osc~ 440;
+#X text 589 176 0;
+#X text 590 77 10;
+#X text 406 186 ------ 123 samples ------;
+#X floatatom 61 204 0 0 0;
+#N canvas 159 26 532 285 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X obj 425 153 t b;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 26 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 21 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 5 0;
+#X restore 32 232 pd output;
+#X msg 108 205 MUTE;
+#X text 149 204 <-- output amplitude;
+#X text 112 276 <-- attack;
+#X text 113 333 <-- release;
+#X msg 32 319 \; pd dsp 1 \; trigger 0;
+#X obj 32 182 *~;
+#X msg 31 264 \; pd dsp 1 \; trigger 1;
+#X text 29 431 Notice how the attack sounds different when you retrigger
+than when you start from zero. This is because if you go from the steady
+state you only rise 30 dB instead of 100 \, so it sounds slower.;
+#X obj 32 106 adsr 100 100 200 70 300;
+#X text 29 381 The table is indexed from 1 to 120 so that 1 gives a
+true zero out and 120 gives 10 (a 20 dB boost.) The extra 20 dB are
+for headroom.;
+#X text 28 498 If this is a problem you have at least 2 ways of dealing
+with it. The best might be to adjust the attack time inside the abstraction
+using snapshot~ to find out where you're slewing from \, as demonstrated
+in the next patch.;
+#X text 406 631 updated for Pd version 0.35;
+#X text 28 568 There's also a "real" dbtorms~ object... but it's almost
+certainly much more compute-intensive than tabread4~ \, since it has
+to call a library "exp" function.;
+#X connect 0 0 21 0;
+#X connect 3 0 18 0;
+#X connect 7 0 18 1;
+#X connect 11 0 12 1;
+#X connect 12 0 11 0;
+#X connect 13 0 12 2;
+#X connect 18 0 12 0;
+#X connect 21 0 3 0;
diff --git a/pd/doc/3.audio.examples/39.envelope.slew.pd b/pd/doc/3.audio.examples/39.envelope.slew.pd
new file mode 100644
index 00000000..74599c6c
--- /dev/null
+++ b/pd/doc/3.audio.examples/39.envelope.slew.pd
@@ -0,0 +1,102 @@
+#N canvas 95 40 682 438 12;
+#X obj 32 80 r trigger;
+#X obj 32 131 tabread4~ dbtorms;
+#N canvas 0 0 450 300 graph1 0;
+#X array dbtorms 123 float 1;
+#A 0 0 0 1.25893e-05 1.41254e-05 1.58489e-05 1.77828e-05 1.99526e-05
+2.23872e-05 2.51189e-05 2.81838e-05 3.16228e-05 3.54813e-05 3.98107e-05
+4.46684e-05 5.01187e-05 5.62341e-05 6.30957e-05 7.07946e-05 7.94328e-05
+8.91251e-05 1e-04 0.000112202 0.000125893 0.000141254 0.000158489 0.000177828
+0.000199526 0.000223872 0.000251189 0.000281838 0.000316228 0.000354813
+0.000398107 0.000446684 0.000501187 0.000562341 0.000630957 0.000707946
+0.000794328 0.000891251 0.001 0.00112202 0.00125893 0.00141254 0.00158489
+0.00177828 0.00199526 0.00223872 0.00251189 0.00281838 0.00316228 0.00354813
+0.00398107 0.00446684 0.00501187 0.00562341 0.00630957 0.00707946 0.00794328
+0.00891251 0.01 0.0112202 0.0125893 0.0141254 0.0158489 0.0177828 0.0199526
+0.0223872 0.0251189 0.0281838 0.0316228 0.0354813 0.0398107 0.0446684
+0.0501187 0.0562341 0.0630957 0.0707946 0.0794328 0.0891251 0.1 0.112202
+0.125893 0.141254 0.158489 0.177828 0.199526 0.223872 0.251189 0.281838
+0.316228 0.354813 0.398107 0.446684 0.501187 0.562341 0.630957 0.707946
+0.794328 0.891251 1 1.12202 1.25893 1.41254 1.58489 1.77828 1.99526
+2.23872 2.51189 2.81838 3.16228 3.54813 3.98107 4.46684 5.01187 5.62341
+6.30957 7.07946 7.94328 8.91251 10 11.2202 12.5893;
+#X coords 0 10 123 0 200 100 1;
+#X restore 392 93 graph;
+#X obj 55 157 osc~ 440;
+#X text 594 186 0;
+#X text 596 86 10;
+#X text 411 196 ------ 123 samples ------;
+#X floatatom 70 206 0 0 0;
+#N canvas 159 26 530 278 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 395 183 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 389 110 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 389 85 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 21 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X restore 32 232 pd output;
+#X msg 106 205 MUTE;
+#X text 147 204 <-- output amplitude;
+#X text 112 276 <-- attack;
+#X text 113 333 <-- release;
+#X msg 32 319 \; pd dsp 1 \; trigger 0;
+#X obj 32 182 *~;
+#X msg 31 264 \; pd dsp 1 \; trigger 1;
+#X text 82 7 ADSR with constant slew rate instead of constant attack
+time;
+#X text 34 28 Here we swap in "adsr2" so you cam compare. I think sometimes
+you'll want the constant rise time \, and sometimes the constant rise
+rate as in adsr2.;
+#X text 40 390 Click on the adsr2 object to see how this is done.;
+#X obj 32 106 adsr2 100 100 200 70 300;
+#X text 420 414 updated for Pd version 0.35;
+#X connect 0 0 19 0;
+#X connect 1 0 14 0;
+#X connect 3 0 14 1;
+#X connect 7 0 8 1;
+#X connect 8 0 7 0;
+#X connect 9 0 8 2;
+#X connect 14 0 8 0;
+#X connect 19 0 1 0;
diff --git a/pd/doc/3.audio.examples/40.envelope.pitch.pd b/pd/doc/3.audio.examples/40.envelope.pitch.pd
new file mode 100644
index 00000000..42fc9ba8
--- /dev/null
+++ b/pd/doc/3.audio.examples/40.envelope.pitch.pd
@@ -0,0 +1,209 @@
+#N canvas 222 84 686 544 12;
+#X obj 48 106 r trigger;
+#X obj 48 154 tabread4~ dbtorms;
+#X floatatom 86 232 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 48 258 pd output;
+#X msg 124 232 MUTE;
+#X text 166 231 <-- output amplitude;
+#X text 141 298 <-- attack;
+#X text 568 305 <-- release;
+#X obj 48 208 *~;
+#N canvas 151 343 812 522 make-table 0;
+#X msg 82 49 bang;
+#X obj 82 78 t b b;
+#X obj 141 142 f;
+#X obj 179 142 + 1;
+#X msg 150 112 0;
+#X obj 82 107 until;
+#X obj 141 176 t f f;
+#X floatatom 369 67 0 0 0;
+#X floatatom 369 127 0 0 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array dbtorms 123 float 1;
+#A 0 0 0 1.25893e-05 1.41254e-05 1.58489e-05 1.77828e-05 1.99526e-05
+2.23872e-05 2.51189e-05 2.81838e-05 3.16228e-05 3.54813e-05 3.98107e-05
+4.46684e-05 5.01187e-05 5.62341e-05 6.30957e-05 7.07946e-05 7.94328e-05
+8.91251e-05 1e-04 0.000112202 0.000125893 0.000141254 0.000158489 0.000177828
+0.000199526 0.000223872 0.000251189 0.000281838 0.000316228 0.000354813
+0.000398107 0.000446684 0.000501187 0.000562341 0.000630957 0.000707946
+0.000794328 0.000891251 0.001 0.00112202 0.00125893 0.00141254 0.00158489
+0.00177828 0.00199526 0.00223872 0.00251189 0.00281838 0.00316228 0.00354813
+0.00398107 0.00446684 0.00501187 0.00562341 0.00630957 0.00707946 0.00794328
+0.00891251 0.01 0.0112202 0.0125893 0.0141254 0.0158489 0.0177828 0.0199526
+0.0223872 0.0251189 0.0281838 0.0316228 0.0354813 0.0398107 0.0446684
+0.0501187 0.0562341 0.0630957 0.0707946 0.0794328 0.0891251 0.1 0.112202
+0.125893 0.141254 0.158489 0.177828 0.199526 0.223872 0.251189 0.281838
+0.316228 0.354813 0.398107 0.446684 0.501187 0.562341 0.630957 0.707946
+0.794328 0.891251 1 1.12202 1.25893 1.41254 1.58489 1.77828 1.99526
+2.23872 2.51189 2.81838 3.16228 3.54813 3.98107 4.46684 5.01187 5.62341
+6.30957 7.07946 7.94328 8.91251 10 11.2202 12.5893;
+#X coords 0 10 123 0 200 100 1;
+#X restore 538 298 graph;
+#X text 740 391 0;
+#X text 742 291 10;
+#X text 544 403 ------ 123 samples ------;
+#N canvas 0 0 450 300 graph2 0;
+#X array mtof 130 float 1;
+#A 0 8.1758 8.66196 9.17702 9.72272 10.3009 10.9134 11.5623 12.2499
+12.9783 13.75 14.5676 15.4339 16.3516 17.3239 18.354 19.4454 20.6017
+21.8268 23.1247 24.4997 25.9565 27.5 29.1352 30.8677 32.7032 34.6478
+36.7081 38.8909 41.2034 43.6535 46.2493 48.9994 51.9131 55 58.2705
+61.7354 65.4064 69.2957 73.4162 77.7817 82.4069 87.3071 92.4986 97.9989
+103.826 110 116.541 123.471 130.813 138.591 146.832 155.563 164.814
+174.614 184.997 195.998 207.652 220 233.082 246.942 261.626 277.183
+293.665 311.127 329.628 349.228 369.994 391.995 415.305 440 466.164
+493.883 523.251 554.365 587.33 622.254 659.255 698.456 739.989 783.991
+830.609 880 932.328 987.767 1046.5 1108.73 1174.66 1244.51 1318.51
+1396.91 1479.98 1567.98 1661.22 1760 1864.66 1975.53 2093 2217.46 2349.32
+2489.02 2637.02 2793.83 2959.96 3135.96 3322.44 3520 3729.31 3951.07
+4186.01 4434.92 4698.64 4978.03 5274.04 5587.65 5919.91 6271.93 6644.88
+7040 7458.62 7902.13 8372.02 8869.84 9397.27 9956.06 10548.1 11175.3
+11839.8 12543.9 13289.8 14080;
+#X coords 0 12000 130 0 200 100 1;
+#X restore 537 130 graph;
+#X obj 283 102 mtof;
+#X floatatom 282 127 0 0 0;
+#X text 541 237 ------ 130 samples ------;
+#X text 746 223 0;
+#X text 748 123 12000;
+#X obj 81 203 mtof;
+#X obj 72 167 sel 129;
+#X obj 80 229 tabwrite mtof;
+#X obj 369 99 tabread4 mtof;
+#X obj 71 418 moses 2;
+#X msg 55 267 bang;
+#X obj 55 296 t b b;
+#X obj 126 357 f;
+#X obj 164 357 + 1;
+#X msg 148 329 0;
+#X obj 55 325 until;
+#X obj 47 385 sel 122;
+#X msg 71 449 0;
+#X obj 115 450 dbtorms;
+#X obj 126 385 t f f;
+#X obj 71 482 tabwrite dbtorms;
+#X text 312 40 ... and test accuracy;
+#X text 23 15 patch to recalculate the mtof table;
+#X text 107 267 bang to recalculate dbtorms table;
+#X connect 0 0 1 0;
+#X connect 1 0 5 0;
+#X connect 1 1 4 0;
+#X connect 2 0 3 0;
+#X connect 2 0 6 0;
+#X connect 2 0 20 0;
+#X connect 3 0 2 1;
+#X connect 4 0 2 1;
+#X connect 5 0 2 0;
+#X connect 6 0 19 0;
+#X connect 6 1 21 1;
+#X connect 7 0 14 0;
+#X connect 7 0 22 0;
+#X connect 14 0 15 0;
+#X connect 19 0 21 0;
+#X connect 20 0 5 1;
+#X connect 22 0 8 0;
+#X connect 23 0 31 0;
+#X connect 23 1 32 0;
+#X connect 24 0 25 0;
+#X connect 25 0 29 0;
+#X connect 25 1 28 0;
+#X connect 26 0 27 0;
+#X connect 26 0 30 0;
+#X connect 26 0 33 0;
+#X connect 27 0 26 1;
+#X connect 28 0 26 1;
+#X connect 29 0 26 0;
+#X connect 30 0 29 1;
+#X connect 31 0 34 0;
+#X connect 32 0 34 0;
+#X connect 33 0 23 0;
+#X connect 33 1 34 1;
+#X restore 451 222 pd make-table;
+#X text 35 6 PITCH ENVELOPES;
+#X text 125 24 For pitch envelopes \, unlike amplitude envelopes \,
+discontinuities are allowed and sometimes you would rather the envelope
+generator actually jump to zero when it's triggered. The "adsr2" object
+does this for you if you send a negative trigger instead of a positive
+one:;
+#X obj 280 106 r trigger2;
+#X obj 280 178 tabread4~ mtof;
+#X obj 280 202 osc~;
+#X msg 46 288 \; pd dsp 1 \; trigger 1 \; trigger2 1;
+#X text 358 297 <-- attack;
+#X msg 249 293 \; pd dsp 1 \; trigger 1 \; trigger2 -1;
+#X msg 472 293 \; pd dsp 1 \; trigger 0 \; trigger2 0;
+#X obj 280 154 +~ 69;
+#X text 358 314 restarting;
+#X text 363 331 pitch env;
+#X text 37 377 We have added a new table \, mtof \, for converting
+audio signals from pitch to frequency. Its range is 1-127 \, so you
+want to add a base pitch in before you start reading from it.;
+#X text 37 443 This is an extreme use of pitch enveloping. In a real
+situation you might want an envelope controlling vibrato depth or the
+like instead of straight pitch.;
+#X obj 48 130 adsr2 100 50 200 90 1000;
+#X obj 280 130 adsr2 20 200 100 100 1000;
+#X text 413 497 updated for Pd version 0.35;
+#X connect 0 0 24 0;
+#X connect 1 0 8 0;
+#X connect 2 0 3 1;
+#X connect 3 0 2 0;
+#X connect 4 0 3 2;
+#X connect 8 0 3 0;
+#X connect 12 0 25 0;
+#X connect 13 0 14 0;
+#X connect 14 0 8 1;
+#X connect 19 0 13 0;
+#X connect 24 0 1 0;
+#X connect 25 0 19 0;
diff --git a/pd/doc/3.audio.examples/41.envelope.portamento.pd b/pd/doc/3.audio.examples/41.envelope.portamento.pd
new file mode 100644
index 00000000..6542e8b5
--- /dev/null
+++ b/pd/doc/3.audio.examples/41.envelope.portamento.pd
@@ -0,0 +1,148 @@
+#N canvas 222 84 642 346 12;
+#X floatatom 75 227 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 46 255 pd output;
+#X msg 123 227 MUTE;
+#X text 166 225 <-- output amplitude;
+#X obj 46 173 tabread4~ mtof;
+#X obj 46 197 osc~;
+#N canvas 247 30 505 439 tables 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array dbtorms 123 float 1;
+#A 0 0 0 1.25893e-05 1.41254e-05 1.58489e-05 1.77828e-05 1.99526e-05
+2.23872e-05 2.51189e-05 2.81838e-05 3.16228e-05 3.54813e-05 3.98107e-05
+4.46684e-05 5.01187e-05 5.62341e-05 6.30957e-05 7.07946e-05 7.94328e-05
+8.91251e-05 1e-04 0.000112202 0.000125893 0.000141254 0.000158489 0.000177828
+0.000199526 0.000223872 0.000251189 0.000281838 0.000316228 0.000354813
+0.000398107 0.000446684 0.000501187 0.000562341 0.000630957 0.000707946
+0.000794328 0.000891251 0.001 0.00112202 0.00125893 0.00141254 0.00158489
+0.00177828 0.00199526 0.00223872 0.00251189 0.00281838 0.00316228 0.00354813
+0.00398107 0.00446684 0.00501187 0.00562341 0.00630957 0.00707946 0.00794328
+0.00891251 0.01 0.0112202 0.0125893 0.0141254 0.0158489 0.0177828 0.0199526
+0.0223872 0.0251189 0.0281838 0.0316228 0.0354813 0.0398107 0.0446684
+0.0501187 0.0562341 0.0630957 0.0707946 0.0794328 0.0891251 0.1 0.112202
+0.125893 0.141254 0.158489 0.177828 0.199526 0.223872 0.251189 0.281838
+0.316228 0.354813 0.398107 0.446684 0.501187 0.562341 0.630957 0.707946
+0.794328 0.891251 1 1.12202 1.25893 1.41254 1.58489 1.77828 1.99526
+2.23872 2.51189 2.81838 3.16228 3.54813 3.98107 4.46684 5.01187 5.62341
+6.30957 7.07946 7.94328 8.91251 10 11.2202 12.5893;
+#X coords 0 10 123 0 200 100 1;
+#X restore 129 49 graph;
+#X text 331 142 0;
+#X text 333 42 10;
+#X text 127 157 ------ 123 samples ------;
+#N canvas 0 0 450 300 graph2 0;
+#X array mtof 130 float 1;
+#A 0 8.1758 8.66196 9.17702 9.72272 10.3009 10.9134 11.5623 12.2499
+12.9783 13.75 14.5676 15.4339 16.3516 17.3239 18.354 19.4454 20.6017
+21.8268 23.1247 24.4997 25.9565 27.5 29.1352 30.8677 32.7032 34.6478
+36.7081 38.8909 41.2034 43.6535 46.2493 48.9994 51.9131 55 58.2705
+61.7354 65.4064 69.2957 73.4162 77.7817 82.4069 87.3071 92.4986 97.9989
+103.826 110 116.541 123.471 130.813 138.591 146.832 155.563 164.814
+174.614 184.997 195.998 207.652 220 233.082 246.942 261.626 277.183
+293.665 311.127 329.628 349.228 369.994 391.995 415.305 440 466.164
+493.883 523.251 554.365 587.33 622.254 659.255 698.456 739.989 783.991
+830.609 880 932.328 987.767 1046.5 1108.73 1174.66 1244.51 1318.51
+1396.91 1479.98 1567.98 1661.22 1760 1864.66 1975.53 2093 2217.46 2349.32
+2489.02 2637.02 2793.83 2959.96 3135.96 3322.44 3520 3729.31 3951.07
+4186.01 4434.92 4698.64 4978.03 5274.04 5587.65 5919.91 6271.93 6644.88
+7040 7458.62 7902.13 8372.02 8869.84 9397.27 9956.06 10548.1 11175.3
+11839.8 12543.9 13289.8 14080;
+#X coords 0 12000 130 0 200 100 1;
+#X restore 136 226 graph;
+#X text 128 336 ------ 130 samples ------;
+#X text 340 318 0;
+#X text 342 218 12000;
+#X restore 490 225 pd tables;
+#X text 35 6 PORTAMENTO;
+#X obj 46 149 line~;
+#X obj 46 101 r pitch;
+#X msg 316 101 36;
+#X msg 345 101 48;
+#X msg 372 101 60;
+#X msg 429 101 72;
+#X msg 401 101 67;
+#X msg 483 101 76;
+#X msg 457 101 74;
+#X obj 451 165 s pitch;
+#X msg 514 101 84;
+#X msg 544 101 96;
+#X floatatom 143 125 0 0 0;
+#X text 173 126 <-- change speed;
+#X floatatom 451 139 0 0 0;
+#X obj 46 125 pack 0 100;
+#X obj 388 192 loadbang;
+#X msg 387 214 \; pitch 72;
+#X text 40 37 Portamento can be done using just line~ \, but you still
+might want to sweep in pitch \, not frequency:;
+#X text 363 293 updated for Pd version 0.35;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 4 0 5 0;
+#X connect 5 0 1 0;
+#X connect 8 0 4 0;
+#X connect 9 0 23 0;
+#X connect 10 0 22 0;
+#X connect 11 0 22 0;
+#X connect 12 0 22 0;
+#X connect 13 0 22 0;
+#X connect 14 0 22 0;
+#X connect 15 0 22 0;
+#X connect 16 0 22 0;
+#X connect 18 0 22 0;
+#X connect 19 0 22 0;
+#X connect 20 0 23 1;
+#X connect 22 0 17 0;
+#X connect 23 0 8 0;
+#X connect 24 0 25 0;
diff --git a/pd/doc/3.audio.examples/42.PART6.analog.sequencer.pd b/pd/doc/3.audio.examples/42.PART6.analog.sequencer.pd
new file mode 100644
index 00000000..b217112f
--- /dev/null
+++ b/pd/doc/3.audio.examples/42.PART6.analog.sequencer.pd
@@ -0,0 +1,133 @@
+#N canvas 222 24 761 466 12;
+#X floatatom 61 408 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 23 433 pd output;
+#X msg 99 405 MUTE;
+#X text 139 406 <-- output amplitude;
+#X obj 336 272 loadbang;
+#X text 35 6 ANALOG SYNTH SEQUENCER;
+#X obj 215 152 metro 100;
+#X obj 214 178 f;
+#X obj 248 181 + 1;
+#X obj 248 205 mod 11;
+#X graph graph1 0 36 11 96 511 197 711 97;
+#X array array1 11 float 0;
+#X pop;
+#X floatatom 215 127 0 0 0;
+#X floatatom 304 152 0 0 0;
+#X obj 214 230 tabread array1;
+#X obj 214 256 mtof;
+#X obj 214 283 osc~ 0;
+#X msg 26 171 1;
+#X obj 24 303 *~;
+#X obj 24 350 cos~;
+#X obj 24 375 hip~ 5;
+#X obj 24 327 +~ 0.1;
+#X msg 336 298 \; array1 0 50 51 52 50 52 56 50 56 58 52 58;
+#X floatatom 43 219 0 0 0;
+#X floatatom 163 221 0 0 0;
+#X floatatom 93 219 0 0 0;
+#X floatatom 128 220 0 0 0;
+#X msg 93 311 0;
+#X msg 93 329 0.1;
+#X msg 93 348 0.25;
+#X msg 215 101 1;
+#X text 244 101 <--START;
+#X text 126 331 <--symmetry;
+#X text 72 185 ADSR controls;
+#X text 43 199 lvl;
+#X text 95 200 A;
+#X text 137 201 D;
+#X text 170 202 S;
+#X floatatom 298 181 0 0 0;
+#X text 332 183 <--increment;
+#X text 339 155 <--msec;
+#X text 20 35 Analog synths had sequencers which could be used in a
+wide variety of ways. You can use an array to hold a sequence of control
+values as shown here.;
+#X obj 23 274 adsr 1 65 13 10 1000;
+#X obj 42 243 / 100;
+#X text 336 340 You can also do microtones \; 50.5 is a quarter tone
+sharper than 50;
+#X text 505 431 updated for Pd version 0.34;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 4 0 21 0;
+#X connect 6 0 7 0;
+#X connect 6 0 16 0;
+#X connect 7 0 8 0;
+#X connect 7 0 13 0;
+#X connect 8 0 9 0;
+#X connect 9 0 7 1;
+#X connect 11 0 6 0;
+#X connect 12 0 6 1;
+#X connect 13 0 14 0;
+#X connect 14 0 15 0;
+#X connect 15 0 17 1;
+#X connect 16 0 41 0;
+#X connect 17 0 20 0;
+#X connect 18 0 19 0;
+#X connect 19 0 1 0;
+#X connect 20 0 18 0;
+#X connect 22 0 42 0;
+#X connect 23 0 41 4;
+#X connect 24 0 41 2;
+#X connect 25 0 41 3;
+#X connect 26 0 20 1;
+#X connect 27 0 20 1;
+#X connect 28 0 20 1;
+#X connect 29 0 11 0;
+#X connect 37 0 8 1;
+#X connect 41 0 17 0;
+#X connect 42 0 41 1;
diff --git a/pd/doc/3.audio.examples/43.monophonic.synth.pd b/pd/doc/3.audio.examples/43.monophonic.synth.pd
new file mode 100644
index 00000000..26603430
--- /dev/null
+++ b/pd/doc/3.audio.examples/43.monophonic.synth.pd
@@ -0,0 +1,135 @@
+#N canvas 63 28 664 702 12;
+#X floatatom 71 494 4 0 0;
+#X floatatom 104 625 0 0 100;
+#N canvas 269 205 698 344 output 0;
+#X obj 388 156 t b;
+#X obj 388 105 f;
+#X obj 388 54 inlet;
+#X obj 388 181 f;
+#X msg 482 174 0;
+#X msg 388 79 bang;
+#X obj 388 130 moses 1;
+#X obj 482 149 t b f;
+#X obj 444 111 moses 1;
+#X obj 91 148 dbtorms;
+#X obj 444 86 r master-lvl;
+#X obj 91 48 r master-lvl;
+#X obj 388 207 s master-lvl;
+#X obj 28 169 inlet~;
+#X obj 213 195 inlet;
+#X obj 229 218 s master-lvl;
+#X msg 101 72 set \$1;
+#X obj 101 98 outlet;
+#X msg 213 241 \; pd dsp 1;
+#X obj 91 201 line~;
+#X obj 31 219 *~;
+#X obj 31 247 dac~;
+#X obj 91 173 pack 0 50;
+#X text 17 149 audio in;
+#X obj 28 194 hip~ 1;
+#X connect 0 0 3 0;
+#X connect 1 0 6 0;
+#X connect 2 0 5 0;
+#X connect 3 0 12 0;
+#X connect 4 0 12 0;
+#X connect 5 0 1 0;
+#X connect 6 0 0 0;
+#X connect 6 1 7 0;
+#X connect 7 0 4 0;
+#X connect 8 1 3 1;
+#X connect 9 0 22 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 16 0;
+#X connect 13 0 24 0;
+#X connect 14 0 15 0;
+#X connect 14 0 18 0;
+#X connect 16 0 17 0;
+#X connect 19 0 20 1;
+#X connect 20 0 21 0;
+#X connect 20 0 21 1;
+#X connect 22 0 19 0;
+#X connect 24 0 20 0;
+#X restore 72 648 pd output;
+#X msg 137 625 MUTE;
+#X obj 71 521 mtof;
+#X obj 77 345 stripnote;
+#X obj 201 500 select;
+#X obj 246 369 float;
+#X obj 255 318 t b f;
+#X obj 255 462 float;
+#X text 318 310 f - store pitch below;
+#X text 301 368 velocity stored here;
+#X text 349 395 test velocity for note on or off;
+#X text 301 425 note on;
+#X text 201 408 note;
+#X text 205 423 off;
+#X text 308 464 recall pitch;
+#X text 272 508 test against latest note-on pitch;
+#X text 96 367 pitch \, only;
+#X text 96 386 for note-on;
+#X text 86 456 pitch;
+#X text 197 6 MONOPHONIC MIDI SYNTH;
+#X obj 79 308 unpack;
+#X obj 80 228 notein;
+#X obj 79 260 pack;
+#X obj 71 547 osc~;
+#X obj 71 573 *~;
+#X obj 71 600 cos~;
+#X obj 166 557 line~;
+#X msg 121 526 \$1 100;
+#X msg 201 526 0 1000;
+#X obj 121 500 / 100;
+#X text 81 74 First \, at top \, incoming MIDI notes are parsed and
+used to set pitch and trigger an ADSR envelope. Second \, the envelope
+generator itself has been extended to offer controls over the time
+and target values via number boxes.;
+#X text 82 25 This patch shows how to make a monophonic synthesizer
+that could be controlled from a MIDI or voltage-control keyboard--in
+this example we assume MIDI.;
+#X text 350 658 updated for Pd version 0.34;
+#X msg 237 256 55 64;
+#X msg 237 282 55 0;
+#X msg 181 230 48 200;
+#X msg 180 257 48 64;
+#X msg 180 283 48 0;
+#X text 79 140 The note-off testing is complicated by the fact that
+we have to test both that the velocity is zero \, and further that
+the note-off pitch matches the pitch that is now playing (the most
+recent note-on pitch.);
+#X text 317 327 b - bang to recall velocity;
+#X obj 246 417 sel 0;
+#X connect 0 0 4 0;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 4 0 25 0;
+#X connect 5 0 0 0;
+#X connect 5 0 6 1;
+#X connect 6 0 30 0;
+#X connect 7 0 42 0;
+#X connect 8 0 7 0;
+#X connect 8 1 9 1;
+#X connect 9 0 6 0;
+#X connect 22 0 5 0;
+#X connect 22 0 8 0;
+#X connect 22 1 5 1;
+#X connect 22 1 7 1;
+#X connect 23 0 24 0;
+#X connect 23 1 24 1;
+#X connect 24 0 22 0;
+#X connect 25 0 26 0;
+#X connect 26 0 27 0;
+#X connect 27 0 2 0;
+#X connect 28 0 26 1;
+#X connect 29 0 28 0;
+#X connect 30 0 28 0;
+#X connect 31 0 29 0;
+#X connect 35 0 22 0;
+#X connect 36 0 22 0;
+#X connect 37 0 22 0;
+#X connect 38 0 22 0;
+#X connect 39 0 22 0;
+#X connect 42 0 9 0;
+#X connect 42 1 31 0;
diff --git a/pd/doc/3.audio.examples/44.sample.hold.pd b/pd/doc/3.audio.examples/44.sample.hold.pd
new file mode 100644
index 00000000..ef504816
--- /dev/null
+++ b/pd/doc/3.audio.examples/44.sample.hold.pd
@@ -0,0 +1,160 @@
+#N canvas 186 95 944 489 12;
+#X text 597 420 updated for Pd version 0.26;
+#X floatatom 94 393 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 65 421 pd output;
+#X msg 141 393 MUTE;
+#X text 184 392 <-- output amplitude;
+#N canvas 0 0 450 300 graph1 0;
+#X array samphold 44100 float 0;
+#X coords 0 1 44100 0 300 200 1;
+#X restore 606 36 graph;
+#N canvas 0 0 439 429 tables 1;
+#N canvas 0 0 450 300 graph1 0;
+#X array dbtorms 123 float 1;
+#A 0 0 0 1.25893e-05 1.41254e-05 1.58489e-05 1.77828e-05 1.99526e-05
+2.23872e-05 2.51189e-05 2.81838e-05 3.16228e-05 3.54813e-05 3.98107e-05
+4.46684e-05 5.01187e-05 5.62341e-05 6.30957e-05 7.07946e-05 7.94328e-05
+8.91251e-05 1e-04 0.000112202 0.000125893 0.000141254 0.000158489 0.000177828
+0.000199526 0.000223872 0.000251189 0.000281838 0.000316228 0.000354813
+0.000398107 0.000446684 0.000501187 0.000562341 0.000630957 0.000707946
+0.000794328 0.000891251 0.001 0.00112202 0.00125893 0.00141254 0.00158489
+0.00177828 0.00199526 0.00223872 0.00251189 0.00281838 0.00316228 0.00354813
+0.00398107 0.00446684 0.00501187 0.00562341 0.00630957 0.00707946 0.00794328
+0.00891251 0.01 0.0112202 0.0125893 0.0141254 0.0158489 0.0177828 0.0199526
+0.0223872 0.0251189 0.0281838 0.0316228 0.0354813 0.0398107 0.0446684
+0.0501187 0.0562341 0.0630957 0.0707946 0.0794328 0.0891251 0.1 0.112202
+0.125893 0.141254 0.158489 0.177828 0.199526 0.223872 0.251189 0.281838
+0.316228 0.354813 0.398107 0.446684 0.501187 0.562341 0.630957 0.707946
+0.794328 0.891251 1 1.12202 1.25893 1.41254 1.58489 1.77828 1.99526
+2.23872 2.51189 2.81838 3.16228 3.54813 3.98107 4.46684 5.01187 5.62341
+6.30957 7.07946 7.94328 8.91251 10 11.2202 12.5893;
+#X coords 0 10 123 0 200 100 1;
+#X restore 78 55 graph;
+#X text 280 148 0;
+#X text 282 48 10;
+#X text 97 158 ------ 123 samples ------;
+#N canvas 0 0 450 300 graph2 0;
+#X array mtof 130 float 1;
+#A 0 8.1758 8.66196 9.17702 9.72272 10.3009 10.9134 11.5623 12.2499
+12.9783 13.75 14.5676 15.4339 16.3516 17.3239 18.354 19.4454 20.6017
+21.8268 23.1247 24.4997 25.9565 27.5 29.1352 30.8677 32.7032 34.6478
+36.7081 38.8909 41.2034 43.6535 46.2493 48.9994 51.9131 55 58.2705
+61.7354 65.4064 69.2957 73.4162 77.7817 82.4069 87.3071 92.4986 97.9989
+103.826 110 116.541 123.471 130.813 138.591 146.832 155.563 164.814
+174.614 184.997 195.998 207.652 220 233.082 246.942 261.626 277.183
+293.665 311.127 329.628 349.228 369.994 391.995 415.305 440 466.164
+493.883 523.251 554.365 587.33 622.254 659.255 698.456 739.989 783.991
+830.609 880 932.328 987.767 1046.5 1108.73 1174.66 1244.51 1318.51
+1396.91 1479.98 1567.98 1661.22 1760 1864.66 1975.53 2093 2217.46 2349.32
+2489.02 2637.02 2793.83 2959.96 3135.96 3322.44 3520 3729.31 3951.07
+4186.01 4434.92 4698.64 4978.03 5274.04 5587.65 5919.91 6271.93 6644.88
+7040 7458.62 7902.13 8372.02 8869.84 9397.27 9956.06 10548.1 11175.3
+11839.8 12543.9 13289.8 14080;
+#X coords 0 12000 130 0 200 100 1;
+#X restore 85 232 graph;
+#X text 95 340 ------ 130 samples ------;
+#X text 294 325 0;
+#X text 296 225 12000;
+#X restore 648 280 pd tables;
+#X text 67 8 SAMPLE AND HOLD;
+#X obj 141 292 phasor~ 5;
+#X obj 44 267 phasor~ 7;
+#X obj 44 292 samphold~;
+#X floatatom 44 242 0 0 0;
+#X floatatom 141 237 0 0 0;
+#X obj 216 345 tabwrite~ samphold;
+#X msg 216 320 bang;
+#X obj 44 367 tabread4~ mtof;
+#X obj 44 317 *~ 48;
+#X obj 44 342 +~ 36;
+#X obj 44 392 osc~;
+#X msg 216 262 0;
+#X text 259 319 <--graph output;
+#X obj 44 217 unpack;
+#X text 254 259 <-- reset phase;
+#X msg 311 131 32 96.33;
+#X msg 124 131 5 7;
+#X msg 44 131 1 5;
+#X msg 78 131 2 11;
+#X msg 161 131 3.7 8.8;
+#X msg 235 131 3.4 8.9;
+#X text 16 31 Another analog favorite \, the sample and hold unit freezes
+an audio signal on command. In the Pd version \, the second input of
+samphold~ triggers it \, and the first input becomes the output's new
+value whenever the trigger decreases from one sample to the next. This
+is ideal for updating values when a phasor wraps around.;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 8 0 10 1;
+#X connect 9 0 10 0;
+#X connect 10 0 16 0;
+#X connect 10 0 13 0;
+#X connect 11 0 9 0;
+#X connect 12 0 8 0;
+#X connect 14 0 13 0;
+#X connect 15 0 18 0;
+#X connect 16 0 17 0;
+#X connect 17 0 15 0;
+#X connect 18 0 2 0;
+#X connect 19 0 8 1;
+#X connect 19 0 9 1;
+#X connect 21 0 11 0;
+#X connect 21 1 12 0;
+#X connect 23 0 21 0;
+#X connect 24 0 21 0;
+#X connect 25 0 21 0;
+#X connect 26 0 21 0;
+#X connect 27 0 21 0;
+#X connect 28 0 21 0;
diff --git a/pd/doc/3.audio.examples/45.envelope.follower.pd b/pd/doc/3.audio.examples/45.envelope.follower.pd
new file mode 100644
index 00000000..0c3de2b4
--- /dev/null
+++ b/pd/doc/3.audio.examples/45.envelope.follower.pd
@@ -0,0 +1,101 @@
+#N canvas 64 77 982 581 12;
+#X text 759 566 updated for Pd version 0.26;
+#X text 67 8 ENVELOPE FOLLOWERS;
+#X text 17 28 The env~ object reports ths RMS signal level over the last 256 samples (by default) or any other power of 2 that's at least twice the block size. The analysis is done in an overlapped fashion so that results appear every N/2 points if N is the analysis window size. So the larger the window \, the stabler the result and the less frequently it appears. Computation time doesn't depend heavily on N.;
+#X text 471 28 Envelope followers are frequently used to detect attacks and periods of silence. (There are fancier attack detectors out there \, though.) Here is a simple threshold-based attack and rest detector.;
+#X obj 152 160 dbtorms;
+#X floatatom 75 142;
+#X obj 75 165 osc~ 440;
+#X obj 75 188 *~ 0;
+#X obj 75 211 env~;
+#X floatatom 136 216;
+#X floatatom 152 137;
+#X msg 219 155 \; pd dsp 1;
+#X obj 87 307 t b f;
+#X floatatom 87 330;
+#X obj 94 385 pack;
+#X text 198 215 note 3.01 dB difference between peak and RMS amplitudes.;
+#X obj 94 408 route 0 1;
+#X obj 94 431 > 55;
+#X obj 139 433 < 45;
+#X obj 94 454 sel 1;
+#X obj 139 456 sel 1;
+#X msg 16 494 1;
+#X msg 45 494 0;
+#X obj 94 494 print attack;
+#X obj 87 365 != 0;
+#X obj 517 267 t b f;
+#X floatatom 517 290;
+#X obj 524 348 pack;
+#X obj 524 371 route 0 1;
+#X obj 524 404 sel 1;
+#X msg 458 525 1;
+#X msg 488 525 0;
+#X obj 517 326 != 0;
+#X obj 547 290 < 45;
+#X obj 542 441 timer;
+#X obj 600 371 sel 0;
+#X obj 616 496 sel 0;
+#X obj 542 487 sel 1;
+#X obj 542 528 print rest;
+#X obj 542 464 > 1000;
+#X text 125 330 state -- 1 if waiting for low threshold \,;
+#X text 151 345 0 if we've attained it and now want the;
+#X text 180 359 high one.;
+#X text 165 408 route the RMS value according to state;
+#X text 191 435 if off \, 55 dB means attack. If on \, 45;
+#X text 192 456 dB or less means state changes to off.;
+#X text 106 279 ATTACK DETECTION;
+#X text 515 244 REST DETECTION;
+#X text 590 288 Here we always will test RMS against a low value;
+#X text 615 305 but as before we route the result according to;
+#X text 637 322 our state \, 1 if "resting" \, 0 if not.;
+#X text 645 368 regardless of state \, when RMS isn't low;
+#X text 667 383 reset the timer;
+#X text 691 511 RMS isn't low enough.;
+#X text 602 403 If we're not in rest \, and the RMS is low \,;
+#X text 626 419 check elapsed time sinse RMS last wasn't low.;
+#X text 613 461 If more than 1 second \, report a rest.;
+#X text 664 495 If we're at rest \, pop out of it when;
+#X text 471 94 Both detectors are state machines with two states \, on and off. If on \, a test is run to determine whether to turn off \, and vice versa. The tests are run at each output of the rms~ object.;
+#X connect 4 0 7 1;
+#X connect 5 0 6 0;
+#X connect 6 0 7 0;
+#X connect 7 0 8 0;
+#X connect 8 0 9 0;
+#X connect 8 0 12 0;
+#X connect 8 0 25 0;
+#X connect 10 0 4 0;
+#X connect 10 0 11 0;
+#X connect 12 0 13 0;
+#X connect 12 1 14 1;
+#X connect 13 0 24 0;
+#X connect 14 0 16 0;
+#X connect 16 0 17 0;
+#X connect 16 1 18 0;
+#X connect 17 0 19 0;
+#X connect 18 0 20 0;
+#X connect 19 0 21 0;
+#X connect 19 0 23 0;
+#X connect 20 0 22 0;
+#X connect 21 0 13 0;
+#X connect 22 0 13 0;
+#X connect 24 0 14 0;
+#X connect 25 0 26 0;
+#X connect 25 1 33 0;
+#X connect 26 0 32 0;
+#X connect 27 0 28 0;
+#X connect 28 0 29 0;
+#X connect 28 1 36 0;
+#X connect 29 0 34 1;
+#X connect 30 0 26 0;
+#X connect 31 0 26 0;
+#X connect 32 0 27 0;
+#X connect 33 0 27 1;
+#X connect 33 0 35 0;
+#X connect 34 0 39 0;
+#X connect 35 0 34 0;
+#X connect 36 0 31 0;
+#X connect 37 0 30 0;
+#X connect 37 0 38 0;
+#X connect 39 0 37 0;
diff --git a/pd/doc/3.audio.examples/46.PART7.filters.pd b/pd/doc/3.audio.examples/46.PART7.filters.pd
new file mode 100644
index 00000000..4a64a0b5
--- /dev/null
+++ b/pd/doc/3.audio.examples/46.PART7.filters.pd
@@ -0,0 +1,72 @@
+#N canvas 124 28 964 549 12;
+#X floatatom 20 391;
+#X floatatom 297 394;
+#X floatatom 210 394;
+#X graph graph1 0 -1 400 1 544 335 944 35;
+#X array orig 400 float;
+#X array lop 400 float;
+#X array hip 400 float;
+#X array bp 400 float;
+#X pop;
+#X obj 334 457 r metro;
+#X obj 180 322 hip~ 100;
+#X obj 105 320 lop~ 100;
+#X obj 273 322 bp~ 100 10;
+#X floatatom 255 245;
+#X floatatom 338 295;
+#X floatatom 118 393;
+#X text 9 11 Filters;
+#X msg 87 130 \; pd dsp 1 \; metro bang;
+#X msg 191 135 \; pd dsp 0;
+#X text 15 417 Original;
+#X text 122 417 Low pass;
+#X text 210 417 High pass;
+#X text 298 416 Band pass;
+#X obj 14 517 tabwrite~ orig;
+#X obj 309 520 tabwrite~ bp;
+#X obj 213 520 tabwrite~ hip;
+#X obj 116 519 tabwrite~ lop;
+#X text 97 110 start;
+#X text 193 111 stop;
+#X floatatom 14 235;
+#X obj 14 261 osc~ 440;
+#X text 51 236 <-- change frequency;
+#X text 294 247 <-- center/rolloff frequency;
+#X text 374 297 <-- Q;
+#X text 548 341 This graph holds 4 arrays for the input and three filter outputs.;
+#X text 375 389 RMS amplitudes of the original signal and the three filter outputs;
+#X text 746 536 updated for Pd version 0.26;
+#X obj 334 482 metro 1000;
+#X text 29 33 Pd provides low \, high \, and band pass filters \, as shown here. By changing the test frequency \, the filter frequency \, and the "Q" value \, you can see how these filters affect the amplitude and phase of incoming signals;
+#X obj 20 366 env~ 4096;
+#X obj 118 368 env~ 4096;
+#X obj 210 369 env~ 4096;
+#X obj 297 369 env~ 4096;
+#X obj 91 213 print~;
+#X text 456 434 Notice how the phase of the graphed sinusoids slips back and forth... this is because graphing always starts on the nearest 64-sample boundary to the time the metronome fires. If you run at 48K the slippage disappears \, because then the metronome fires every 48K samples \, which is a multiple of 64;
+#X connect 4 0 32 0;
+#X connect 5 0 20 0;
+#X connect 5 0 36 0;
+#X connect 6 0 21 0;
+#X connect 6 0 35 0;
+#X connect 7 0 19 0;
+#X connect 7 0 37 0;
+#X connect 8 0 6 1;
+#X connect 8 0 5 1;
+#X connect 8 0 7 1;
+#X connect 9 0 7 2;
+#X connect 24 0 25 0;
+#X connect 25 0 18 0;
+#X connect 25 0 6 0;
+#X connect 25 0 5 0;
+#X connect 25 0 7 0;
+#X connect 25 0 34 0;
+#X connect 25 0 38 0;
+#X connect 32 0 18 0;
+#X connect 32 0 21 0;
+#X connect 32 0 20 0;
+#X connect 32 0 19 0;
+#X connect 34 0 0 0;
+#X connect 35 0 10 0;
+#X connect 36 0 2 0;
+#X connect 37 0 1 0;
diff --git a/pd/doc/3.audio.examples/47.bandpass.pd b/pd/doc/3.audio.examples/47.bandpass.pd
new file mode 100644
index 00000000..0ce9cc47
--- /dev/null
+++ b/pd/doc/3.audio.examples/47.bandpass.pd
@@ -0,0 +1,146 @@
+#N canvas 58 17 943 765 12;
+#X floatatom 91 731 0 0 0;
+#X graph graph1 0 -1 155947 1 624 401 824 251;
+#X array array1 155948 float 0;
+#X pop;
+#X floatatom 247 705 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 406 192 t b;
+#X obj 406 132 f;
+#X obj 406 72 inlet;
+#X text 413 35 mute;
+#X obj 406 222 f;
+#X msg 510 214 0;
+#X msg 406 102 bang;
+#X obj 406 162 moses 1;
+#X obj 510 184 t b f;
+#X obj 476 140 moses 1;
+#X obj 100 178 dbtorms;
+#X obj 476 110 r master-lvl;
+#X obj 100 50 r master-lvl;
+#X obj 406 252 s master-lvl;
+#X obj 26 217 inlet~;
+#X obj 239 49 inlet;
+#X text 239 22 level;
+#X obj 239 120 s master-lvl;
+#X msg 115 78 set \$1;
+#X obj 115 107 outlet;
+#X msg 257 77 \; pd dsp 1;
+#X obj 100 233 line~;
+#X obj 26 254 *~;
+#X obj 26 289 dac~;
+#X obj 100 205 pack 0 50;
+#X text 24 190 audio;
+#X text 112 132 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 210 740 pd output;
+#X msg 280 707 MUTE;
+#X text 320 706 <-- output amplitude;
+#X obj 206 654 hip~ 5;
+#X obj 31 545 tabread4~ array1;
+#X obj 31 340 r totsamps;
+#X obj 31 430 /;
+#X obj 206 625 bp~;
+#X obj 206 596 bp~;
+#X obj 216 463 mtof;
+#X floatatom 216 491 0 0 0;
+#X floatatom 319 570 0 0 0;
+#X obj 319 541 r q;
+#X floatatom 216 434 0 0 0;
+#X msg 227 550 \; p set \$1;
+#X obj 216 406 r p;
+#X obj 90 647 env~ 4096;
+#X obj 31 487 *~ 0;
+#X obj 31 458 phasor~ 0;
+#X obj 31 516 +~ 1;
+#X obj 227 521 ftom;
+#X msg 826 49 \; pd dsp 0;
+#X obj 90 674 + 0.5;
+#X obj 91 702 int;
+#X msg 31 400 44100;
+#X obj 31 368 t b f;
+#X text 648 14 START;
+#X text 845 19 STOP;
+#X text 88 11 BANDPASS FILTERS;
+#X text 12 41 In this example we use two cascaded bandpass filters
+to troll for partials in Jonathan Harvey's famous bell sample.;
+#X obj 78 487 r totsamps;
+#X text 14 82 Note that filters can give unexpected level changes.
+The bp~ object is designed to have roughly unit gain at teh pass band
+\, so the higher you set "Q" the more amplitude is lost. YOu can correct
+for this by pushing the output amplitude \, but be sure to remember
+to reset the output amplitude before you reduce Q again. I set the
+Q to 100 and the output amplitude to 110 or 120 (with the room gain
+way down.) Then holding the shift key \, slowly drag the center pitch
+upward listening for modes.;
+#X text 16 233 You can hear partials around 48 \, 51.3 \, 55 (faint!)
+\, 57 (fainter!) \, 60 \, two beating partials around 65 \, 67 \, 69
+\, 70.9 \, 71.75 \, 72.6 \, 74 \, 74.65 \, 75.6 \, 77 \, 81.2 \, 84.6
+\, 86.5 \, and probably many more. There's also one down at 36 \, but
+it's easier to see it on the meter than hear it.;
+#X text 271 436 <-- center pitch;
+#X text 281 450 (shift-drag to fine tune);
+#X text 292 492 <-- center frequency;
+#X text 359 570 <-- Q (filter selectivity);
+#X obj 683 453 r readfile;
+#X obj 683 530 soundfiler;
+#X msg 683 503 read -resize \$1 array1;
+#X obj 683 478 symbol;
+#X msg 553 48 \; readfile ../sound/bell.aiff \; totsamps 143718 \;
+p 69 \; q 0 \; pd dsp 1;
+#X text 681 739 updated for Pd version 0.33;
+#X connect 2 0 3 1;
+#X connect 3 0 2 0;
+#X connect 4 0 3 2;
+#X connect 6 0 3 0;
+#X connect 7 0 11 0;
+#X connect 8 0 28 0;
+#X connect 9 0 21 0;
+#X connect 10 0 6 0;
+#X connect 10 0 19 0;
+#X connect 11 0 10 0;
+#X connect 12 0 13 0;
+#X connect 13 0 11 1;
+#X connect 13 0 10 1;
+#X connect 13 0 23 0;
+#X connect 14 0 11 2;
+#X connect 14 0 10 2;
+#X connect 15 0 14 0;
+#X connect 16 0 12 0;
+#X connect 18 0 16 0;
+#X connect 19 0 25 0;
+#X connect 20 0 22 0;
+#X connect 21 0 20 0;
+#X connect 22 0 7 0;
+#X connect 23 0 17 0;
+#X connect 25 0 26 0;
+#X connect 26 0 0 0;
+#X connect 27 0 9 0;
+#X connect 28 0 27 0;
+#X connect 28 1 9 1;
+#X connect 33 0 20 1;
+#X connect 40 0 43 0;
+#X connect 42 0 41 0;
+#X connect 43 0 42 0;
diff --git a/pd/doc/3.audio.examples/48.filter.sweep.pd b/pd/doc/3.audio.examples/48.filter.sweep.pd
new file mode 100644
index 00000000..0d478275
--- /dev/null
+++ b/pd/doc/3.audio.examples/48.filter.sweep.pd
@@ -0,0 +1,173 @@
+#N canvas 194 58 693 586 12;
+#X text 437 559 updated for Pd version 0.26;
+#X floatatom 70 539 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 41 567 pd output;
+#X msg 99 539 MUTE;
+#X text 138 538 <-- output amplitude;
+#X obj 41 212 line~;
+#X floatatom 41 164 0 0 0;
+#X obj 41 188 pack 0 100;
+#X obj 528 110 loadbang;
+#X text 35 6 SWEEPING FILTERS;
+#N canvas 0 0 600 392 conversion-tables 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array dbtorms 123 float 1;
+#A 0 0 0 1.25893e-05 1.41254e-05 1.58489e-05 1.77828e-05 1.99526e-05
+2.23872e-05 2.51189e-05 2.81838e-05 3.16228e-05 3.54813e-05 3.98107e-05
+4.46684e-05 5.01187e-05 5.62341e-05 6.30957e-05 7.07946e-05 7.94328e-05
+8.91251e-05 1e-04 0.000112202 0.000125893 0.000141254 0.000158489 0.000177828
+0.000199526 0.000223872 0.000251189 0.000281838 0.000316228 0.000354813
+0.000398107 0.000446684 0.000501187 0.000562341 0.000630957 0.000707946
+0.000794328 0.000891251 0.001 0.00112202 0.00125893 0.00141254 0.00158489
+0.00177828 0.00199526 0.00223872 0.00251189 0.00281838 0.00316228 0.00354813
+0.00398107 0.00446684 0.00501187 0.00562341 0.00630957 0.00707946 0.00794328
+0.00891251 0.01 0.0112202 0.0125893 0.0141254 0.0158489 0.0177828 0.0199526
+0.0223872 0.0251189 0.0281838 0.0316228 0.0354813 0.0398107 0.0446684
+0.0501187 0.0562341 0.0630957 0.0707946 0.0794328 0.0891251 0.1 0.112202
+0.125893 0.141254 0.158489 0.177828 0.199526 0.223872 0.251189 0.281838
+0.316228 0.354813 0.398107 0.446684 0.501187 0.562341 0.630957 0.707946
+0.794328 0.891251 1 1.12202 1.25893 1.41254 1.58489 1.77828 1.99526
+2.23872 2.51189 2.81838 3.16228 3.54813 3.98107 4.46684 5.01187 5.62341
+6.30957 7.07946 7.94328 8.91251 10 11.2202 12.5893;
+#X coords 0 10 123 0 200 100 1;
+#X restore 302 48 graph;
+#X text 504 141 0;
+#X text 506 41 10;
+#X text 321 151 ------ 123 samples ------;
+#N canvas 0 0 450 300 graph2 0;
+#X array mtof 130 float 1;
+#A 0 8.1758 8.66196 9.17702 9.72272 10.3009 10.9134 11.5623 12.2499
+12.9783 13.75 14.5676 15.4339 16.3516 17.3239 18.354 19.4454 20.6017
+21.8268 23.1247 24.4997 25.9565 27.5 29.1352 30.8677 32.7032 34.6478
+36.7081 38.8909 41.2034 43.6535 46.2493 48.9994 51.9131 55 58.2705
+61.7354 65.4064 69.2957 73.4162 77.7817 82.4069 87.3071 92.4986 97.9989
+103.826 110 116.541 123.471 130.813 138.591 146.832 155.563 164.814
+174.614 184.997 195.998 207.652 220 233.082 246.942 261.626 277.183
+293.665 311.127 329.628 349.228 369.994 391.995 415.305 440 466.164
+493.883 523.251 554.365 587.33 622.254 659.255 698.456 739.989 783.991
+830.609 880 932.328 987.767 1046.5 1108.73 1174.66 1244.51 1318.51
+1396.91 1479.98 1567.98 1661.22 1760 1864.66 1975.53 2093 2217.46 2349.32
+2489.02 2637.02 2793.83 2959.96 3135.96 3322.44 3520 3729.31 3951.07
+4186.01 4434.92 4698.64 4978.03 5274.04 5587.65 5919.91 6271.93 6644.88
+7040 7458.62 7902.13 8372.02 8869.84 9397.27 9956.06 10548.1 11175.3
+11839.8 12543.9 13289.8 14080;
+#X coords 0 12000 130 0 200 100 1;
+#X restore 309 225 graph;
+#X text 319 333 ------ 130 samples ------;
+#X text 518 318 0;
+#X text 520 218 12000;
+#X restore 461 256 pd conversion-tables;
+#X obj 41 260 phasor~;
+#X obj 41 236 tabread4~ mtof;
+#X obj 174 384 +~;
+#X obj 190 361 line~;
+#X obj 190 337 pack 0 100;
+#X floatatom 190 313 0 0 0;
+#X floatatom 174 164 0 0 0;
+#X floatatom 197 238 0 0 0;
+#X obj 41 140 r pitch;
+#X obj 197 214 r depth;
+#X obj 174 140 r speed;
+#X obj 190 289 r offset;
+#X obj 121 465 r q;
+#X floatatom 121 489 0 0 0;
+#X obj 41 484 vcf~;
+#X obj 41 508 hip~ 5;
+#X obj 174 263 *~ 0;
+#X obj 174 188 phasor~ 0;
+#X obj 174 408 tabread4~ mtof;
+#X msg 528 134 \; pitch 48 \; speed -2 \; depth 27 \; offset 56 \;
+q 2;
+#X text 160 490 <-- Q (selectivity);
+#X text 51 277 sawtooth;
+#X text 50 291 oscillator;
+#X text 228 167 <-- sweep speed;
+#X text 265 189 LFO for sweep;
+#X text 251 241 <-- sweep depth;
+#X text 242 316 <-- base center frequency;
+#X text 218 383 add base to sweep;
+#X text 307 408 convert to Hz.;
+#X text 13 28 If you want actively changing center frequencies \, use
+"vcf~" instead of "bp~". The vcf~ module takes an audio signal to set
+center frequency. (Q is still set by messages though.) Vcf is somewhat
+more expensive than bp~.;
+#X text 78 165 <-- pitch;
+#X text 13 95 Note the effect of negative and positive sweep speed.
+;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 5 0 12 0;
+#X connect 6 0 7 0;
+#X connect 7 0 5 0;
+#X connect 8 0 30 0;
+#X connect 11 0 25 0;
+#X connect 12 0 11 0;
+#X connect 13 0 29 0;
+#X connect 14 0 13 1;
+#X connect 15 0 14 0;
+#X connect 16 0 15 0;
+#X connect 17 0 28 0;
+#X connect 18 0 27 1;
+#X connect 19 0 6 0;
+#X connect 20 0 18 0;
+#X connect 21 0 17 0;
+#X connect 22 0 16 0;
+#X connect 23 0 24 0;
+#X connect 24 0 25 2;
+#X connect 25 0 26 0;
+#X connect 26 0 2 0;
+#X connect 27 0 13 0;
+#X connect 28 0 27 0;
+#X connect 29 0 25 1;
diff --git a/pd/doc/3.audio.examples/49.filter.floyd.pd b/pd/doc/3.audio.examples/49.filter.floyd.pd
new file mode 100644
index 00000000..02027117
--- /dev/null
+++ b/pd/doc/3.audio.examples/49.filter.floyd.pd
@@ -0,0 +1,193 @@
+#N canvas 133 190 795 593 12;
+#X floatatom 44 540 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 15 568 pd output;
+#X msg 90 540 MUTE;
+#X text 151 539 <-- output amplitude;
+#X obj 487 217 loadbang;
+#N canvas 0 0 600 392 conversion-tables 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array dbtorms 123 float 1;
+#A 0 0 0 1.25893e-05 1.41254e-05 1.58489e-05 1.77828e-05 1.99526e-05
+2.23872e-05 2.51189e-05 2.81838e-05 3.16228e-05 3.54813e-05 3.98107e-05
+4.46684e-05 5.01187e-05 5.62341e-05 6.30957e-05 7.07946e-05 7.94328e-05
+8.91251e-05 1e-04 0.000112202 0.000125893 0.000141254 0.000158489 0.000177828
+0.000199526 0.000223872 0.000251189 0.000281838 0.000316228 0.000354813
+0.000398107 0.000446684 0.000501187 0.000562341 0.000630957 0.000707946
+0.000794328 0.000891251 0.001 0.00112202 0.00125893 0.00141254 0.00158489
+0.00177828 0.00199526 0.00223872 0.00251189 0.00281838 0.00316228 0.00354813
+0.00398107 0.00446684 0.00501187 0.00562341 0.00630957 0.00707946 0.00794328
+0.00891251 0.01 0.0112202 0.0125893 0.0141254 0.0158489 0.0177828 0.0199526
+0.0223872 0.0251189 0.0281838 0.0316228 0.0354813 0.0398107 0.0446684
+0.0501187 0.0562341 0.0630957 0.0707946 0.0794328 0.0891251 0.1 0.112202
+0.125893 0.141254 0.158489 0.177828 0.199526 0.223872 0.251189 0.281838
+0.316228 0.354813 0.398107 0.446684 0.501187 0.562341 0.630957 0.707946
+0.794328 0.891251 1 1.12202 1.25893 1.41254 1.58489 1.77828 1.99526
+2.23872 2.51189 2.81838 3.16228 3.54813 3.98107 4.46684 5.01187 5.62341
+6.30957 7.07946 7.94328 8.91251 10 11.2202 12.5893;
+#X coords 0 10 123 0 200 100 1;
+#X restore 302 48 graph;
+#X text 504 141 0;
+#X text 506 41 10;
+#X text 321 151 ------ 123 samples ------;
+#N canvas 0 0 450 300 graph2 0;
+#X array mtof 130 float 1;
+#A 0 8.1758 8.66196 9.17702 9.72272 10.3009 10.9134 11.5623 12.2499
+12.9783 13.75 14.5676 15.4339 16.3516 17.3239 18.354 19.4454 20.6017
+21.8268 23.1247 24.4997 25.9565 27.5 29.1352 30.8677 32.7032 34.6478
+36.7081 38.8909 41.2034 43.6535 46.2493 48.9994 51.9131 55 58.2705
+61.7354 65.4064 69.2957 73.4162 77.7817 82.4069 87.3071 92.4986 97.9989
+103.826 110 116.541 123.471 130.813 138.591 146.832 155.563 164.814
+174.614 184.997 195.998 207.652 220 233.082 246.942 261.626 277.183
+293.665 311.127 329.628 349.228 369.994 391.995 415.305 440 466.164
+493.883 523.251 554.365 587.33 622.254 659.255 698.456 739.989 783.991
+830.609 880 932.328 987.767 1046.5 1108.73 1174.66 1244.51 1318.51
+1396.91 1479.98 1567.98 1661.22 1760 1864.66 1975.53 2093 2217.46 2349.32
+2489.02 2637.02 2793.83 2959.96 3135.96 3322.44 3520 3729.31 3951.07
+4186.01 4434.92 4698.64 4978.03 5274.04 5587.65 5919.91 6271.93 6644.88
+7040 7458.62 7902.13 8372.02 8869.84 9397.27 9956.06 10548.1 11175.3
+11839.8 12543.9 13289.8 14080;
+#X coords 0 12000 130 0 200 100 1;
+#X restore 309 225 graph;
+#X text 319 333 ------ 130 samples ------;
+#X text 518 318 0;
+#X text 520 218 12000;
+#X restore 490 428 pd conversion-tables;
+#X obj 250 308 line~;
+#X obj 250 284 pack 0 100;
+#X floatatom 251 213 0 0 0;
+#X obj 171 413 r q;
+#X floatatom 171 437 0 0 0;
+#X obj 15 492 vcf~;
+#X obj 15 516 hip~ 5;
+#X obj 250 333 tabread4~ mtof;
+#X text 214 436 <-- Q (selectivity);
+#X text 277 354 convert to Hz.;
+#X text 35 6 ANOTHER SWEEPING FILTER EXAMPLE;
+#X obj 15 286 clip~ 0 0.5;
+#X obj 15 310 *~ 2;
+#X obj 15 334 -~;
+#X text 121 270 trick to;
+#X text 121 291 make symmetric;
+#X text 121 312 triangle wave;
+#X obj 31 161 f;
+#X obj 64 159 + 1;
+#X obj 31 211 tabread array1;
+#X obj 31 235 mtof;
+#X obj 31 113 r metro;
+#X obj 64 183 mod 8;
+#X obj 31 259 phasor~ 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array array1 8 float 0;
+#X coords 0 96 8 36 200 100 1;
+#X restore 464 75 graph;
+#X obj 251 189 r cf;
+#X text 293 210 <-- center frequency;
+#X obj 31 137 metro 85;
+#X obj 15 376 hip~ 5000;
+#X obj 15 399 *~ 100;
+#X obj 252 237 moses 61;
+#X msg 251 260 61;
+#X msg 487 241 \; cf 61 \; q 30 \; metro 1 \; array1 0 45 48 50 48
+55 53 55 57;
+#X text 13 28 Here's an approximate reconstruction of an old riff by
+Pink Floyd (I haven't checked the tempo or transposition against the
+original yet.) Because we're filtering a waveform with odd partials
+\, it's easier to pick out the partials in the filtered sound.;
+#X text 104 352 Here we fudge;
+#X text 100 371 to better imitate;
+#X text 100 390 the EMS3 bandpass;
+#X text 85 403 sound;
+#X text 340 231 protect against;
+#X text 341 252 hitting the;
+#X text 341 271 fundamental;
+#X text 137 139 sequencer for;
+#X text 137 158 8 note loop;
+#X obj 171 459 moses 1;
+#X msg 144 459 1;
+#X text 241 460 speaker protection;
+#X text 437 558 updated for Pd version 0.35;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 4 0 38 0;
+#X connect 6 0 13 0;
+#X connect 7 0 6 0;
+#X connect 8 0 36 0;
+#X connect 9 0 10 0;
+#X connect 10 0 49 0;
+#X connect 11 0 12 0;
+#X connect 12 0 1 0;
+#X connect 13 0 11 1;
+#X connect 17 0 18 0;
+#X connect 18 0 19 0;
+#X connect 19 0 34 0;
+#X connect 23 0 24 0;
+#X connect 23 0 25 0;
+#X connect 24 0 28 0;
+#X connect 25 0 26 0;
+#X connect 26 0 29 0;
+#X connect 27 0 33 0;
+#X connect 28 0 23 1;
+#X connect 29 0 17 0;
+#X connect 29 0 19 1;
+#X connect 31 0 8 0;
+#X connect 33 0 23 0;
+#X connect 34 0 35 0;
+#X connect 35 0 11 0;
+#X connect 36 0 37 0;
+#X connect 36 1 7 0;
+#X connect 37 0 7 0;
+#X connect 49 0 50 0;
+#X connect 49 1 11 2;
+#X connect 50 0 11 2;
diff --git a/pd/doc/3.audio.examples/50.filter.noise.pd b/pd/doc/3.audio.examples/50.filter.noise.pd
new file mode 100644
index 00000000..7421c180
--- /dev/null
+++ b/pd/doc/3.audio.examples/50.filter.noise.pd
@@ -0,0 +1,196 @@
+#N canvas 137 175 735 587 12;
+#X text 437 559 updated for Pd version 0.26;
+#X floatatom 67 397 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 38 425 pd output;
+#X msg 96 397 MUTE;
+#X text 135 396 <-- output amplitude;
+#X obj 515 150 loadbang;
+#N canvas 0 0 600 392 conversion-tables 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array dbtorms 123 float 1;
+#A 0 0 0 1.25893e-05 1.41254e-05 1.58489e-05 1.77828e-05 1.99526e-05
+2.23872e-05 2.51189e-05 2.81838e-05 3.16228e-05 3.54813e-05 3.98107e-05
+4.46684e-05 5.01187e-05 5.62341e-05 6.30957e-05 7.07946e-05 7.94328e-05
+8.91251e-05 1e-04 0.000112202 0.000125893 0.000141254 0.000158489 0.000177828
+0.000199526 0.000223872 0.000251189 0.000281838 0.000316228 0.000354813
+0.000398107 0.000446684 0.000501187 0.000562341 0.000630957 0.000707946
+0.000794328 0.000891251 0.001 0.00112202 0.00125893 0.00141254 0.00158489
+0.00177828 0.00199526 0.00223872 0.00251189 0.00281838 0.00316228 0.00354813
+0.00398107 0.00446684 0.00501187 0.00562341 0.00630957 0.00707946 0.00794328
+0.00891251 0.01 0.0112202 0.0125893 0.0141254 0.0158489 0.0177828 0.0199526
+0.0223872 0.0251189 0.0281838 0.0316228 0.0354813 0.0398107 0.0446684
+0.0501187 0.0562341 0.0630957 0.0707946 0.0794328 0.0891251 0.1 0.112202
+0.125893 0.141254 0.158489 0.177828 0.199526 0.223872 0.251189 0.281838
+0.316228 0.354813 0.398107 0.446684 0.501187 0.562341 0.630957 0.707946
+0.794328 0.891251 1 1.12202 1.25893 1.41254 1.58489 1.77828 1.99526
+2.23872 2.51189 2.81838 3.16228 3.54813 3.98107 4.46684 5.01187 5.62341
+6.30957 7.07946 7.94328 8.91251 10 11.2202 12.5893;
+#X coords 0 10 123 0 200 100 1;
+#X restore 70 45 graph;
+#X text 272 138 0;
+#X text 274 38 10;
+#X text 89 148 ------ 123 samples ------;
+#N canvas 0 0 450 300 graph2 0;
+#X array mtof 130 float 1;
+#A 0 8.1758 8.66196 9.17702 9.72272 10.3009 10.9134 11.5623 12.2499
+12.9783 13.75 14.5676 15.4339 16.3516 17.3239 18.354 19.4454 20.6017
+21.8268 23.1247 24.4997 25.9565 27.5 29.1352 30.8677 32.7032 34.6478
+36.7081 38.8909 41.2034 43.6535 46.2493 48.9994 51.9131 55 58.2705
+61.7354 65.4064 69.2957 73.4162 77.7817 82.4069 87.3071 92.4986 97.9989
+103.826 110 116.541 123.471 130.813 138.591 146.832 155.563 164.814
+174.614 184.997 195.998 207.652 220 233.082 246.942 261.626 277.183
+293.665 311.127 329.628 349.228 369.994 391.995 415.305 440 466.164
+493.883 523.251 554.365 587.33 622.254 659.255 698.456 739.989 783.991
+830.609 880 932.328 987.767 1046.5 1108.73 1174.66 1244.51 1318.51
+1396.91 1479.98 1567.98 1661.22 1760 1864.66 1975.53 2093 2217.46 2349.32
+2489.02 2637.02 2793.83 2959.96 3135.96 3322.44 3520 3729.31 3951.07
+4186.01 4434.92 4698.64 4978.03 5274.04 5587.65 5919.91 6271.93 6644.88
+7040 7458.62 7902.13 8372.02 8869.84 9397.27 9956.06 10548.1 11175.3
+11839.8 12543.9 13289.8 14080;
+#X coords 0 12000 130 0 200 100 1;
+#X restore 77 222 graph;
+#X text 87 330 ------ 130 samples ------;
+#X text 286 315 0;
+#X text 288 215 12000;
+#N canvas 244 212 672 338 regenerate-tables 0;
+#X msg 415 84 bang;
+#X obj 415 113 t b b;
+#X obj 474 177 f;
+#X obj 512 177 + 1;
+#X msg 483 147 0;
+#X obj 415 142 until;
+#X obj 474 211 t f f;
+#X obj 414 238 mtof;
+#X obj 405 202 sel 129;
+#X obj 413 264 tabwrite mtof;
+#X obj 35 227 moses 2;
+#X msg 19 76 bang;
+#X obj 19 105 t b b;
+#X obj 90 166 f;
+#X obj 128 166 + 1;
+#X msg 112 138 0;
+#X obj 19 134 until;
+#X obj 11 194 sel 122;
+#X msg 35 258 0;
+#X obj 79 259 dbtorms;
+#X obj 90 194 t f f;
+#X obj 35 291 tabwrite dbtorms;
+#X text 18 49 bang to recalculate dbtorms table;
+#X text 356 50 bang to recalculate the mtof table;
+#X connect 0 0 1 0;
+#X connect 1 0 5 0;
+#X connect 1 1 4 0;
+#X connect 2 0 3 0;
+#X connect 2 0 6 0;
+#X connect 2 0 8 0;
+#X connect 3 0 2 1;
+#X connect 4 0 2 1;
+#X connect 5 0 2 0;
+#X connect 6 0 7 0;
+#X connect 6 1 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 5 1;
+#X connect 10 0 18 0;
+#X connect 10 1 19 0;
+#X connect 11 0 12 0;
+#X connect 12 0 16 0;
+#X connect 12 1 15 0;
+#X connect 13 0 14 0;
+#X connect 13 0 17 0;
+#X connect 13 0 20 0;
+#X connect 14 0 13 1;
+#X connect 15 0 13 1;
+#X connect 16 0 13 0;
+#X connect 17 0 16 1;
+#X connect 18 0 21 0;
+#X connect 19 0 21 0;
+#X connect 20 0 10 0;
+#X connect 20 1 21 1;
+#X restore 375 76 pd regenerate-tables;
+#X restore 449 418 pd conversion-tables;
+#X obj 49 201 line~;
+#X obj 49 177 pack 0 100;
+#X floatatom 49 151 0 0 0;
+#X obj 88 254 r q;
+#X floatatom 88 278 0 0 0;
+#X obj 38 342 vcf~;
+#X obj 38 366 hip~ 5;
+#X obj 49 226 tabread4~ mtof;
+#X text 124 277 <-- Q (selectivity);
+#X obj 49 127 r cf;
+#X text 84 145 <-- center frequency;
+#X text 35 6 FILTERING NOISE;
+#X obj 39 102 noise~;
+#X msg 515 174 \; cf 60 \; q 3 \;;
+#X text 13 28 THe noise~ module puts out unit-amplitude white noise.
+Be careful again here about surging amplitudes if Q hits zero.;
+#X obj 88 305 moses 1;
+#X msg 60 305 1;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 5 0 20 0;
+#X connect 7 0 14 0;
+#X connect 8 0 7 0;
+#X connect 9 0 8 0;
+#X connect 10 0 11 0;
+#X connect 11 0 22 0;
+#X connect 12 0 13 0;
+#X connect 13 0 2 0;
+#X connect 14 0 12 1;
+#X connect 16 0 9 0;
+#X connect 19 0 12 0;
+#X connect 22 0 23 0;
+#X connect 22 1 12 2;
+#X connect 23 0 12 2;
diff --git a/pd/doc/3.audio.examples/51.ring.modulation.pd b/pd/doc/3.audio.examples/51.ring.modulation.pd
new file mode 100644
index 00000000..0380a07d
--- /dev/null
+++ b/pd/doc/3.audio.examples/51.ring.modulation.pd
@@ -0,0 +1,153 @@
+#N canvas 1 23 809 567 12;
+#X graph graph1 0 -1.02 882 1.02 542 471 742 341;
+#X array pulse-output 882 float 0;
+#X pop;
+#X floatatom 101 331 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 63 359 pd output;
+#X msg 139 332 MUTE;
+#X text 179 331 <-- output amplitude;
+#X floatatom 63 48 0 0 0;
+#X obj 63 303 hip~ 5;
+#X graph graph1 0 0 128 1000 490 259 746 129;
+#X array spectrum 128 float 0;
+#X pop;
+#X obj 129 278 tabwrite~ pulse-output;
+#X msg 129 243 bang;
+#X text 170 244 <-- click to graph;
+#N canvas 204 17 358 234 fft 0;
+#X obj 46 48 inlet~;
+#X obj 159 181 tabwrite~ spectrum;
+#X obj 159 145 inlet;
+#X obj 46 78 rfft~;
+#X obj 46 111 *~;
+#X obj 77 111 *~;
+#X obj 46 141 sqrt~;
+#X obj 191 45 block~ 2048 1;
+#X connect 0 0 3 0;
+#X connect 2 0 1 0;
+#X connect 3 0 4 0;
+#X connect 3 0 4 1;
+#X connect 3 1 5 0;
+#X connect 3 1 5 1;
+#X connect 4 0 6 0;
+#X connect 5 0 6 0;
+#X connect 6 0 1 0;
+#X restore 74 281 pd fft;
+#X text 488 265 0;
+#X obj 79 194 osc~ 0;
+#N canvas 0 0 600 384 pulse-train 0;
+#X obj 185 162 line~;
+#X obj 39 251 cos~;
+#X obj 185 138 pack 0 50;
+#X obj 40 178 -~ 0.5;
+#X obj 40 204 *~;
+#X obj 185 63 / 10;
+#X obj 185 87 moses 0;
+#X msg 185 111 0;
+#X obj 39 227 clip~ -0.5 0.5;
+#X obj 185 187 +~ 1;
+#X obj 38 274 +~ 1;
+#X obj 184 38 inlet;
+#X obj 38 299 outlet~;
+#X obj 40 154 phasor~ 344.532;
+#X text 51 13 This is the pulse train generator from example 17;
+#X connect 0 0 9 0;
+#X connect 1 0 10 0;
+#X connect 2 0 0 0;
+#X connect 3 0 4 0;
+#X connect 4 0 8 0;
+#X connect 5 0 6 0;
+#X connect 6 0 7 0;
+#X connect 6 1 2 0;
+#X connect 7 0 2 0;
+#X connect 8 0 1 0;
+#X connect 9 0 4 1;
+#X connect 10 0 12 0;
+#X connect 11 0 5 0;
+#X connect 13 0 3 0;
+#X restore 63 75 pd pulse-train;
+#X floatatom 79 168 0 0 0;
+#X text 124 167 <-- modulation frequency;
+#X text 102 48 <-- bandwidth;
+#X obj 63 218 *~;
+#X text 736 262 2656 Hz.;
+#X text 196 77 fundamental is 344.5 Hz;
+#X text 157 210 an oscillator...;
+#X text 66 22 RING MODULATION;
+#X text 542 473 ---- 0.02 seconds ----;
+#X text 528 533 updated for Pd version 0.34;
+#X text 156 195 Just multiply by;
+#X text 12 422 Ring modulation is just multiplication by an oscillator.
+This patch shows the effect of ring modulation on a pulse train. When
+bandwidth is high and modulation frequency is moderately low \, you
+see the spectrum reflect off the "y axis".;
+#X obj 79 140 * 344.5;
+#X floatatom 79 114 0 0 0;
+#X text 120 114 <-- modulation frequency as;
+#X text 159 128 multiple of fundamental;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 5 0 14 0;
+#X connect 6 0 2 0;
+#X connect 9 0 8 0;
+#X connect 9 0 11 1;
+#X connect 13 0 18 1;
+#X connect 14 0 18 0;
+#X connect 15 0 13 0;
+#X connect 18 0 11 0;
+#X connect 18 0 6 0;
+#X connect 18 0 8 0;
+#X connect 27 0 15 0;
+#X connect 28 0 27 0;
diff --git a/pd/doc/3.audio.examples/52.ssb.modulation.pd b/pd/doc/3.audio.examples/52.ssb.modulation.pd
new file mode 100644
index 00000000..968b3b45
--- /dev/null
+++ b/pd/doc/3.audio.examples/52.ssb.modulation.pd
@@ -0,0 +1,150 @@
+#N canvas 7 6 923 594 12;
+#X obj 170 349 phasor~ 0;
+#X obj 170 393 cos~;
+#X obj 206 371 +~ -0.25;
+#X obj 206 394 cos~;
+#X obj 23 438 *~;
+#X obj 88 438 *~;
+#X obj 22 462 -~;
+#X floatatom 170 322 0 0 0;
+#X obj 23 238 tabread4~ array1;
+#X obj 23 67 r totsamps;
+#X obj 23 142 /;
+#X obj 23 190 *~ 0;
+#X obj 23 166 phasor~ 0;
+#X obj 23 214 +~ 1;
+#X msg 23 117 44100;
+#X obj 23 91 t b f;
+#X obj 62 190 r totsamps;
+#X msg 636 38 \; pd dsp 0;
+#X text 429 14 START;
+#X text 653 20 STOP;
+#X floatatom 51 499 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 22 527 pd output;
+#X msg 87 500 MUTE;
+#X text 121 499 <-- output amplitude;
+#N canvas 0 0 600 388 hilbert 0;
+#X obj 166 190 biquad~ 0.83774 -0.06338 0.06338 -0.83774 1;
+#X obj 166 164 biquad~ 1.94632 -0.94657 0.94657 -1.94632 1;
+#X obj 99 111 biquad~ -0.02569 0.260502 -0.260502 0.02569 1;
+#X obj 99 137 biquad~ 1.8685 -0.870686 0.870686 -1.8685 1;
+#X obj 98 76 inlet~;
+#X obj 166 213 outlet~;
+#X obj 99 213 outlet~;
+#X text 95 261 This is a pair of all-pass filters whose outputs somehow
+manage to be about 90 degrees out of phase from each other. I don't
+know what phase relation they have with the original signal. I adapted
+this from a 4X patch by Emmanuel Favreau \, circa 1982;
+#X connect 0 0 5 0;
+#X connect 1 0 0 0;
+#X connect 2 0 3 0;
+#X connect 3 0 6 0;
+#X connect 4 0 1 0;
+#X connect 4 0 2 0;
+#X restore 23 400 pd hilbert;
+#X graph graph1 0 -1 155947 1 441 284 641 134;
+#X array array1 155948 float 0;
+#X pop;
+#X text 36 257 sample loop for;
+#X text 36 271 test signal;
+#X text 35 314 pair of allpass;
+#X text 34 333 filters to make;
+#X text 34 353 90 degree phase;
+#X text 32 373 shifted versions;
+#X text 201 323 <-- shift frequency;
+#X text 122 438 <-- complex multiply;
+#X text 123 452 (calculate real part);
+#X text 161 412 cosine and sine waves;
+#X text 55 7 SINGLE SIDEBAND MODULATION;
+#X text 55 26 (AKA FREQUENCY SHIFTING);
+#X text 394 296 The signal sideband modulator gives you only one sideband
+for each frequency in teh input signal (whereas ring modulation gave
+both a positie and negative sideband.) You can set the shift frequency
+positive to shift all frequencies upward \, or negative to shift them
+downwards.;
+#X obj 484 417 r readfile;
+#X msg 334 34 \; readfile ../sound/bell.aiff \; pd dsp 1;
+#X obj 484 444 symbol;
+#X msg 483 470 read -resize \$1 array1;
+#X obj 483 496 soundfiler;
+#X obj 483 521 s totsamps;
+#X text 671 568 updated for Pd version 0.33;
+#X connect 0 0 2 0;
+#X connect 0 0 1 0;
+#X connect 1 0 4 1;
+#X connect 2 0 3 0;
+#X connect 3 0 5 1;
+#X connect 4 0 6 0;
+#X connect 5 0 6 1;
+#X connect 6 0 21 0;
+#X connect 7 0 0 0;
+#X connect 8 0 24 0;
+#X connect 9 0 15 0;
+#X connect 10 0 12 0;
+#X connect 11 0 13 0;
+#X connect 12 0 11 0;
+#X connect 13 0 8 0;
+#X connect 14 0 10 0;
+#X connect 15 0 14 0;
+#X connect 15 1 10 1;
+#X connect 16 0 11 1;
+#X connect 20 0 21 1;
+#X connect 21 0 20 0;
+#X connect 22 0 21 2;
+#X connect 24 0 4 0;
+#X connect 24 1 5 0;
+#X connect 39 0 41 0;
+#X connect 41 0 42 0;
+#X connect 42 0 43 0;
+#X connect 43 0 44 0;
diff --git a/pd/doc/3.audio.examples/53.delays.pd b/pd/doc/3.audio.examples/53.delays.pd
new file mode 100644
index 00000000..5c4f20de
--- /dev/null
+++ b/pd/doc/3.audio.examples/53.delays.pd
@@ -0,0 +1,225 @@
+#N canvas 22 1 729 584 12;
+#X floatatom 62 506 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 33 534 pd output;
+#X msg 91 506 MUTE;
+#X text 130 505 <-- output amplitude;
+#X obj 33 482 hip~ 5;
+#X text 92 12 DELAYS;
+#X obj 33 341 -~;
+#X obj 30 241 tabread4~ mtof;
+#X obj 33 317 *~ 3;
+#X obj 69 318 *~ 2;
+#X obj 49 266 phasor~;
+#X floatatom 81 215 0 0 0;
+#X obj 33 293 clip~ 0 0.667;
+#X obj 30 190 line~;
+#X obj 30 165 pack 0 1000;
+#X obj 28 64 metro 1000;
+#X obj 28 88 random 200;
+#X obj 29 114 - 100;
+#X obj 30 141 * 0.001;
+#X obj 33 453 +~;
+#X obj 44 374 delwrite~ delay1 2000;
+#X floatatom 49 401 0 0 0;
+#X obj 49 426 delread~ delay1 1000;
+#X obj 27 41 loadbang;
+#X text 210 37 You can delay a signal using the delwrite~ and delread~
+objects. In this example \, the pitch of the oscillator is varying
+slightly so that the delayed signal is different from the straight
+signal.;
+#X text 212 99 delread always delays the signal an integer number of
+samples and does no interpolation.;
+#X text 211 137 The delwrite~ object creates the delay line \; you
+give it a name and a size in milliseconds. Each delwrite~ should have
+a different name.;
+#X text 209 184 Delread~'s arguments are the name of a delwrite (of
+which there should be exactly one) and a delay time in milliseconds
+between 0 and the length of the delay line. Each delwrite~ may have
+as many delread~s as you wish \, which function as multiple delay taps.
+;
+#X obj 30 215 +~ 60;
+#X text 112 215 <-- pitch;
+#X text 83 401 <-- delay time;
+#X text 60 341 asymmetric triangle wave;
+#X text 236 372 write to delay line;
+#X text 232 425 read from delay line;
+#X text 59 454 add the original and the delayed signal;
+#N canvas 0 0 600 392 conversion-tables 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array dbtorms 123 float 1;
+#A 0 0 0 1.25893e-05 1.41254e-05 1.58489e-05 1.77828e-05 1.99526e-05
+2.23872e-05 2.51189e-05 2.81838e-05 3.16228e-05 3.54813e-05 3.98107e-05
+4.46684e-05 5.01187e-05 5.62341e-05 6.30957e-05 7.07946e-05 7.94328e-05
+8.91251e-05 1e-04 0.000112202 0.000125893 0.000141254 0.000158489 0.000177828
+0.000199526 0.000223872 0.000251189 0.000281838 0.000316228 0.000354813
+0.000398107 0.000446684 0.000501187 0.000562341 0.000630957 0.000707946
+0.000794328 0.000891251 0.001 0.00112202 0.00125893 0.00141254 0.00158489
+0.00177828 0.00199526 0.00223872 0.00251189 0.00281838 0.00316228 0.00354813
+0.00398107 0.00446684 0.00501187 0.00562341 0.00630957 0.00707946 0.00794328
+0.00891251 0.01 0.0112202 0.0125893 0.0141254 0.0158489 0.0177828 0.0199526
+0.0223872 0.0251189 0.0281838 0.0316228 0.0354813 0.0398107 0.0446684
+0.0501187 0.0562341 0.0630957 0.0707946 0.0794328 0.0891251 0.1 0.112202
+0.125893 0.141254 0.158489 0.177828 0.199526 0.223872 0.251189 0.281838
+0.316228 0.354813 0.398107 0.446684 0.501187 0.562341 0.630957 0.707946
+0.794328 0.891251 1 1.12202 1.25893 1.41254 1.58489 1.77828 1.99526
+2.23872 2.51189 2.81838 3.16228 3.54813 3.98107 4.46684 5.01187 5.62341
+6.30957 7.07946 7.94328 8.91251 10 11.2202 12.5893;
+#X coords 0 10 123 0 200 100 1;
+#X restore 70 45 graph;
+#X text 272 138 0;
+#X text 274 38 10;
+#X text 89 148 ------ 123 samples ------;
+#N canvas 0 0 450 300 graph2 0;
+#X array mtof 130 float 1;
+#A 0 8.1758 8.66196 9.17702 9.72272 10.3009 10.9134 11.5623 12.2499
+12.9783 13.75 14.5676 15.4339 16.3516 17.3239 18.354 19.4454 20.6017
+21.8268 23.1247 24.4997 25.9565 27.5 29.1352 30.8677 32.7032 34.6478
+36.7081 38.8909 41.2034 43.6535 46.2493 48.9994 51.9131 55 58.2705
+61.7354 65.4064 69.2957 73.4162 77.7817 82.4069 87.3071 92.4986 97.9989
+103.826 110 116.541 123.471 130.813 138.591 146.832 155.563 164.814
+174.614 184.997 195.998 207.652 220 233.082 246.942 261.626 277.183
+293.665 311.127 329.628 349.228 369.994 391.995 415.305 440 466.164
+493.883 523.251 554.365 587.33 622.254 659.255 698.456 739.989 783.991
+830.609 880 932.328 987.767 1046.5 1108.73 1174.66 1244.51 1318.51
+1396.91 1479.98 1567.98 1661.22 1760 1864.66 1975.53 2093 2217.46 2349.32
+2489.02 2637.02 2793.83 2959.96 3135.96 3322.44 3520 3729.31 3951.07
+4186.01 4434.92 4698.64 4978.03 5274.04 5587.65 5919.91 6271.93 6644.88
+7040 7458.62 7902.13 8372.02 8869.84 9397.27 9956.06 10548.1 11175.3
+11839.8 12543.9 13289.8 14080;
+#X coords 0 12000 130 0 200 100 1;
+#X restore 77 222 graph;
+#X text 87 330 ------ 130 samples ------;
+#X text 286 315 0;
+#X text 288 215 12000;
+#N canvas 244 212 672 338 regenerate-tables 0;
+#X msg 415 84 bang;
+#X obj 415 113 t b b;
+#X obj 474 177 f;
+#X obj 512 177 + 1;
+#X msg 483 147 0;
+#X obj 415 142 until;
+#X obj 474 211 t f f;
+#X obj 414 238 mtof;
+#X obj 405 202 sel 129;
+#X obj 413 264 tabwrite mtof;
+#X obj 35 227 moses 2;
+#X msg 19 76 bang;
+#X obj 19 105 t b b;
+#X obj 90 166 f;
+#X obj 128 166 + 1;
+#X msg 112 138 0;
+#X obj 19 134 until;
+#X obj 11 194 sel 122;
+#X msg 35 258 0;
+#X obj 79 259 dbtorms;
+#X obj 90 194 t f f;
+#X obj 35 291 tabwrite dbtorms;
+#X text 18 49 bang to recalculate dbtorms table;
+#X text 356 50 bang to recalculate the mtof table;
+#X connect 0 0 1 0;
+#X connect 1 0 5 0;
+#X connect 1 1 4 0;
+#X connect 2 0 3 0;
+#X connect 2 0 6 0;
+#X connect 2 0 8 0;
+#X connect 3 0 2 1;
+#X connect 4 0 2 1;
+#X connect 5 0 2 0;
+#X connect 6 0 7 0;
+#X connect 6 1 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 5 1;
+#X connect 10 0 18 0;
+#X connect 10 1 19 0;
+#X connect 11 0 12 0;
+#X connect 12 0 16 0;
+#X connect 12 1 15 0;
+#X connect 13 0 14 0;
+#X connect 13 0 17 0;
+#X connect 13 0 20 0;
+#X connect 14 0 13 1;
+#X connect 15 0 13 1;
+#X connect 16 0 13 0;
+#X connect 17 0 16 1;
+#X connect 18 0 21 0;
+#X connect 19 0 21 0;
+#X connect 20 0 10 0;
+#X connect 20 1 21 1;
+#X restore 375 76 pd regenerate-tables;
+#X restore 449 418 pd conversion-tables;
+#X text 427 536 updated for Pd version 0.35;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 4 0 1 0;
+#X connect 6 0 19 0;
+#X connect 6 0 20 0;
+#X connect 7 0 10 0;
+#X connect 8 0 6 0;
+#X connect 9 0 6 1;
+#X connect 10 0 9 0;
+#X connect 10 0 12 0;
+#X connect 11 0 28 1;
+#X connect 12 0 8 0;
+#X connect 13 0 28 0;
+#X connect 14 0 13 0;
+#X connect 15 0 16 0;
+#X connect 16 0 17 0;
+#X connect 17 0 18 0;
+#X connect 18 0 14 0;
+#X connect 19 0 4 0;
+#X connect 21 0 22 0;
+#X connect 22 0 19 1;
+#X connect 23 0 15 0;
+#X connect 28 0 7 0;
diff --git a/pd/doc/3.audio.examples/54.delay.loop.pd b/pd/doc/3.audio.examples/54.delay.loop.pd
new file mode 100644
index 00000000..71b35253
--- /dev/null
+++ b/pd/doc/3.audio.examples/54.delay.loop.pd
@@ -0,0 +1,213 @@
+#N canvas 22 1 630 601 12;
+#X text 309 531 updated for Pd version 0.26;
+#X floatatom 58 505 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 29 533 pd output;
+#X msg 87 505 MUTE;
+#X text 126 504 <-- output amplitude;
+#X obj 29 476 hip~ 5;
+#X obj 29 297 -~;
+#X obj 29 273 *~ 3;
+#X obj 66 279 *~ 2;
+#X floatatom 29 177 0 0 0;
+#X obj 29 249 clip~ 0 0.667;
+#X obj 39 450 delwrite~ delay1 2000;
+#X floatatom 45 353 0 0 0;
+#X text 79 176 <-- pitch;
+#X text 75 352 <-- delay time;
+#X text 238 450 write to delay line;
+#X text 226 378 read from delay line;
+#X text 64 426 add the original and the delayed signal;
+#X obj 29 201 mtof;
+#X msg 135 238 1;
+#X obj 29 321 *~;
+#X obj 29 225 phasor~ 0;
+#X obj 135 286 tabread4~ dbtorms;
+#X obj 135 262 adsr 100 100 2000 0 2000;
+#X obj 29 427 +~;
+#X obj 45 377 delread~ delay1 160;
+#X obj 45 401 *~ 0.7;
+#X text 103 401 feedback gain;
+#X text 57 9 DELAYS WITH FEEDBACK;
+#X text 33 39 You can feed the result of a delread~ module back into
+its own delwrite~ \, as long as you're careful about stability. For
+delays below 30 msec \, you can frequently hear teh resonant pitch.
+For longer delay times you get the famous old delay loop effect.;
+#X text 32 118 We've added an amplitude control here so that teh test
+oscillator only speaks while you're dragging the pitch up and down.
+Be sure to try the shift key.;
+#N canvas 0 0 600 392 conversion-tables 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array dbtorms 123 float 1;
+#A 0 0 0 1.25893e-05 1.41254e-05 1.58489e-05 1.77828e-05 1.99526e-05
+2.23872e-05 2.51189e-05 2.81838e-05 3.16228e-05 3.54813e-05 3.98107e-05
+4.46684e-05 5.01187e-05 5.62341e-05 6.30957e-05 7.07946e-05 7.94328e-05
+8.91251e-05 1e-04 0.000112202 0.000125893 0.000141254 0.000158489 0.000177828
+0.000199526 0.000223872 0.000251189 0.000281838 0.000316228 0.000354813
+0.000398107 0.000446684 0.000501187 0.000562341 0.000630957 0.000707946
+0.000794328 0.000891251 0.001 0.00112202 0.00125893 0.00141254 0.00158489
+0.00177828 0.00199526 0.00223872 0.00251189 0.00281838 0.00316228 0.00354813
+0.00398107 0.00446684 0.00501187 0.00562341 0.00630957 0.00707946 0.00794328
+0.00891251 0.01 0.0112202 0.0125893 0.0141254 0.0158489 0.0177828 0.0199526
+0.0223872 0.0251189 0.0281838 0.0316228 0.0354813 0.0398107 0.0446684
+0.0501187 0.0562341 0.0630957 0.0707946 0.0794328 0.0891251 0.1 0.112202
+0.125893 0.141254 0.158489 0.177828 0.199526 0.223872 0.251189 0.281838
+0.316228 0.354813 0.398107 0.446684 0.501187 0.562341 0.630957 0.707946
+0.794328 0.891251 1 1.12202 1.25893 1.41254 1.58489 1.77828 1.99526
+2.23872 2.51189 2.81838 3.16228 3.54813 3.98107 4.46684 5.01187 5.62341
+6.30957 7.07946 7.94328 8.91251 10 11.2202 12.5893;
+#X coords 0 10 123 0 200 100 1;
+#X restore 70 45 graph;
+#X text 272 138 0;
+#X text 274 38 10;
+#X text 89 148 ------ 123 samples ------;
+#N canvas 0 0 450 300 graph2 0;
+#X array mtof 130 float 1;
+#A 0 8.1758 8.66196 9.17702 9.72272 10.3009 10.9134 11.5623 12.2499
+12.9783 13.75 14.5676 15.4339 16.3516 17.3239 18.354 19.4454 20.6017
+21.8268 23.1247 24.4997 25.9565 27.5 29.1352 30.8677 32.7032 34.6478
+36.7081 38.8909 41.2034 43.6535 46.2493 48.9994 51.9131 55 58.2705
+61.7354 65.4064 69.2957 73.4162 77.7817 82.4069 87.3071 92.4986 97.9989
+103.826 110 116.541 123.471 130.813 138.591 146.832 155.563 164.814
+174.614 184.997 195.998 207.652 220 233.082 246.942 261.626 277.183
+293.665 311.127 329.628 349.228 369.994 391.995 415.305 440 466.164
+493.883 523.251 554.365 587.33 622.254 659.255 698.456 739.989 783.991
+830.609 880 932.328 987.767 1046.5 1108.73 1174.66 1244.51 1318.51
+1396.91 1479.98 1567.98 1661.22 1760 1864.66 1975.53 2093 2217.46 2349.32
+2489.02 2637.02 2793.83 2959.96 3135.96 3322.44 3520 3729.31 3951.07
+4186.01 4434.92 4698.64 4978.03 5274.04 5587.65 5919.91 6271.93 6644.88
+7040 7458.62 7902.13 8372.02 8869.84 9397.27 9956.06 10548.1 11175.3
+11839.8 12543.9 13289.8 14080;
+#X coords 0 12000 130 0 200 100 1;
+#X restore 77 222 graph;
+#X text 87 330 ------ 130 samples ------;
+#X text 286 315 0;
+#X text 288 215 12000;
+#N canvas 244 212 672 338 regenerate-tables 0;
+#X msg 415 84 bang;
+#X obj 415 113 t b b;
+#X obj 474 177 f;
+#X obj 512 177 + 1;
+#X msg 483 147 0;
+#X obj 415 142 until;
+#X obj 474 211 t f f;
+#X obj 414 238 mtof;
+#X obj 405 202 sel 129;
+#X obj 413 264 tabwrite mtof;
+#X obj 35 227 moses 2;
+#X msg 19 76 bang;
+#X obj 19 105 t b b;
+#X obj 90 166 f;
+#X obj 128 166 + 1;
+#X msg 112 138 0;
+#X obj 19 134 until;
+#X obj 11 194 sel 122;
+#X msg 35 258 0;
+#X obj 79 259 dbtorms;
+#X obj 90 194 t f f;
+#X obj 35 291 tabwrite dbtorms;
+#X text 18 49 bang to recalculate dbtorms table;
+#X text 356 50 bang to recalculate the mtof table;
+#X connect 0 0 1 0;
+#X connect 1 0 5 0;
+#X connect 1 1 4 0;
+#X connect 2 0 3 0;
+#X connect 2 0 6 0;
+#X connect 2 0 8 0;
+#X connect 3 0 2 1;
+#X connect 4 0 2 1;
+#X connect 5 0 2 0;
+#X connect 6 0 7 0;
+#X connect 6 1 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 5 1;
+#X connect 10 0 18 0;
+#X connect 10 1 19 0;
+#X connect 11 0 12 0;
+#X connect 12 0 16 0;
+#X connect 12 1 15 0;
+#X connect 13 0 14 0;
+#X connect 13 0 17 0;
+#X connect 13 0 20 0;
+#X connect 14 0 13 1;
+#X connect 15 0 13 1;
+#X connect 16 0 13 0;
+#X connect 17 0 16 1;
+#X connect 18 0 21 0;
+#X connect 19 0 21 0;
+#X connect 20 0 10 0;
+#X connect 20 1 21 1;
+#X restore 375 76 pd regenerate-tables;
+#X restore 334 483 pd conversion-tables;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 5 0 2 0;
+#X connect 6 0 20 0;
+#X connect 7 0 6 0;
+#X connect 8 0 6 1;
+#X connect 9 0 18 0;
+#X connect 9 0 19 0;
+#X connect 10 0 7 0;
+#X connect 12 0 25 0;
+#X connect 18 0 21 0;
+#X connect 19 0 23 0;
+#X connect 20 0 24 0;
+#X connect 21 0 8 0;
+#X connect 21 0 10 0;
+#X connect 22 0 20 1;
+#X connect 23 0 22 0;
+#X connect 24 0 11 0;
+#X connect 24 0 5 0;
+#X connect 25 0 26 0;
+#X connect 26 0 24 1;
diff --git a/pd/doc/3.audio.examples/55.delay.variable.pd b/pd/doc/3.audio.examples/55.delay.variable.pd
new file mode 100644
index 00000000..bb16de95
--- /dev/null
+++ b/pd/doc/3.audio.examples/55.delay.variable.pd
@@ -0,0 +1,129 @@
+#N canvas 100 17 671 522 12;
+#X obj 63 306 hip~ 10;
+#X floatatom 331 222;
+#X obj 331 270 line~;
+#X obj 331 246 pack 0 100;
+#X floatatom 256 192;
+#X floatatom 442 297;
+#X obj 442 369 line~;
+#X obj 442 345 pack 0 100;
+#X obj 442 321 * 0.01;
+#X floatatom 143 167;
+#X obj 143 238 line~;
+#X obj 143 214 pack 0 100;
+#X obj 63 258 *~;
+#X obj 63 282 cos~;
+#X floatatom 63 135;
+#X obj 63 159 mtof;
+#X obj 63 183 * 0.5;
+#X obj 63 330 clip~ -0.2 0.2;
+#X obj 143 190 * 0.01;
+#X obj 426 444 delwrite~ delay1 1000;
+#X obj 63 354 +~;
+#X obj 426 396 *~;
+#X obj 256 336 vd~ delay1;
+#X obj 256 288 *~;
+#X obj 256 216 / 100;
+#X floatatom 93 419;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 181 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 158 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 64 447 pd output;
+#X msg 122 419 MUTE;
+#X text 161 418 <-- output amplitude;
+#X obj 63 378 hip~ 5;
+#X text 401 505 updated for Pd version 0.26;
+#X obj 256 264 +~ 1;
+#X obj 256 240 osc~ 0;
+#X obj 256 312 +~ 1.46;
+#X obj 426 420 clip~ -5 5;
+#X text 43 35 This is a fuzzed FM generator going into a delay loop \, this time using a variable delay object (vd~). You can get several interesting effects this way. We have taken the precaution if clipping inside the loop to avoid instabilities. You can push the loop gain past 1 if you want \, it will just uscillate.;
+#X text 184 165 <-- timbre;
+#X text 96 136 <-- pitch;
+#X text 300 192 <-- cycle frequency (hundredths);
+#X text 361 222 <-- cycle depth (msec);
+#X text 491 298 <-- feedback (hundredths);
+#X text 86 9 VARIABLE DELAYS;
+#X obj 63 207 osc~ 0;
+#X connect 0 0 17 0;
+#X connect 1 0 3 0;
+#X connect 2 0 23 1;
+#X connect 3 0 2 0;
+#X connect 4 0 24 0;
+#X connect 5 0 8 0;
+#X connect 6 0 21 1;
+#X connect 7 0 6 0;
+#X connect 8 0 7 0;
+#X connect 9 0 18 0;
+#X connect 10 0 12 1;
+#X connect 11 0 10 0;
+#X connect 12 0 13 0;
+#X connect 13 0 0 0;
+#X connect 14 0 15 0;
+#X connect 15 0 16 0;
+#X connect 16 0 42 0;
+#X connect 17 0 20 0;
+#X connect 18 0 11 0;
+#X connect 20 0 29 0;
+#X connect 21 0 34 0;
+#X connect 22 0 20 1;
+#X connect 23 0 33 0;
+#X connect 24 0 32 0;
+#X connect 25 0 26 1;
+#X connect 26 0 25 0;
+#X connect 27 0 26 2;
+#X connect 29 0 26 0;
+#X connect 29 0 21 0;
+#X connect 31 0 23 0;
+#X connect 32 0 31 0;
+#X connect 33 0 22 0;
+#X connect 34 0 19 0;
+#X connect 42 0 12 0;
diff --git a/pd/doc/3.audio.examples/56.delay.pitchshift.pd b/pd/doc/3.audio.examples/56.delay.pitchshift.pd
new file mode 100644
index 00000000..feb56e2f
--- /dev/null
+++ b/pd/doc/3.audio.examples/56.delay.pitchshift.pd
@@ -0,0 +1,226 @@
+#N canvas 93 36 1005 580 12;
+#X obj 19 493 hip~ 5;
+#X floatatom 19 87 0 0 0;
+#X obj 84 359 *~;
+#X obj 192 290 line~;
+#X floatatom 265 114 0 0 0;
+#X text 68 9 PITCH SHIFTER;
+#X obj 192 264 pack 0 200;
+#X obj 266 141 moses 1;
+#X msg 227 141 1;
+#X obj 266 88 r window;
+#X obj 19 61 r transpose;
+#X obj 19 143 exp;
+#X floatatom 19 169 0 0 0;
+#X obj 19 259 /;
+#X obj 146 189 * 0.001;
+#X obj 314 366 line~;
+#X obj 315 338 pack 0 200;
+#X floatatom 315 258 0 0 0;
+#X text 314 202 delay (msec);
+#X obj 315 232 r delay;
+#X obj 84 385 +~;
+#X msg 315 311 1;
+#X obj 315 285 moses 1.5;
+#X obj 84 411 vd~ delay1;
+#X obj 19 410 cos~;
+#X obj 19 437 *~;
+#X obj 19 466 +~;
+#X obj 106 317 wrap~;
+#X obj 251 360 *~;
+#X obj 251 393 +~;
+#X obj 251 422 vd~ delay1;
+#X obj 188 420 cos~;
+#X obj 188 447 *~;
+#X msg 492 56 \; transpose 0 \; window 100 \; delay 0;
+#X obj 492 30 loadbang;
+#X obj 264 42 delwrite~ delay1 5000;
+#X obj 146 216 t b f;
+#X floatatom 19 285 0 0 0;
+#X obj 19 312 phasor~ 0;
+#X floatatom 51 526 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 372 176 t b;
+#X obj 372 121 f;
+#X obj 372 66 inlet;
+#X text 378 32 mute;
+#X obj 372 204 f;
+#X msg 468 196 0;
+#X msg 372 94 bang;
+#X obj 372 149 moses 1;
+#X obj 468 168 t b f;
+#X obj 437 129 moses 1;
+#X obj 91 163 dbtorms;
+#X obj 437 101 r master-lvl;
+#X obj 91 46 r master-lvl;
+#X obj 372 231 s master-lvl;
+#X obj 24 199 inlet~;
+#X obj 219 45 inlet;
+#X text 219 20 level;
+#X obj 219 110 s master-lvl;
+#X msg 106 72 set \$1;
+#X obj 106 98 outlet;
+#X msg 235 70 \; pd dsp 1;
+#X obj 91 213 line~;
+#X obj 24 233 *~;
+#X obj 24 265 dac~;
+#X obj 91 188 pack 0 50;
+#X text 22 174 audio;
+#X text 102 121 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 19 557 pd output;
+#X msg 83 526 MUTE;
+#X text 125 525 <-- output amplitude;
+#X obj 106 290 +~ 0.5;
+#X obj 19 358 -~ 0.5;
+#X obj 19 384 *~ 0.5;
+#X obj 188 359 -~ 0.5;
+#X obj 188 392 *~ 0.5;
+#X floatatom 227 167 0 0 0;
+#X obj 19 196 - 1;
+#X obj 19 117 * 0.05776;
+#X obj 19 222 * -1;
+#X text 53 86 <-- transposition;
+#X text 96 99 (halftones);
+#X text 86 177 speed;
+#X text 85 191 change;
+#X text 310 113 <--window (msec);
+#X text 54 252 tape head;
+#X text 55 265 rotation speed;
+#N canvas 0 0 612 637 test-input 0;
+#X graph graph1 0 -1 155947 1 150 291 350 141;
+#X array array1 155948 float 0;
+#X pop;
+#X obj 139 518 tabread4~ array1;
+#X obj 139 333 r totsamps;
+#X obj 139 413 /;
+#X obj 139 465 *~ 0;
+#X obj 139 439 phasor~ 0;
+#X obj 139 492 +~ 1;
+#X msg 139 386 44100;
+#X obj 139 360 t b f;
+#X obj 182 469 r totsamps;
+#X text 153 538 sample loop for;
+#X text 153 555 test signal;
+#X obj 162 30 loadbang;
+#X obj 139 590 outlet~;
+#X obj 393 169 r readfile;
+#X obj 393 199 symbol;
+#X msg 392 228 read -resize \$1 array1;
+#X obj 392 256 soundfiler;
+#X obj 392 284 s totsamps;
+#X msg 161 64 \; readfile ../sound/bell.aiff;
+#X connect 1 0 13 0;
+#X connect 2 0 8 0;
+#X connect 3 0 5 0;
+#X connect 4 0 6 0;
+#X connect 5 0 4 0;
+#X connect 6 0 1 0;
+#X connect 7 0 3 0;
+#X connect 8 0 7 0;
+#X connect 8 1 3 1;
+#X connect 9 0 4 1;
+#X connect 12 0 19 0;
+#X connect 14 0 15 0;
+#X connect 15 0 16 0;
+#X connect 16 0 17 0;
+#X connect 17 0 18 0;
+#X restore 264 11 pd test-input;
+#X text 439 161 This is a classic rotating-tape-head style pitch shifter
+using the vd~ variable delay object. Ther are two moving tape heads
+\, each of which is loudest at the middle of its trajectory \, and
+enveloped out at the moment it has to jump back (or forward) to start
+another scratch. Most of the brain work is in computing how fast the
+tape heads have to move to get the desired transposition.;
+#X text 439 280 The "window size" is the total trajectory of the read
+points in the delay line \, in milliseconds. The delay times are controlled
+by a phasor~ object. The second delay time \, 180 degrees out of phase
+from the first one \, is computed using the "wrap" object.;
+#X text 437 370 The "window size" is the total trajectory of the read
+points in the delay line \, in milliseconds. The delay times are controlled
+by a phasor~ object. The second delay time \, 180 degrees out of phase
+from the first one \, is computed using the "wrap" object.;
+#X text 436 462 The cos~ objects compute the fadein and fadeout of
+the two delay line outputs. They each traverse the positive half of
+the cosine waveform (phase -0.25 to +0.25) over the time the phase
+goes from one end to the other.;
+#X text 757 557 updated for Pd version 0.33;
+#X connect 0 0 40 0;
+#X connect 1 0 50 0;
+#X connect 2 0 20 0;
+#X connect 3 0 2 1;
+#X connect 3 0 28 1;
+#X connect 4 0 7 0;
+#X connect 6 0 3 0;
+#X connect 7 0 8 0;
+#X connect 7 1 48 0;
+#X connect 8 0 48 0;
+#X connect 9 0 4 0;
+#X connect 10 0 1 0;
+#X connect 11 0 12 0;
+#X connect 12 0 49 0;
+#X connect 13 0 37 0;
+#X connect 14 0 36 0;
+#X connect 15 0 20 1;
+#X connect 15 0 29 1;
+#X connect 16 0 15 0;
+#X connect 17 0 22 0;
+#X connect 19 0 17 0;
+#X connect 20 0 23 0;
+#X connect 21 0 16 0;
+#X connect 22 0 21 0;
+#X connect 22 1 16 0;
+#X connect 23 0 25 1;
+#X connect 24 0 25 0;
+#X connect 25 0 26 0;
+#X connect 26 0 0 0;
+#X connect 27 0 28 0;
+#X connect 27 0 46 0;
+#X connect 28 0 29 0;
+#X connect 29 0 30 0;
+#X connect 30 0 32 1;
+#X connect 31 0 32 0;
+#X connect 32 0 26 1;
+#X connect 34 0 33 0;
+#X connect 36 0 13 0;
+#X connect 36 1 13 1;
+#X connect 37 0 38 0;
+#X connect 38 0 2 0;
+#X connect 38 0 44 0;
+#X connect 38 0 43 0;
+#X connect 39 0 40 1;
+#X connect 40 0 39 0;
+#X connect 41 0 40 2;
+#X connect 43 0 27 0;
+#X connect 44 0 45 0;
+#X connect 45 0 24 0;
+#X connect 46 0 47 0;
+#X connect 47 0 31 0;
+#X connect 48 0 6 0;
+#X connect 48 0 14 0;
+#X connect 49 0 51 0;
+#X connect 50 0 11 0;
+#X connect 51 0 13 0;
+#X connect 59 0 35 0;
diff --git a/pd/doc/3.audio.examples/57.delay.reverb.pd b/pd/doc/3.audio.examples/57.delay.reverb.pd
new file mode 100644
index 00000000..aad17023
--- /dev/null
+++ b/pd/doc/3.audio.examples/57.delay.reverb.pd
@@ -0,0 +1,316 @@
+#N canvas 127 171 643 406 12;
+#N canvas 0 0 499 321 test-input 0;
+#X obj 75 253 outlet~;
+#X obj 74 201 -~;
+#X obj 74 177 *~ 3;
+#X obj 111 183 *~ 2;
+#X floatatom 74 81 0 0 0;
+#X obj 74 153 clip~ 0 0.667;
+#X text 124 80 <-- pitch;
+#X obj 74 105 mtof;
+#X msg 195 142 1;
+#X obj 74 225 *~;
+#X obj 74 129 phasor~ 0;
+#X obj 195 190 tabread4~ dbtorms;
+#X obj 195 166 adsr 100 100 2000 0 2000;
+#X obj 73 54 inlet;
+#N canvas 0 0 600 392 conversion-tables 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array dbtorms 123 float 1;
+#A 0 0 0 1.25893e-05 1.41254e-05 1.58489e-05 1.77828e-05 1.99526e-05
+2.23872e-05 2.51189e-05 2.81838e-05 3.16228e-05 3.54813e-05 3.98107e-05
+4.46684e-05 5.01187e-05 5.62341e-05 6.30957e-05 7.07946e-05 7.94328e-05
+8.91251e-05 1e-04 0.000112202 0.000125893 0.000141254 0.000158489 0.000177828
+0.000199526 0.000223872 0.000251189 0.000281838 0.000316228 0.000354813
+0.000398107 0.000446684 0.000501187 0.000562341 0.000630957 0.000707946
+0.000794328 0.000891251 0.001 0.00112202 0.00125893 0.00141254 0.00158489
+0.00177828 0.00199526 0.00223872 0.00251189 0.00281838 0.00316228 0.00354813
+0.00398107 0.00446684 0.00501187 0.00562341 0.00630957 0.00707946 0.00794328
+0.00891251 0.01 0.0112202 0.0125893 0.0141254 0.0158489 0.0177828 0.0199526
+0.0223872 0.0251189 0.0281838 0.0316228 0.0354813 0.0398107 0.0446684
+0.0501187 0.0562341 0.0630957 0.0707946 0.0794328 0.0891251 0.1 0.112202
+0.125893 0.141254 0.158489 0.177828 0.199526 0.223872 0.251189 0.281838
+0.316228 0.354813 0.398107 0.446684 0.501187 0.562341 0.630957 0.707946
+0.794328 0.891251 1 1.12202 1.25893 1.41254 1.58489 1.77828 1.99526
+2.23872 2.51189 2.81838 3.16228 3.54813 3.98107 4.46684 5.01187 5.62341
+6.30957 7.07946 7.94328 8.91251 10 11.2202 12.5893;
+#X coords 0 10 123 0 200 100 1;
+#X restore 70 45 graph;
+#X text 272 138 0;
+#X text 274 38 10;
+#X text 89 148 ------ 123 samples ------;
+#N canvas 0 0 450 300 graph2 0;
+#X array mtof 130 float 1;
+#A 0 8.1758 8.66196 9.17702 9.72272 10.3009 10.9134 11.5623 12.2499
+12.9783 13.75 14.5676 15.4339 16.3516 17.3239 18.354 19.4454 20.6017
+21.8268 23.1247 24.4997 25.9565 27.5 29.1352 30.8677 32.7032 34.6478
+36.7081 38.8909 41.2034 43.6535 46.2493 48.9994 51.9131 55 58.2705
+61.7354 65.4064 69.2957 73.4162 77.7817 82.4069 87.3071 92.4986 97.9989
+103.826 110 116.541 123.471 130.813 138.591 146.832 155.563 164.814
+174.614 184.997 195.998 207.652 220 233.082 246.942 261.626 277.183
+293.665 311.127 329.628 349.228 369.994 391.995 415.305 440 466.164
+493.883 523.251 554.365 587.33 622.254 659.255 698.456 739.989 783.991
+830.609 880 932.328 987.767 1046.5 1108.73 1174.66 1244.51 1318.51
+1396.91 1479.98 1567.98 1661.22 1760 1864.66 1975.53 2093 2217.46 2349.32
+2489.02 2637.02 2793.83 2959.96 3135.96 3322.44 3520 3729.31 3951.07
+4186.01 4434.92 4698.64 4978.03 5274.04 5587.65 5919.91 6271.93 6644.88
+7040 7458.62 7902.13 8372.02 8869.84 9397.27 9956.06 10548.1 11175.3
+11839.8 12543.9 13289.8 14080;
+#X coords 0 12000 130 0 200 100 1;
+#X restore 77 222 graph;
+#X text 87 330 ------ 130 samples ------;
+#X text 286 315 0;
+#X text 288 215 12000;
+#N canvas 244 212 672 338 regenerate-tables 0;
+#X msg 415 84 bang;
+#X obj 415 113 t b b;
+#X obj 474 177 f;
+#X obj 512 177 + 1;
+#X msg 483 147 0;
+#X obj 415 142 until;
+#X obj 474 211 t f f;
+#X obj 414 238 mtof;
+#X obj 405 202 sel 129;
+#X obj 413 264 tabwrite mtof;
+#X obj 35 227 moses 2;
+#X msg 19 76 bang;
+#X obj 19 105 t b b;
+#X obj 90 166 f;
+#X obj 128 166 + 1;
+#X msg 112 138 0;
+#X obj 19 134 until;
+#X obj 11 194 sel 122;
+#X msg 35 258 0;
+#X obj 79 259 dbtorms;
+#X obj 90 194 t f f;
+#X obj 35 291 tabwrite dbtorms;
+#X text 18 49 bang to recalculate dbtorms table;
+#X text 356 50 bang to recalculate the mtof table;
+#X connect 0 0 1 0;
+#X connect 1 0 5 0;
+#X connect 1 1 4 0;
+#X connect 2 0 3 0;
+#X connect 2 0 6 0;
+#X connect 2 0 8 0;
+#X connect 3 0 2 1;
+#X connect 4 0 2 1;
+#X connect 5 0 2 0;
+#X connect 6 0 7 0;
+#X connect 6 1 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 5 1;
+#X connect 10 0 18 0;
+#X connect 10 1 19 0;
+#X connect 11 0 12 0;
+#X connect 12 0 16 0;
+#X connect 12 1 15 0;
+#X connect 13 0 14 0;
+#X connect 13 0 17 0;
+#X connect 13 0 20 0;
+#X connect 14 0 13 1;
+#X connect 15 0 13 1;
+#X connect 16 0 13 0;
+#X connect 17 0 16 1;
+#X connect 18 0 21 0;
+#X connect 19 0 21 0;
+#X connect 20 0 10 0;
+#X connect 20 1 21 1;
+#X restore 375 76 pd regenerate-tables;
+#X restore 260 101 pd conversion-tables;
+#X connect 1 0 9 0;
+#X connect 2 0 1 0;
+#X connect 3 0 1 1;
+#X connect 4 0 7 0;
+#X connect 4 0 8 0;
+#X connect 5 0 2 0;
+#X connect 7 0 10 0;
+#X connect 8 0 12 0;
+#X connect 9 0 0 0;
+#X connect 10 0 3 0;
+#X connect 10 0 5 0;
+#X connect 11 0 9 1;
+#X connect 12 0 11 0;
+#X connect 13 0 4 0;
+#X restore 75 129 pd test-input;
+#X text 328 322 updated for Pd version 0.26;
+#X text 62 8 REVERBERATOR;
+#X floatatom 75 100 0 0 0;
+#X text 126 105 <-- pitch;
+#X floatatom 137 219 0 0 0;
+#N canvas 159 26 618 379 output 0;
+#X obj 393 156 t b;
+#X obj 393 106 f;
+#X obj 393 56 inlet;
+#X text 399 25 mute;
+#X obj 393 181 f;
+#X msg 480 174 0;
+#X msg 393 81 bang;
+#X obj 393 131 moses 1;
+#X obj 480 149 t b f;
+#X obj 452 113 moses 1;
+#X obj 138 144 dbtorms;
+#X obj 452 88 r master-lvl;
+#X obj 138 38 r master-lvl;
+#X obj 393 206 s master-lvl;
+#X obj 22 140 inlet~;
+#X obj 254 37 inlet;
+#X text 254 14 level;
+#X obj 254 96 s master-lvl;
+#X msg 151 61 set \$1;
+#X obj 151 85 outlet;
+#X msg 269 60 \; pd dsp 1;
+#X obj 138 190 line~;
+#X obj 22 231 *~;
+#X obj 138 167 pack 0 50;
+#X text 34 118 audio;
+#X text 148 106 show level;
+#X obj 73 140 inlet~;
+#X obj 73 232 *~;
+#X obj 22 260 dac~ 1;
+#X obj 73 260 dac~ 2;
+#X obj 22 182 hip~ 5;
+#X obj 73 181 hip~ 5;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 23 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 30 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 21 0 27 1;
+#X connect 22 0 28 0;
+#X connect 23 0 21 0;
+#X connect 26 0 31 0;
+#X connect 27 0 29 0;
+#X connect 30 0 22 0;
+#X connect 31 0 27 0;
+#X restore 75 247 pd output;
+#X msg 166 219 MUTE;
+#X text 184 217 <-- output amplitude;
+#N canvas 137 269 1056 577 reverb 0;
+#X obj 77 37 inlet~;
+#X obj 77 85 echo echo-del2 8.45346;
+#X obj 77 61 echo echo-del1 5.43216;
+#X obj 77 109 echo echo-del3 13.4367;
+#X obj 77 133 echo echo-del4 21.5463;
+#X obj 77 157 echo echo-del5 34.3876;
+#X obj 77 181 echo echo-del6 55.5437;
+#X obj 253 545 delwrite~ loop-del1 70;
+#X obj 295 157 delread~ loop-del1 70;
+#X obj 466 157 delread~ loop-del2 81.9345;
+#X obj 416 545 delwrite~ loop-del2 81.9345;
+#X obj 665 154 delread~ loop-del3 94.7545;
+#X obj 855 154 delread~ loop-del4 115.945;
+#X obj 622 545 delwrite~ loop-del3 94.7545;
+#X obj 820 545 delwrite~ loop-del4 115.945;
+#X obj 282 215 +~;
+#X obj 443 215 +~;
+#X obj 187 251 outlet~;
+#X obj 364 244 outlet~;
+#X obj 251 337 +~;
+#X obj 619 328 +~;
+#X obj 408 334 -~;
+#X obj 791 327 -~;
+#X obj 251 391 +~;
+#X obj 411 397 +~;
+#X obj 615 407 -~;
+#X obj 785 388 -~;
+#X obj 620 474 *~ 0;
+#X obj 620 511 lop~ 5000;
+#X obj 415 472 *~ 0;
+#X obj 415 510 lop~ 5000;
+#X obj 246 475 *~ 0;
+#X obj 246 513 lop~ 5000;
+#X obj 821 472 *~ 0;
+#X obj 821 506 lop~ 5000;
+#X obj 924 254 inlet;
+#X obj 924 278 moses 100;
+#X obj 926 325 moses -100;
+#X msg 979 303 100;
+#X msg 930 349 -100;
+#X obj 934 386 / 200;
+#X connect 0 0 2 0;
+#X connect 1 0 3 0;
+#X connect 1 1 3 1;
+#X connect 2 0 1 0;
+#X connect 2 1 1 1;
+#X connect 3 0 4 0;
+#X connect 3 1 4 1;
+#X connect 4 0 5 0;
+#X connect 4 1 5 1;
+#X connect 5 0 6 0;
+#X connect 5 1 6 1;
+#X connect 6 0 15 0;
+#X connect 6 1 16 0;
+#X connect 8 0 15 1;
+#X connect 9 0 16 1;
+#X connect 11 0 19 1;
+#X connect 11 0 21 1;
+#X connect 12 0 20 1;
+#X connect 12 0 22 1;
+#X connect 15 0 17 0;
+#X connect 15 0 19 0;
+#X connect 15 0 21 0;
+#X connect 16 0 18 0;
+#X connect 16 0 20 0;
+#X connect 16 0 22 0;
+#X connect 19 0 23 0;
+#X connect 19 0 25 0;
+#X connect 20 0 25 1;
+#X connect 20 0 23 1;
+#X connect 21 0 24 0;
+#X connect 21 0 26 0;
+#X connect 22 0 24 1;
+#X connect 22 0 26 1;
+#X connect 23 0 27 0;
+#X connect 24 0 29 0;
+#X connect 25 0 31 0;
+#X connect 26 0 33 0;
+#X connect 27 0 28 0;
+#X connect 28 0 13 0;
+#X connect 29 0 30 0;
+#X connect 30 0 10 0;
+#X connect 31 0 32 0;
+#X connect 32 0 7 0;
+#X connect 33 0 34 0;
+#X connect 34 0 14 0;
+#X connect 35 0 36 0;
+#X connect 36 0 37 0;
+#X connect 36 1 38 0;
+#X connect 37 0 39 0;
+#X connect 37 1 40 0;
+#X connect 38 0 37 0;
+#X connect 39 0 40 0;
+#X connect 40 0 33 1;
+#X connect 40 0 31 1;
+#X connect 40 0 29 1;
+#X connect 40 0 27 1;
+#X restore 50 193 pd reverb;
+#X floatatom 108 163 0 0 0;
+#X text 143 163 <-- feedback (100 maximum);
+#X text 32 41 Here is a simple recirculating reverberator. "Feedback"
+should be between -100 and 100;
+#X text 37 285 You can spend a lifetime tweaking reverberators... we'll
+just leave it at that for now.;
+#X connect 0 0 9 0;
+#X connect 0 0 6 0;
+#X connect 3 0 0 0;
+#X connect 5 0 6 2;
+#X connect 6 0 5 0;
+#X connect 7 0 6 3;
+#X connect 9 0 6 0;
+#X connect 9 1 6 1;
+#X connect 10 0 9 1;
diff --git a/pd/doc/3.audio.examples/58.PART8.moreFM.pd b/pd/doc/3.audio.examples/58.PART8.moreFM.pd
new file mode 100644
index 00000000..1b0e112b
--- /dev/null
+++ b/pd/doc/3.audio.examples/58.PART8.moreFM.pd
@@ -0,0 +1,132 @@
+#N canvas 25 100 785 694 12;
+#X floatatom 197 602 0 0 0;
+#N canvas 159 26 541 274 output 0;
+#X obj 351 166 t b;
+#X obj 351 114 f;
+#X obj 351 62 inlet;
+#X text 358 30 mute;
+#X obj 351 192 f;
+#X msg 442 186 0;
+#X msg 351 88 bang;
+#X obj 351 140 moses 1;
+#X obj 413 114 moses 1;
+#X obj 86 153 dbtorms;
+#X obj 413 88 r master-lvl;
+#X obj 86 44 r master-lvl;
+#X obj 351 218 s master-lvl;
+#X obj 12 166 inlet~;
+#X obj 207 43 inlet;
+#X text 207 19 level;
+#X obj 207 104 s master-lvl;
+#X msg 100 67 set \$1;
+#X obj 100 93 outlet;
+#X msg 222 67 \; pd dsp 1;
+#X obj 86 205 line~;
+#X obj 12 218 *~;
+#X obj 12 244 dac~;
+#X obj 86 179 pack 0 50;
+#X text 12 142 audio;
+#X text 100 118 show level;
+#X obj 12 192 hip~ 1;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 26 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 21 0;
+#X restore 158 630 pd output;
+#X msg 234 602 MUTE;
+#X text 528 618 updated for Pd version 0.34;
+#X text 277 601 <-- output;
+#X text 79 4 FM \, PWM \, PAF as formant generators;
+#X text 39 22 The next several patches illustrate "Synthesizing Sounds
+with Specified \, Time-Varying Spectra" presented at ICMC 2001 and
+reprinted on http://www.crca.ucsd.edu/~msp/publications.html.;
+#X obj 146 468 line~;
+#X obj 146 444 pack 0 50;
+#X floatatom 146 389 0 0 0;
+#X obj 124 493 *~;
+#X text 145 367 index;
+#X floatatom 248 398 0 0 0;
+#X text 250 370 carrier freq;
+#X obj 158 547 cos~;
+#X graph graph1 0 0 128 500 499 556 755 426;
+#X array spectrum 128 float 0;
+#X pop;
+#X msg 233 546 bang;
+#X text 271 543 <-- click to graph;
+#N canvas 204 17 358 238 fft 0;
+#X obj 46 48 inlet~;
+#X obj 159 181 tabwrite~ spectrum;
+#X obj 159 145 inlet;
+#X obj 46 78 rfft~;
+#X obj 46 111 *~;
+#X obj 77 111 *~;
+#X obj 46 141 sqrt~;
+#X obj 191 45 block~ 1024 1;
+#X connect 0 0 3 0;
+#X connect 2 0 1 0;
+#X connect 3 0 4 0;
+#X connect 3 0 4 1;
+#X connect 3 1 5 0;
+#X connect 3 1 5 1;
+#X connect 4 0 6 0;
+#X connect 5 0 6 0;
+#X connect 6 0 1 0;
+#X restore 187 574 pd fft;
+#X text 490 562 0;
+#X text 738 559 5512;
+#X obj 153 520 +~;
+#X obj 248 448 phasor~;
+#X obj 23 458 osc~ 172.266;
+#X obj 146 417 / 100;
+#X obj 248 422 * 172.266;
+#X text 34 93 First compare this phase modulation example with the
+ring modulation example from the section on processing (patch 51).
+Here we choose a convenient \, fixed modulation frequency and consider
+the effect of changing carrier frequency and modulation index. It's
+exactly as if the carrier frequency were a ring modulation frequency.
+;
+#X text 33 331 Next we'll look at two techniques for sliding a formant
+frequency without losing harmonicity.;
+#X text 33 191 Using either method we can synthesize the hat-shaped
+spectra called "formants." However \, if you try to move the formant
+up or down in frequency \, you'll lose harmonicity \; the partials
+are only integer multiples of the fundamental \, 172.266 \, when the
+carrier is an integer multiple. To hear this \, set index to 20 and
+carrier frequency to zero \, and scroll carrier through integers. Then
+shift-drag on the carrier frequency to change it in hundredths. Presto
+\, inharmonic sounds...;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 7 0 10 1;
+#X connect 8 0 7 0;
+#X connect 9 0 24 0;
+#X connect 10 0 21 0;
+#X connect 12 0 25 0;
+#X connect 14 0 1 0;
+#X connect 14 0 18 0;
+#X connect 16 0 18 1;
+#X connect 21 0 14 0;
+#X connect 22 0 21 1;
+#X connect 23 0 10 0;
+#X connect 24 0 8 0;
+#X connect 25 0 22 0;
diff --git a/pd/doc/3.audio.examples/59.packets.pd b/pd/doc/3.audio.examples/59.packets.pd
new file mode 100644
index 00000000..072ae27d
--- /dev/null
+++ b/pd/doc/3.audio.examples/59.packets.pd
@@ -0,0 +1,161 @@
+#N canvas 83 221 878 705 12;
+#X obj 315 424 line~;
+#X obj 47 517 cos~;
+#X graph graph1 0 -2 882 2 644 469 844 339;
+#X array pulse-output 882 float 0;
+#X pop;
+#X floatatom 84 633 0 0 0;
+#N canvas 176 241 532 273 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 398 111 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 398 86 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 17 148 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 15 125 audio;
+#X text 93 110 show level;
+#X obj 17 177 hip~ 1;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 26 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 21 0;
+#X restore 48 660 pd output;
+#X msg 124 633 MUTE;
+#X obj 315 400 pack 0 50;
+#X floatatom 315 328 0 0 0;
+#X obj 47 429 -~ 0.5;
+#X obj 47 468 *~;
+#X obj 315 352 / 10;
+#X obj 47 493 clip~ -0.5 0.5;
+#X text 315 306 bandwidth;
+#X msg 165 602 bang;
+#X obj 315 448 +~ 1;
+#X obj 48 540 +~ 1;
+#X obj 197 513 cos~;
+#X obj 48 569 *~;
+#X floatatom 219 342 4 0 0;
+#X obj 219 366 / 10;
+#X text 644 472 --- 0.02 seconds ---;
+#X obj 197 487 *~;
+#N canvas 204 427 939 588 graph 0;
+#X obj 91 345 inlet~;
+#X obj 729 341 inlet;
+#X obj 74 395 tabwrite~ pulse-output;
+#X obj 319 350 inlet~;
+#X obj 493 343 inlet~;
+#X obj 302 400 tabwrite~ window;
+#X obj 484 401 tabwrite~ carrier;
+#X msg 390 53 \; array2 rename window;
+#X connect 0 0 2 0;
+#X connect 1 0 2 0;
+#X connect 1 0 5 0;
+#X connect 1 0 6 0;
+#X connect 3 0 5 0;
+#X connect 4 0 6 0;
+#X restore 76 602 pd graph;
+#X obj 315 376 max 0;
+#X obj 219 438 line~;
+#X obj 219 414 pack 0 50;
+#X obj 219 390 max 0;
+#X graph graph3 0 -2 881 2 644 328 844 188;
+#X array carrier 882 float 0;
+#X pop;
+#X graph graph4 0 -2 881 2 645 175 845 35;
+#X array window 882 float 0;
+#X pop;
+#X text 211 601 <-- graph;
+#X floatatom 47 381 4 0 0;
+#X obj 47 405 phasor~ 100;
+#X text 31 2 WINDOWED PACKETS;
+#X text 169 632 <-- output;
+#X text 43 356 freq.;
+#X text 43 337 fundamental;
+#X text 28 51 The simpler technique is to synthesize enveloped sinusoidal
+wave packets. The packets should repeat at the fundamental frequency
+\, but the frequency of the packet itself controls the center frequency
+of the formant. The length of the packet varies inversely with bandwidth.
+;
+#X text 27 132 In the patch below \, the "clip~" followed by "cos~"
+and "+~ 1" is the enveloping ("windowing" function \, which appears
+in the top graph. This is just the original PWM patch from part 2 The
+carrier \, on the other hand \, is a broken sinusoid made by amplifying
+the phasor~ (the "*~" controlled by "center freq.") and taking the
+cos~ of the result. The "breaks" in the sinusoid only occur when the
+enveloping signal is zero.;
+#X text 197 256 center;
+#X text 195 275 freq. (in;
+#X text 194 294 tenths of;
+#X text 193 314 fundamental);
+#X text 92 535 window;
+#X text 233 487 magnified phase;
+#X text 278 531 desired center frequency;
+#X text 250 514 <--this cosine goes at the;
+#X text 279 550 but its phase is reset each;
+#X text 277 569 fundamental period.;
+#X text 612 666 updated for Pd version 0.34;
+#X connect 0 0 14 0;
+#X connect 1 0 15 0;
+#X connect 3 0 4 1;
+#X connect 4 0 3 0;
+#X connect 5 0 4 2;
+#X connect 6 0 0 0;
+#X connect 7 0 10 0;
+#X connect 8 0 9 0;
+#X connect 8 0 21 0;
+#X connect 9 0 11 0;
+#X connect 10 0 23 0;
+#X connect 11 0 1 0;
+#X connect 13 0 22 3;
+#X connect 14 0 9 1;
+#X connect 15 0 17 0;
+#X connect 15 0 22 1;
+#X connect 16 0 17 1;
+#X connect 16 0 22 2;
+#X connect 17 0 22 0;
+#X connect 17 0 4 0;
+#X connect 18 0 19 0;
+#X connect 19 0 26 0;
+#X connect 21 0 16 0;
+#X connect 23 0 6 0;
+#X connect 24 0 21 1;
+#X connect 25 0 24 0;
+#X connect 26 0 25 0;
+#X connect 30 0 31 0;
+#X connect 31 0 8 0;
diff --git a/pd/doc/3.audio.examples/60.packet.spectrum.pd b/pd/doc/3.audio.examples/60.packet.spectrum.pd
new file mode 100644
index 00000000..bef1483b
--- /dev/null
+++ b/pd/doc/3.audio.examples/60.packet.spectrum.pd
@@ -0,0 +1,147 @@
+#N canvas 83 221 774 628 12;
+#X obj 302 351 line~;
+#X obj 34 444 cos~;
+#X floatatom 71 560 0 0 0;
+#N canvas 176 241 532 273 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 398 111 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 398 86 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 17 148 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 15 125 audio;
+#X text 93 110 show level;
+#X obj 17 177 hip~ 1;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 26 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 21 0;
+#X restore 35 587 pd output;
+#X msg 111 560 MUTE;
+#X obj 302 327 pack 0 50;
+#X floatatom 302 255 0 0 0;
+#X obj 34 356 -~ 0.5;
+#X obj 34 395 *~;
+#X obj 302 279 / 10;
+#X obj 34 420 clip~ -0.5 0.5;
+#X text 302 233 bandwidth;
+#X obj 302 375 +~ 1;
+#X obj 35 467 +~ 1;
+#X obj 184 440 cos~;
+#X obj 35 496 *~;
+#X floatatom 206 269 4 0 0;
+#X obj 206 293 / 10;
+#X obj 184 414 *~;
+#X text 204 224 center;
+#X text 204 243 freq.;
+#X obj 302 303 max 0;
+#X obj 206 365 line~;
+#X obj 206 341 pack 0 50;
+#X obj 206 317 max 0;
+#X floatatom 34 308 4 0 0;
+#X obj 34 332 phasor~ 100;
+#X text 156 559 <-- output;
+#X text 30 283 freq.;
+#X text 30 264 fundamental;
+#X graph graph1 0 0 128 500 440 492 696 362;
+#X array spectrum 128 float 0;
+#X pop;
+#X msg 108 498 bang;
+#N canvas 204 17 358 238 fft 0;
+#X obj 46 48 inlet~;
+#X obj 159 181 tabwrite~ spectrum;
+#X obj 159 145 inlet;
+#X obj 46 78 rfft~;
+#X obj 46 111 *~;
+#X obj 77 111 *~;
+#X obj 46 141 sqrt~;
+#X obj 191 45 block~ 1024 1;
+#X connect 0 0 3 0;
+#X connect 2 0 1 0;
+#X connect 3 0 4 0;
+#X connect 3 0 4 1;
+#X connect 3 1 5 0;
+#X connect 3 1 5 1;
+#X connect 4 0 6 0;
+#X connect 5 0 6 0;
+#X connect 6 0 1 0;
+#X restore 59 524 pd fft;
+#X text 439 502 0;
+#X text 687 499 5512;
+#X text 149 498 <-- graph;
+#X text 31 2 WINDOWED PACKET SPECTRUM;
+#X text 19 34 Here's the spectrum you get. Note that even if you put
+the center frequency right on a partial \, there is significant energy
+in neighboring partials (try fundamental 440 \, "center freq" 30 \,
+bandwidth 0.);
+#X text 18 104 The center frequency is in units of ten per partial
+\, or in other words a value of "30" means "centered on the third partial".
+;
+#X text 505 596 updated for Pd version 0.34;
+#X text 22 155 This technique only works if you're doing Hanning-window
+shaped PWM--you can't combine this naturally with FM or with the waveshaping
+technique we'll see later.;
+#X connect 0 0 12 0;
+#X connect 1 0 13 0;
+#X connect 2 0 3 1;
+#X connect 3 0 2 0;
+#X connect 4 0 3 2;
+#X connect 5 0 0 0;
+#X connect 6 0 9 0;
+#X connect 7 0 8 0;
+#X connect 7 0 18 0;
+#X connect 8 0 10 0;
+#X connect 9 0 21 0;
+#X connect 10 0 1 0;
+#X connect 12 0 8 1;
+#X connect 13 0 15 0;
+#X connect 14 0 15 1;
+#X connect 15 0 3 0;
+#X connect 15 0 32 0;
+#X connect 16 0 17 0;
+#X connect 17 0 24 0;
+#X connect 18 0 14 0;
+#X connect 21 0 5 0;
+#X connect 22 0 18 1;
+#X connect 23 0 22 0;
+#X connect 24 0 23 0;
+#X connect 25 0 26 0;
+#X connect 26 0 7 0;
+#X connect 31 0 32 1;
diff --git a/pd/doc/3.audio.examples/61.two.cosines.pd b/pd/doc/3.audio.examples/61.two.cosines.pd
new file mode 100644
index 00000000..0f813164
--- /dev/null
+++ b/pd/doc/3.audio.examples/61.two.cosines.pd
@@ -0,0 +1,124 @@
+#N canvas 10 49 705 610 12;
+#X floatatom 262 549 0 0 0;
+#N canvas 176 241 532 273 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 398 111 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 398 86 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 17 148 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 15 125 audio;
+#X text 93 110 show level;
+#X obj 17 177 hip~ 1;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 26 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 21 0;
+#X restore 226 576 pd output;
+#X msg 302 549 MUTE;
+#X msg 59 550 bang;
+#X obj 185 427 cos~;
+#X floatatom 232 217 4 0 0;
+#X obj 232 241 / 10;
+#X text 464 288 --- 0.02 seconds ---;
+#X obj 185 397 *~;
+#X text 236 133 center;
+#X obj 232 313 line~;
+#X obj 232 265 max 0;
+#X graph graph3 0 -2 881 2 450 276 650 136;
+#X array carrier 882 float 0;
+#X pop;
+#X floatatom 33 284 4 0 0;
+#X text 29 240 fundamental;
+#X text 31 2 ADDING TWO COSINES;
+#X text 234 152 freq. (in;
+#X text 233 171 tenths of;
+#X text 232 191 fundamental);
+#X text 29 259 frequency;
+#X obj 227 427 cos~;
+#X obj 268 340 wrap~;
+#X obj 232 367 -~;
+#X obj 227 397 +~;
+#X obj 232 464 -~;
+#X obj 247 494 *~;
+#X obj 225 519 +~;
+#X obj 59 578 tabwrite~ carrier;
+#X obj 33 308 phasor~ 40;
+#X obj 232 289 pack 0 50;
+#X text 27 36 The other \, spiffier way is to make a sum of cosines
+to interpolate between adjacent harmonics. Suppose for example we want
+a center frequency of 5.3 (in units of the fundamental.) We just take
+partial 5 with amplitude 0.7 and partial 6 with amplitude 0.7:;
+#X text 451 581 updated for Pd version 0.34;
+#X text 281 366 subtract to get the integer part "n";
+#X text 277 399 multiply phase by n and n+1;
+#X text 282 427 synthesize the two partials;
+#X text 54 526 graph;
+#X text 347 548 <--output;
+#X text 323 339 the fractional part "b";
+#X text 280 463 p2 - p1;
+#X text 295 492 b * (p2 - p1);
+#X text 264 519 b * p2 + (1-b) * p1;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 3 0 27 0;
+#X connect 4 0 24 1;
+#X connect 4 0 26 0;
+#X connect 5 0 6 0;
+#X connect 6 0 11 0;
+#X connect 8 0 4 0;
+#X connect 8 0 23 0;
+#X connect 10 0 22 0;
+#X connect 10 0 21 0;
+#X connect 11 0 29 0;
+#X connect 13 0 28 0;
+#X connect 20 0 24 0;
+#X connect 21 0 22 1;
+#X connect 21 0 25 1;
+#X connect 22 0 8 1;
+#X connect 23 0 20 0;
+#X connect 24 0 25 0;
+#X connect 25 0 26 1;
+#X connect 26 0 1 0;
+#X connect 26 0 27 0;
+#X connect 28 0 8 0;
+#X connect 28 0 23 1;
+#X connect 29 0 10 0;
diff --git a/pd/doc/3.audio.examples/62.declickit.pd b/pd/doc/3.audio.examples/62.declickit.pd
new file mode 100644
index 00000000..eb296e63
--- /dev/null
+++ b/pd/doc/3.audio.examples/62.declickit.pd
@@ -0,0 +1,132 @@
+#N canvas 10 49 715 680 12;
+#X floatatom 242 612 0 0 0;
+#N canvas 176 241 532 273 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 398 111 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 398 86 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 17 148 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 15 125 audio;
+#X text 93 110 show level;
+#X obj 17 177 hip~ 1;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 26 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 21 0;
+#X restore 206 639 pd output;
+#X msg 282 612 MUTE;
+#X obj 165 490 cos~;
+#X obj 165 460 *~;
+#X obj 207 490 cos~;
+#X obj 249 406 wrap~;
+#X obj 212 415 -~;
+#X obj 207 460 +~;
+#X obj 207 525 -~;
+#X obj 227 557 *~;
+#X obj 205 582 +~;
+#X text 454 648 updated for Pd version 0.34;
+#X text 327 611 <--output;
+#X obj 191 184 loadbang;
+#X obj 191 210 metro 400;
+#X obj 203 234 del 200;
+#X obj 204 335 samphold~;
+#X obj 259 364 toggle 20 0 empty empty empty 20 8 0 10 -262144 -1 -1
+0 1;
+#X obj 166 290 sig~;
+#X msg 203 263 3.5;
+#X msg 166 263 2;
+#X obj 23 280 phasor~ 169;
+#N canvas 0 0 600 400 switch 0;
+#X obj 85 52 inlet~;
+#X obj 177 58 inlet~;
+#X obj 298 66 inlet;
+#X obj 112 107 -~;
+#X obj 112 131 *~ 0;
+#X obj 90 157 +~;
+#X obj 160 249 outlet~;
+#X connect 0 0 3 1;
+#X connect 0 0 5 0;
+#X connect 1 0 3 0;
+#X connect 2 0 4 1;
+#X connect 3 0 4 0;
+#X connect 4 0 5 1;
+#X connect 5 0 6 0;
+#X restore 166 364 pd switch;
+#X text 31 2 CHANGING THE CENTER FREQUENCY QUICKLY;
+#X text 313 381 off to hear the straight sig~;
+#X text 286 363 <--on to hear the "samphold~" \,;
+#X text 25 27 Since in the previous patch the amplitudes of the two
+cosines depend on "center frequency" we can't change that discontinuously
+without clicking \, as you hear in this patch. The fix is to use a
+samphold~ object to keep the center frequency frozen except at phase
+crossings. At the phase crossings the two weighted cosines add to one
+\, so we can discontinuously change the frequencies and weights there.
+;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 3 0 9 1;
+#X connect 3 0 11 0;
+#X connect 4 0 3 0;
+#X connect 4 0 8 0;
+#X connect 5 0 9 0;
+#X connect 6 0 7 1;
+#X connect 6 0 10 1;
+#X connect 7 0 4 1;
+#X connect 8 0 5 0;
+#X connect 9 0 10 0;
+#X connect 10 0 11 1;
+#X connect 11 0 1 0;
+#X connect 14 0 15 0;
+#X connect 15 0 21 0;
+#X connect 15 0 16 0;
+#X connect 16 0 20 0;
+#X connect 17 0 23 1;
+#X connect 18 0 23 2;
+#X connect 19 0 17 0;
+#X connect 19 0 23 0;
+#X connect 20 0 19 0;
+#X connect 21 0 19 0;
+#X connect 22 0 4 0;
+#X connect 22 0 8 1;
+#X connect 22 0 17 1;
+#X connect 23 0 7 0;
+#X connect 23 0 6 0;
diff --git a/pd/doc/3.audio.examples/63.sweepable.FM.pd b/pd/doc/3.audio.examples/63.sweepable.FM.pd
new file mode 100644
index 00000000..ff3827ee
--- /dev/null
+++ b/pd/doc/3.audio.examples/63.sweepable.FM.pd
@@ -0,0 +1,161 @@
+#N canvas 89 117 803 661 12;
+#X floatatom 242 605 0 0 0;
+#N canvas 176 241 532 273 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 398 111 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 398 86 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 17 148 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 15 125 audio;
+#X text 93 110 show level;
+#X obj 17 177 hip~ 1;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 26 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 21 0;
+#X restore 204 634 pd output;
+#X msg 280 606 MUTE;
+#X obj 165 506 cos~;
+#X obj 165 460 *~;
+#X obj 208 508 cos~;
+#X obj 249 409 wrap~;
+#X obj 212 408 -~;
+#X obj 208 485 +~;
+#X obj 206 543 -~;
+#X obj 226 575 *~;
+#X obj 204 600 +~;
+#X text 520 628 updated for Pd version 0.34;
+#X text 325 605 <--output;
+#X obj 212 378 samphold~;
+#X text 31 2 APPLYING TWO-COSINE CARRIER TO FM;
+#X floatatom 229 238 4 0 0;
+#X obj 229 261 / 10;
+#X text 229 157 center;
+#X obj 229 330 line~;
+#X obj 229 284 max 0;
+#X text 229 177 freq. (in;
+#X text 229 197 tenths of;
+#X text 229 217 fundamental);
+#X obj 229 307 pack 0 50;
+#X obj 118 313 phasor~;
+#X floatatom 118 290 4 0 0;
+#X text 103 237 fundamental;
+#X text 103 257 (= mod freq);
+#X text 432 284 index;
+#X text 432 304 (percent);
+#X floatatom 432 325 4 0 0;
+#X obj 382 391 cos~;
+#X obj 432 394 line~;
+#X obj 382 414 *~;
+#X obj 432 348 / 100;
+#X obj 432 371 pack 0 50;
+#X obj 165 483 +~;
+#X graph graph1 0 0 128 500 515 256 771 126;
+#X array spectrum 128 float 0;
+#X pop;
+#X text 511 261 0;
+#X text 759 258 5512;
+#X msg 117 599 bang;
+#N canvas 204 17 358 238 fft 0;
+#X obj 46 48 inlet~;
+#X obj 159 181 tabwrite~ spectrum;
+#X obj 159 145 inlet;
+#X obj 46 78 rfft~;
+#X obj 46 111 *~;
+#X obj 77 111 *~;
+#X obj 46 141 sqrt~;
+#X obj 191 45 block~ 1024 1;
+#X connect 0 0 3 0;
+#X connect 2 0 1 0;
+#X connect 3 0 4 0;
+#X connect 3 0 4 1;
+#X connect 3 1 5 0;
+#X connect 3 1 5 1;
+#X connect 4 0 6 0;
+#X connect 5 0 6 0;
+#X connect 6 0 1 0;
+#X restore 68 630 pd fft;
+#X text 25 33 And now we just treat the cosines like carrier signals
+in an FM instrument. This doesn't work as well as you'd wish \, because
+the phases of the partials of the two FM instruments don't line up
+\, so that \, for indices of modulation above about 20% \, you get
+beating effects as the center frequency goes up and down.;
+#X text 385 440 modulating;
+#X text 385 460 oscillator;
+#X text 37 482 both phases-->;
+#X text 6 465 add modulator to;
+#X text 117 577 graph;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 3 0 9 1;
+#X connect 3 0 11 0;
+#X connect 4 0 37 0;
+#X connect 5 0 9 0;
+#X connect 6 0 7 1;
+#X connect 6 0 10 1;
+#X connect 7 0 4 1;
+#X connect 8 0 5 0;
+#X connect 9 0 10 0;
+#X connect 10 0 11 1;
+#X connect 11 0 1 0;
+#X connect 11 0 42 0;
+#X connect 14 0 7 0;
+#X connect 14 0 6 0;
+#X connect 16 0 17 0;
+#X connect 17 0 20 0;
+#X connect 19 0 14 0;
+#X connect 20 0 24 0;
+#X connect 24 0 19 0;
+#X connect 25 0 14 1;
+#X connect 25 0 32 0;
+#X connect 25 0 4 0;
+#X connect 25 0 8 1;
+#X connect 26 0 25 0;
+#X connect 31 0 35 0;
+#X connect 32 0 34 0;
+#X connect 33 0 34 1;
+#X connect 34 0 37 1;
+#X connect 35 0 36 0;
+#X connect 36 0 33 0;
+#X connect 37 0 8 0;
+#X connect 37 0 3 0;
+#X connect 41 0 42 1;
diff --git a/pd/doc/3.audio.examples/64.paf.pd b/pd/doc/3.audio.examples/64.paf.pd
new file mode 100644
index 00000000..56c024c0
--- /dev/null
+++ b/pd/doc/3.audio.examples/64.paf.pd
@@ -0,0 +1,234 @@
+#N canvas 53 0 782 687 12;
+#X floatatom 253 735 0 0 0;
+#N canvas 176 241 532 273 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 398 111 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 398 86 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 17 148 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 15 125 audio;
+#X text 93 110 show level;
+#X obj 17 177 hip~ 1;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 26 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 21 0;
+#X restore 215 764 pd output;
+#X msg 291 735 MUTE;
+#X obj 158 616 cos~;
+#X obj 158 593 *~;
+#X obj 201 616 cos~;
+#X obj 257 542 wrap~;
+#X obj 221 542 -~;
+#X obj 201 593 +~;
+#X obj 194 646 -~;
+#X obj 214 678 *~;
+#X obj 176 678 +~;
+#X text 515 768 updated for Pd version 0.34;
+#X text 336 734 <--output;
+#X obj 221 510 samphold~;
+#X floatatom 221 369 4 0 0;
+#X obj 221 392 / 10;
+#X text 221 288 center;
+#X obj 221 461 line~;
+#X obj 221 415 max 0;
+#X text 221 308 freq. (in;
+#X text 221 328 tenths of;
+#X text 221 348 fundamental);
+#X obj 221 438 pack 0 50;
+#X obj 104 445 phasor~;
+#X floatatom 104 399 4 0 0;
+#X text 84 372 fundamental;
+#X text 435 441 index;
+#X text 435 461 (percent);
+#X floatatom 435 482 4 0 0;
+#X obj 435 528 line~;
+#X obj 343 550 *~;
+#X obj 435 505 pack 0 50;
+#X graph graph1 0 0 128 500 510 395 766 265;
+#X array spectrum 128 float 0;
+#X pop;
+#X text 501 397 0;
+#X text 745 398 5512;
+#X msg 80 713 bang;
+#N canvas 204 17 358 238 fft 0;
+#X obj 46 48 inlet~;
+#X obj 159 181 tabwrite~ spectrum;
+#X obj 159 145 inlet;
+#X obj 46 78 rfft~;
+#X obj 46 111 *~;
+#X obj 77 111 *~;
+#X obj 46 141 sqrt~;
+#X obj 191 45 block~ 1024 1;
+#X connect 0 0 3 0;
+#X connect 2 0 1 0;
+#X connect 3 0 4 0;
+#X connect 3 0 4 1;
+#X connect 3 1 5 0;
+#X connect 3 1 5 1;
+#X connect 4 0 6 0;
+#X connect 5 0 6 0;
+#X connect 6 0 1 0;
+#X restore 31 745 pd fft;
+#X text 82 687 graph;
+#X text 31 2 THE PAF: TWO-COSINE RING MODULATOR FOR WAVESHAPER;
+#X graph graph4 0 0 199 1 563 718 763 578;
+#X array bell-curve 200 float 1;
+#A 0 1.12535e-07 1.54727e-07 2.12059e-07 2.89706e-07 3.94519e-07 5.35535e-07
+7.24633e-07 9.77371e-07 1.31404e-06 1.76105e-06 2.35258e-06 3.13275e-06
+4.15832e-06 5.50199e-06 7.25659e-06 9.54016e-06 1.25023e-05 1.63317e-05
+2.1266e-05 2.76026e-05 3.57128e-05 4.60584e-05 5.92113e-05 7.58768e-05
+9.69224e-05 0.00012341 0.000156634 0.000198167 0.000249912 0.000314163
+0.000393669 0.000491721 0.000612231 0.000759842 0.000940028 0.00115923
+0.00142498 0.00174605 0.00213263 0.00259648 0.00315111 0.00381201 0.00459678
+0.0055254 0.0066204 0.00790705 0.0094136 0.0111714 0.013215 0.0155826
+0.0183156 0.0214592 0.0250621 0.0291763 0.0338573 0.0391639 0.0451575
+0.0519019 0.0594631 0.0679081 0.0773047 0.0877205 0.0992216 0.111872
+0.125732 0.140858 0.1573 0.1751 0.194291 0.214896 0.236928 0.260383
+0.285247 0.311486 0.339053 0.367879 0.397882 0.428956 0.46098 0.493812
+0.527292 0.561244 0.595473 0.62977 0.663916 0.697676 0.730811 0.763074
+0.794216 0.823987 0.852144 0.878447 0.902668 0.924595 0.944027 0.960789
+0.974725 0.985703 0.99362 0.998401 1 0.998401 0.99362 0.985703 0.974725
+0.960789 0.944027 0.924595 0.902668 0.878447 0.852144 0.823987 0.794216
+0.763074 0.730811 0.697676 0.663916 0.62977 0.595473 0.561244 0.527292
+0.493812 0.46098 0.428956 0.397882 0.367879 0.339053 0.311486 0.285247
+0.260383 0.236928 0.214896 0.194291 0.1751 0.1573 0.140858 0.125732
+0.111872 0.0992216 0.0877205 0.0773047 0.0679081 0.0594631 0.0519019
+0.0451575 0.0391639 0.0338573 0.0291763 0.0250621 0.0214592 0.0183156
+0.0155826 0.013215 0.0111714 0.0094136 0.00790705 0.0066204 0.0055254
+0.00459678 0.00381201 0.00315111 0.00259648 0.00213263 0.00174605 0.00142498
+0.00115923 0.000940028 0.000759842 0.000612231 0.000491721 0.000393669
+0.000314163 0.000249912 0.000198167 0.000156634 0.00012341 9.69224e-05
+7.58768e-05 5.92113e-05 4.60584e-05 3.57128e-05 2.76026e-05 2.1266e-05
+1.63317e-05 1.25023e-05 9.54016e-06 7.25659e-06 5.50199e-06 4.15832e-06
+3.13275e-06 2.35258e-06 1.76105e-06 1.31404e-06 9.77371e-07 7.24633e-07
+5.35535e-07 3.94519e-07 2.89706e-07 2.12059e-07 1.54727e-07;
+#X pop;
+#N canvas 94 264 600 388 make-table 0;
+#X msg 81 44 bang;
+#X obj 81 73 t b b;
+#X obj 159 142 f;
+#X obj 197 142 + 1;
+#X msg 175 112 0;
+#X obj 81 102 until;
+#X obj 161 177 t f f;
+#X obj 76 306 tabwrite bell-curve;
+#X obj 52 270 expr exp(-$f1*$f1);
+#X obj 63 168 sel 199;
+#X obj 51 241 expr ($f1-100)/25;
+#X connect 0 0 1 0;
+#X connect 1 0 5 0;
+#X connect 1 1 4 0;
+#X connect 2 0 3 0;
+#X connect 2 0 6 0;
+#X connect 2 0 9 0;
+#X connect 3 0 2 1;
+#X connect 4 0 2 1;
+#X connect 5 0 2 0;
+#X connect 6 0 10 0;
+#X connect 6 1 7 1;
+#X connect 8 0 7 0;
+#X connect 9 0 5 1;
+#X connect 10 0 8 0;
+#X restore 627 538 pd make-table;
+#X obj 104 422 * 0.5;
+#X obj 343 527 cos~;
+#X obj 343 504 -~ 0.25;
+#X obj 343 573 +~ 100;
+#X obj 343 596 tabread4~ bell-curve;
+#X obj 104 474 *~ 2;
+#X obj 215 707 *~;
+#X text 25 33 Instead of using the two cosines as FM carrier oscillators
+\, we can use them as ring modulators for a synthetic tone. Here (as
+described in the paper) we use a sinusoid looking up a Gaussian bell
+curve. This has the nice properties that the partials are always positive
+cosines in phase \, and the spectrum spreads out smoothly as the index
+changes.;
+#X text 26 137 We needed the sine wave to have half the fundamental
+frequency \, so we run the phasor~ at half speed but double its output
+to the cosine pair and the samphold~ \, thus giving us the original
+frequency. As to the half-speed signal \, we take its sine (-~ 0.25
+and cos~) \, then center it for lookup in a 200-point table containing
+a bell curve.;
+#X text 251 705 <--ring mod step;
+#X text 375 621 waveshaper;
+#X text 27 239 Then with ~* we do the ring modulation and we're done.
+;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 3 0 9 1;
+#X connect 3 0 11 0;
+#X connect 4 0 8 0;
+#X connect 4 0 3 0;
+#X connect 5 0 9 0;
+#X connect 6 0 7 1;
+#X connect 6 0 10 1;
+#X connect 7 0 4 1;
+#X connect 8 0 5 0;
+#X connect 9 0 10 0;
+#X connect 10 0 11 1;
+#X connect 11 0 48 0;
+#X connect 14 0 7 0;
+#X connect 14 0 6 0;
+#X connect 15 0 16 0;
+#X connect 16 0 19 0;
+#X connect 18 0 14 0;
+#X connect 19 0 23 0;
+#X connect 23 0 18 0;
+#X connect 24 0 44 0;
+#X connect 24 0 47 0;
+#X connect 25 0 42 0;
+#X connect 29 0 32 0;
+#X connect 30 0 31 1;
+#X connect 31 0 45 0;
+#X connect 32 0 30 0;
+#X connect 36 0 37 1;
+#X connect 42 0 24 0;
+#X connect 43 0 31 0;
+#X connect 44 0 43 0;
+#X connect 45 0 46 0;
+#X connect 46 0 48 1;
+#X connect 47 0 14 1;
+#X connect 47 0 4 0;
+#X connect 47 0 8 0;
+#X connect 48 0 1 0;
+#X connect 48 0 37 0;
diff --git a/pd/doc/3.audio.examples/65.paf.control.pd b/pd/doc/3.audio.examples/65.paf.control.pd
new file mode 100644
index 00000000..7d329357
--- /dev/null
+++ b/pd/doc/3.audio.examples/65.paf.control.pd
@@ -0,0 +1,219 @@
+#N canvas 89 36 743 752 12;
+#X floatatom 217 684 0 0 0;
+#N canvas 176 241 532 273 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 398 111 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 398 86 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 17 148 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 15 125 audio;
+#X text 93 110 show level;
+#X obj 17 177 hip~ 1;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 26 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 21 0;
+#X restore 179 713 pd output;
+#X msg 255 684 MUTE;
+#X obj 122 565 cos~;
+#X obj 122 542 *~;
+#X obj 165 565 cos~;
+#X obj 220 510 wrap~;
+#X obj 184 510 -~;
+#X obj 165 542 +~;
+#X obj 158 595 -~;
+#X obj 178 627 *~;
+#X obj 140 627 +~;
+#X text 478 719 updated for Pd version 0.34;
+#X text 299 702 <--output;
+#X obj 184 478 samphold~;
+#X floatatom 183 266 4 0 0;
+#X text 181 218 center;
+#X obj 184 345 line~;
+#X obj 184 322 pack 0 50;
+#X obj 67 413 phasor~;
+#X floatatom 69 305 4 0 0;
+#X text 52 256 fundamental;
+#X floatatom 408 361 4 0 0;
+#X obj 408 438 line~;
+#X obj 306 518 *~;
+#X obj 408 415 pack 0 50;
+#N canvas 94 264 600 388 make-table 0;
+#X msg 81 44 bang;
+#X obj 81 73 t b b;
+#X obj 159 142 f;
+#X obj 197 142 + 1;
+#X msg 175 112 0;
+#X obj 81 102 until;
+#X obj 161 177 t f f;
+#X obj 76 306 tabwrite bell-curve;
+#X obj 52 270 expr exp(-$f1*$f1);
+#X obj 63 168 sel 199;
+#X obj 51 241 expr ($f1-100)/25;
+#X graph graph4 0 0 199 1 342 225 542 85;
+#X array bell-curve 200 float 1;
+#A 0 1.12535e-07 1.54727e-07 2.12059e-07 2.89706e-07 3.94519e-07 5.35535e-07
+7.24633e-07 9.77371e-07 1.31404e-06 1.76105e-06 2.35258e-06 3.13275e-06
+4.15832e-06 5.50199e-06 7.25659e-06 9.54016e-06 1.25023e-05 1.63317e-05
+2.1266e-05 2.76026e-05 3.57128e-05 4.60584e-05 5.92113e-05 7.58768e-05
+9.69224e-05 0.00012341 0.000156634 0.000198167 0.000249912 0.000314163
+0.000393669 0.000491721 0.000612231 0.000759842 0.000940028 0.00115923
+0.00142498 0.00174605 0.00213263 0.00259648 0.00315111 0.00381201 0.00459678
+0.0055254 0.0066204 0.00790705 0.0094136 0.0111714 0.013215 0.0155826
+0.0183156 0.0214592 0.0250621 0.0291763 0.0338573 0.0391639 0.0451575
+0.0519019 0.0594631 0.0679081 0.0773047 0.0877205 0.0992216 0.111872
+0.125732 0.140858 0.1573 0.1751 0.194291 0.214896 0.236928 0.260383
+0.285247 0.311486 0.339053 0.367879 0.397882 0.428956 0.46098 0.493812
+0.527292 0.561244 0.595473 0.62977 0.663916 0.697676 0.730811 0.763074
+0.794216 0.823987 0.852144 0.878447 0.902668 0.924595 0.944027 0.960789
+0.974725 0.985703 0.99362 0.998401 1 0.998401 0.99362 0.985703 0.974725
+0.960789 0.944027 0.924595 0.902668 0.878447 0.852144 0.823987 0.794216
+0.763074 0.730811 0.697676 0.663916 0.62977 0.595473 0.561244 0.527292
+0.493812 0.46098 0.428956 0.397882 0.367879 0.339053 0.311486 0.285247
+0.260383 0.236928 0.214896 0.194291 0.1751 0.1573 0.140858 0.125732
+0.111872 0.0992216 0.0877205 0.0773047 0.0679081 0.0594631 0.0519019
+0.0451575 0.0391639 0.0338573 0.0291763 0.0250621 0.0214592 0.0183156
+0.0155826 0.013215 0.0111714 0.0094136 0.00790705 0.0066204 0.0055254
+0.00459678 0.00381201 0.00315111 0.00259648 0.00213263 0.00174605 0.00142498
+0.00115923 0.000940028 0.000759842 0.000612231 0.000491721 0.000393669
+0.000314163 0.000249912 0.000198167 0.000156634 0.00012341 9.69224e-05
+7.58768e-05 5.92113e-05 4.60584e-05 3.57128e-05 2.76026e-05 2.1266e-05
+1.63317e-05 1.25023e-05 9.54016e-06 7.25659e-06 5.50199e-06 4.15832e-06
+3.13275e-06 2.35258e-06 1.76105e-06 1.31404e-06 9.77371e-07 7.24633e-07
+5.35535e-07 3.94519e-07 2.89706e-07 2.12059e-07 1.54727e-07;
+#X pop;
+#X connect 0 0 1 0;
+#X connect 1 0 5 0;
+#X connect 1 1 4 0;
+#X connect 2 0 3 0;
+#X connect 2 0 6 0;
+#X connect 2 0 9 0;
+#X connect 3 0 2 1;
+#X connect 4 0 2 1;
+#X connect 5 0 2 0;
+#X connect 6 0 10 0;
+#X connect 6 1 7 1;
+#X connect 8 0 7 0;
+#X connect 9 0 5 1;
+#X connect 10 0 8 0;
+#X restore 573 591 pd make-table;
+#X obj 67 390 * 0.5;
+#X obj 306 495 cos~;
+#X obj 306 472 -~ 0.25;
+#X obj 306 549 +~ 100;
+#X obj 306 572 tabread4~ bell-curve;
+#X obj 67 442 *~ 2;
+#X obj 179 656 *~;
+#X text 338 597 waveshaper;
+#X text 31 2 CHANGING PAF CONTROLS TO NATURAL UNITS;
+#X obj 67 362 mtof;
+#X obj 68 335 max 0;
+#X obj 206 370 expr 1/$f1;
+#X obj 183 296 mtof;
+#X text 181 238 freq.;
+#X obj 184 394 *~;
+#X text 406 340 bandwidth;
+#X obj 408 389 mtof;
+#X obj 408 474 *~;
+#X obj 408 498 *~ 25;
+#X text 25 33 The more "natural" units for describing a formant might
+be center frequency and bandwidth \, so that you can change the fundamental
+without having the formant shift up and down in parallel. Here all
+three frequencies are expressed in MIDI units. The bandwidth and center
+frequency have to be divided by the fundamental (the expr 1/$f1 takes
+its reciprocal and two *~ objects finish the division.);
+#X text 448 473 divide by fundamental;
+#X text 466 497 range for table;
+#X text 372 548 offset to middle of table;
+#X text 191 416 C.F. relative;
+#X text 192 432 to fundamental;
+#X text 48 275 (MIDI units);
+#X text 215 654 ring mod;
+#X text 25 150 And now you essentially have the PAF. Note \, however
+\, that there's a nice paf~ "external" object in the "extras" library
+that does this all more efficiently and takes care of a couple of subtle
+details we don't see here...;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 3 0 9 1;
+#X connect 3 0 11 0;
+#X connect 4 0 8 0;
+#X connect 4 0 3 0;
+#X connect 5 0 9 0;
+#X connect 6 0 7 1;
+#X connect 6 0 10 1;
+#X connect 7 0 4 1;
+#X connect 8 0 5 0;
+#X connect 9 0 10 0;
+#X connect 10 0 11 1;
+#X connect 11 0 33 0;
+#X connect 14 0 7 0;
+#X connect 14 0 6 0;
+#X connect 15 0 39 0;
+#X connect 17 0 41 0;
+#X connect 18 0 17 0;
+#X connect 19 0 29 0;
+#X connect 19 0 32 0;
+#X connect 20 0 37 0;
+#X connect 22 0 43 0;
+#X connect 23 0 44 0;
+#X connect 24 0 30 0;
+#X connect 25 0 23 0;
+#X connect 27 0 19 0;
+#X connect 28 0 24 0;
+#X connect 29 0 28 0;
+#X connect 30 0 31 0;
+#X connect 31 0 33 1;
+#X connect 32 0 14 1;
+#X connect 32 0 4 0;
+#X connect 32 0 8 0;
+#X connect 33 0 1 0;
+#X connect 36 0 27 0;
+#X connect 36 0 38 0;
+#X connect 37 0 36 0;
+#X connect 38 0 41 1;
+#X connect 38 0 44 1;
+#X connect 39 0 18 0;
+#X connect 41 0 14 0;
+#X connect 43 0 25 0;
+#X connect 44 0 45 0;
+#X connect 45 0 24 1;
diff --git a/pd/doc/3.audio.examples/66.PART9.quartic.pd b/pd/doc/3.audio.examples/66.PART9.quartic.pd
new file mode 100644
index 00000000..d71da05a
--- /dev/null
+++ b/pd/doc/3.audio.examples/66.PART9.quartic.pd
@@ -0,0 +1,140 @@
+#N canvas 57 35 614 650 12;
+#X graph graph1 0 0 40 1 151 551 551 301;
+#X array array-ampdb 41 float 1;
+#A 0 0.01 0.0112202 0.0125893 0.0141254 0.0158489 0.0177828 0.0199526
+0.0223872 0.0251189 0.0281838 0.0316228 0.0354813 0.0398107 0.0446684
+0.0501187 0.0562341 0.0630957 0.0707946 0.0794328 0.0891251 0.1 0.112202
+0.125893 0.141254 0.158489 0.177828 0.199526 0.223872 0.251189 0.281838
+0.316228 0.354813 0.398107 0.446684 0.501187 0.562341 0.630957 0.707946
+0.794328 0.891251 1;
+#X array array-dbdb 41 float 1;
+#A 0 0 0.025 0.05 0.075 0.1 0.125 0.15 0.175 0.2 0.225 0.25 0.275 0.3
+0.325 0.35 0.375 0.4 0.425 0.45 0.475 0.5 0.525 0.55 0.575 0.6 0.625
+0.65 0.675 0.7 0.725 0.75 0.775 0.8 0.825 0.85 0.875 0.9 0.925 0.95
+0.975 1;
+#X array array-4thpow 41 float 1;
+#A 0 0 3.90624e-07 6.25001e-06 3.16406e-05 1e-04 0.000244141 0.00050625
+0.000937891 0.0016 0.00256289 0.00390625 0.00571914 0.0081 0.0111566
+0.0150063 0.0197754 0.0256 0.0326254 0.0410062 0.0509067 0.0625 0.0759691
+0.0915063 0.109313 0.1296 0.152588 0.178506 0.207594 0.2401 0.276282
+0.316406 0.36075 0.4096 0.46325 0.522006 0.586182 0.6561 0.732094 0.814506
+0.903688 1;
+#X pop;
+#N canvas 293 37 890 657 otherstuff 0;
+#X obj 42 438 loadbang;
+#X msg 259 94 bang;
+#X obj 259 123 t b b;
+#X obj 337 192 f;
+#X obj 375 192 + 1;
+#X msg 353 162 0;
+#X obj 259 152 until;
+#X obj 263 329 dbtorms;
+#X obj 339 227 t f f;
+#X msg 51 101 bang;
+#X obj 51 130 t b b;
+#X obj 129 199 f;
+#X obj 167 199 + 1;
+#X msg 145 169 0;
+#X obj 51 159 until;
+#X obj 131 234 t f f;
+#X obj 59 339 tabwrite array-dbdb;
+#X obj 263 355 tabwrite array-ampdb;
+#X msg 505 98 bang;
+#X obj 505 127 t b b;
+#X obj 583 196 f;
+#X obj 621 196 + 1;
+#X msg 599 166 0;
+#X obj 505 156 until;
+#X obj 585 231 t f f;
+#X obj 559 432 expr $f1 * $f1 * $f1 * $f1;
+#X obj 559 341 expr 1 + $f2 * ($f1 - 1);
+#X obj 705 253 loadbang;
+#X floatatom 703 309 0 0 0;
+#X msg 705 281 1;
+#X obj 559 385 max 0;
+#X obj 561 464 tabwrite array-4thpow;
+#X obj 263 274 + 100;
+#X obj 51 232 sel 40;
+#X obj 258 221 sel 40;
+#X obj 503 226 sel 40;
+#X obj 559 300 / 40;
+#X obj 263 302 - 40;
+#X obj 93 303 / 40;
+#X msg 43 465 \; graph1 xlabel -0.03 0 10 20 30 40 \; graph1 ylabel
+-2 0.25 0.5 0.75 1;
+#X text 53 27 (here's how I computed the three transfer functions...)
+;
+#X connect 0 0 39 0;
+#X connect 1 0 2 0;
+#X connect 2 0 6 0;
+#X connect 2 1 5 0;
+#X connect 3 0 4 0;
+#X connect 3 0 8 0;
+#X connect 3 0 34 0;
+#X connect 4 0 3 1;
+#X connect 5 0 3 1;
+#X connect 6 0 3 0;
+#X connect 7 0 17 0;
+#X connect 8 0 32 0;
+#X connect 8 1 17 1;
+#X connect 9 0 10 0;
+#X connect 10 0 14 0;
+#X connect 10 1 13 0;
+#X connect 11 0 12 0;
+#X connect 11 0 15 0;
+#X connect 11 0 33 0;
+#X connect 12 0 11 1;
+#X connect 13 0 11 1;
+#X connect 14 0 11 0;
+#X connect 15 0 38 0;
+#X connect 15 1 16 1;
+#X connect 18 0 19 0;
+#X connect 19 0 23 0;
+#X connect 19 1 22 0;
+#X connect 20 0 21 0;
+#X connect 20 0 24 0;
+#X connect 20 0 35 0;
+#X connect 21 0 20 1;
+#X connect 22 0 20 1;
+#X connect 23 0 20 0;
+#X connect 24 0 36 0;
+#X connect 24 1 31 1;
+#X connect 25 0 31 0;
+#X connect 26 0 30 0;
+#X connect 27 0 29 0;
+#X connect 28 0 26 1;
+#X connect 29 0 28 0;
+#X connect 30 0 25 0;
+#X connect 32 0 37 0;
+#X connect 33 0 14 1;
+#X connect 34 0 6 1;
+#X connect 35 0 23 1;
+#X connect 36 0 26 0;
+#X connect 37 0 7 0;
+#X connect 38 0 16 0;
+#X restore 53 608 pd otherstuff;
+#X text 292 403 linear;
+#X text 279 509 decibels;
+#X text 387 518 quartic;
+#X text 45 5 QUARTIC CURVES AS THE IDEAL AMPLITUDE AND FREQUENCY SCALERS
+;
+#X text 346 611 updated for Pd version 0.34;
+#X text 246 578 units-->;
+#X text 45 447 amplitude;
+#X text 79 429 |;
+#X text 79 420 |;
+#X text 79 410 |;
+#X text 79 402 |;
+#X text 78 398 ^;
+#X text 38 149 The graph below shows that a simple quartic curve \,
+x-to-the-fourth-power \, twists like decibels but--unlike decibels--actually
+hits zero at left. You get the best of both worlds. Moreover \, raising
+something to the fourth power is very cheap: just two multiplications--whereas
+\, if you're computing envelopes in dB \, eventually you'll have to
+exponentiate \, sample by sample \, to get to linear units.;
+#X text 36 34 It's an old saw that we perceive amplitude and frequency
+logarithmically. But using decibels as a unit for controlling amplitude
+and frequency gets ugly for two reasons. First \, it's expensive to
+do the conversion. Second and more profoundly \, decibels grow by shifting
+\, and things should grow by scaling \, so that \, for example \, zero
+really means "nothing.";
diff --git a/pd/doc/3.audio.examples/67.more.quartic.pd b/pd/doc/3.audio.examples/67.more.quartic.pd
new file mode 100644
index 00000000..fdb01dab
--- /dev/null
+++ b/pd/doc/3.audio.examples/67.more.quartic.pd
@@ -0,0 +1,147 @@
+#N canvas 130 66 880 587 12;
+#X floatatom 89 506 0 0 100;
+#N canvas 159 26 516 274 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 396 182 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 391 110 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 391 85 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 20 182 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 104 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 90 outlet;
+#X msg 214 65 \; pd dsp 1;
+#X obj 83 198 line~;
+#X obj 20 207 *~;
+#X obj 20 232 dac~;
+#X obj 83 173 pack 0 50;
+#X text 20 159 audio;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 21 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X restore 51 534 pd output;
+#X msg 127 507 MUTE;
+#X obj 32 441 *~;
+#X obj 12 469 -~;
+#X obj 73 446 *~;
+#X floatatom 170 421 1 0 100;
+#X obj 365 337 osc~;
+#X obj 365 362 *~;
+#X obj 425 342 line~;
+#X obj 365 312 line~;
+#X obj 550 468 osc~;
+#X obj 599 496 *~;
+#X obj 621 379 line~;
+#X obj 550 379 line~;
+#X obj 550 326 sqrt;
+#X obj 550 352 sqrt;
+#X obj 621 326 sqrt;
+#X obj 621 352 sqrt;
+#X obj 550 411 *~;
+#X obj 550 441 *~;
+#X obj 621 411 *~;
+#X obj 621 440 *~;
+#X obj 550 301 unpack;
+#X obj 621 301 unpack;
+#X obj 365 287 r freq;
+#X obj 425 318 r amp;
+#X obj 550 276 r freq;
+#X obj 621 276 r amp;
+#X obj 365 388 s~ linear;
+#X obj 599 523 s~ quartic;
+#X obj 12 377 r~ linear;
+#X obj 56 406 r~ quartic;
+#X msg 27 185 \; amp 0 5000 \;;
+#X msg 29 139 \; amp 1 5000 \;;
+#X msg 139 185 \; amp 0 1000 \;;
+#X msg 141 139 \; amp 1 1000 \;;
+#X msg 26 238 \; freq 1760 5000 \;;
+#X msg 29 286 \; freq 55 5000 \;;
+#X msg 180 238 \; freq 1760 1000 \;;
+#X msg 183 286 \; freq 55 1000 \;;
+#X text 90 15 QUARTIC AND LINEAR ENVELOPES COMPARED;
+#X obj 202 488 loadbang;
+#X msg 202 516 \; amp 1 \; freq 1760;
+#X text 194 414 1 for quartic \; 0 for linear;
+#X text 19 39 This patch has two sine wave oscillators \, one with
+linear envelopes \, the other with quartic ones which sound more uniform.
+The "toggle switch" at bottom selects between the two \, and the message
+boxes sweep the amplitude and frequency up and down.;
+#X text 366 257 LINEAR;
+#X text 555 249 QUARTIC;
+#X text 335 120 The two oscillators are below. In the quartic one \,
+for both the amplitude and the frequency \, we have to take the fourth
+root of the target value (which we get by taking square root twice.)
+Then we raise the line~ output to the fourth power by squaring twice
+(the *~ objects \, whose left and right inlets are the same.) The cost
+is mostly that of the four additional *~ objects.;
+#X text 579 560 updated for Pd version 0.34;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 3 0 4 1;
+#X connect 4 0 1 0;
+#X connect 5 0 1 0;
+#X connect 6 0 5 1;
+#X connect 6 0 3 1;
+#X connect 7 0 8 0;
+#X connect 8 0 29 0;
+#X connect 9 0 8 1;
+#X connect 10 0 7 0;
+#X connect 11 0 12 0;
+#X connect 12 0 30 0;
+#X connect 13 0 21 0;
+#X connect 13 0 21 1;
+#X connect 14 0 19 0;
+#X connect 14 0 19 1;
+#X connect 15 0 16 0;
+#X connect 16 0 14 0;
+#X connect 17 0 18 0;
+#X connect 18 0 13 0;
+#X connect 19 0 20 0;
+#X connect 19 0 20 1;
+#X connect 20 0 11 0;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 22 0 12 1;
+#X connect 23 0 15 0;
+#X connect 23 1 14 1;
+#X connect 24 0 17 0;
+#X connect 24 1 13 1;
+#X connect 25 0 10 0;
+#X connect 26 0 9 0;
+#X connect 27 0 23 0;
+#X connect 28 0 24 0;
+#X connect 31 0 3 0;
+#X connect 31 0 4 0;
+#X connect 32 0 5 0;
+#X connect 42 0 43 0;
diff --git a/pd/doc/3.audio.examples/68.qlist.pd b/pd/doc/3.audio.examples/68.qlist.pd
new file mode 100644
index 00000000..58495ca1
--- /dev/null
+++ b/pd/doc/3.audio.examples/68.qlist.pd
@@ -0,0 +1,102 @@
+#N canvas 233 179 684 516 12;
+#X floatatom 57 459 0 0 0;
+#N canvas 159 26 497 272 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 425 153 t b f;
+#X obj 397 117 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 92 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 22 182 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 199 100 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 20 159 audio;
+#X text 93 110 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 19 488 pd output;
+#X msg 95 459 MUTE;
+#X obj 19 211 osc-voice amp1 pit1;
+#X obj 19 240 osc-voice amp2 pit2;
+#X obj 19 269 osc-voice amp3 pit3;
+#X obj 19 298 osc-voice amp4 pit4;
+#X obj 19 327 osc-voice amp5 pit5;
+#X obj 19 356 osc-voice amp6 pit6;
+#X obj 19 385 osc-voice amp7 pit7;
+#X obj 19 414 osc-voice amp8 pit8;
+#X obj 467 382 qlist;
+#X msg 389 226 stop;
+#X msg 527 339 read qlist.txt;
+#X obj 527 294 loadbang;
+#X text 261 203 start;
+#X text 391 202 stop;
+#X text 537 318 reread file;
+#X msg 470 238 rewind;
+#X msg 538 238 next;
+#X msg 258 254 tempo 100 \, bang;
+#X msg 253 227 tempo 1 \, bang;
+#X text 82 11 USING QLIST TO SEQUENCE AN OSCILLATOR BANK;
+#X text 474 215 single step;
+#X obj 556 454 r #;
+#X text 35 61 Here is an eight voice additive synthesis patch controlled
+by a qlist. Open a text editor on the file \, "qlist.txt" \, to see
+how the oscillators' amplitudes and frequencies are specified. The
+abstraction \, "osc-voice" \, shows an effective way to make patches
+react to qlists but also to mousing.;
+#X text 258 453 this is where qlist comments go:;
+#X text 418 485 updatged for Pd version 0.34;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 3 0 4 0;
+#X connect 4 0 5 0;
+#X connect 5 0 6 0;
+#X connect 6 0 7 0;
+#X connect 7 0 8 0;
+#X connect 8 0 9 0;
+#X connect 9 0 10 0;
+#X connect 10 0 1 0;
+#X connect 12 0 11 0;
+#X connect 13 0 11 0;
+#X connect 14 0 13 0;
+#X connect 18 0 11 0;
+#X connect 19 0 11 0;
+#X connect 20 0 11 0;
+#X connect 21 0 11 0;
diff --git a/pd/doc/3.audio.examples/69.more.adsr.pd b/pd/doc/3.audio.examples/69.more.adsr.pd
new file mode 100644
index 00000000..5b38917a
--- /dev/null
+++ b/pd/doc/3.audio.examples/69.more.adsr.pd
@@ -0,0 +1,117 @@
+#N canvas 105 38 705 609 12;
+#X obj 39 140 r trigger;
+#X floatatom 70 376 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 351 166 t b;
+#X obj 351 114 f;
+#X obj 351 62 inlet;
+#X text 358 30 mute;
+#X obj 351 192 f;
+#X msg 442 185 0;
+#X msg 351 88 bang;
+#X obj 351 140 moses 1;
+#X obj 442 159 t b f;
+#X obj 413 122 moses 1;
+#X obj 86 154 dbtorms;
+#X obj 413 96 r master-lvl;
+#X obj 86 44 r master-lvl;
+#X obj 351 218 s master-lvl;
+#X obj 23 188 inlet~;
+#X obj 207 42 inlet;
+#X text 207 19 level;
+#X obj 207 104 s master-lvl;
+#X msg 100 67 set \$1;
+#X obj 100 93 outlet;
+#X msg 222 66 \; pd dsp 1;
+#X obj 86 202 line~;
+#X obj 23 221 *~;
+#X obj 23 250 dac~;
+#X obj 86 178 pack 0 50;
+#X text 21 165 audio;
+#X text 97 114 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 39 406 pd output;
+#X msg 107 376 MUTE;
+#X text 150 375 <-- output amplitude;
+#X text 518 189 <-- attack;
+#X text 485 281 <-- release;
+#X obj 39 338 *~;
+#X obj 39 282 *~;
+#X obj 39 310 *~;
+#X obj 80 340 osc~ 440;
+#X text 88 9 ADSR envelope;
+#X text 35 29 You can use the quartic trick to simplify patches using
+ADSR envelopes. For amplitude control it's especially simple. It's
+usually good enough in practice to control amplitudes by ranging an
+ADSR from 0 to 1 (divide by 100 so your input range can be a comfortable
+0-100) \, then take fourth power:;
+#X obj 77 227 / 100;
+#X floatatom 77 197 3 0 100;
+#X msg 383 269 \; trigger 0;
+#X obj 39 166 unpack;
+#X floatatom 39 197 1 0 100;
+#X msg 383 175 \; trigger 1 100;
+#X text 503 229 <-- softer attack;
+#X msg 382 222 \; trigger 1 60;
+#X text 441 583 updated for Pd version 0.34;
+#X text 14 437 Note that the units aren't dB \; for most of the range
+0-100 it's about 0.4 dB per unit. If you want something closer to dB
+\, you can set the scale as 0-40 instead of 0-100 (just change "/ 100"
+to "/ 40") and then you'll get the response shown in the first patch
+in this section.;
+#X obj 596 519 *~;
+#X obj 596 547 *~;
+#X floatatom 605 390 3 0 100;
+#X obj 605 416 mtof;
+#X obj 605 441 sqrt;
+#X obj 605 466 sqrt;
+#X text 174 526 To use ADSR to control pitch \, you should;
+#X text 173 545 usually just use real pitch units like this-->;
+#X obj 596 490 adsr 0;
+#X obj 39 253 adsr 0 100 200 70 300;
+#X connect 0 0 16 0;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 7 0 2 0;
+#X connect 8 0 9 0;
+#X connect 8 0 9 1;
+#X connect 9 0 7 0;
+#X connect 10 0 7 1;
+#X connect 13 0 32 1;
+#X connect 14 0 13 0;
+#X connect 16 0 17 0;
+#X connect 16 1 14 0;
+#X connect 17 0 32 0;
+#X connect 23 0 24 0;
+#X connect 23 0 24 1;
+#X connect 25 0 26 0;
+#X connect 26 0 27 0;
+#X connect 27 0 28 0;
+#X connect 28 0 31 1;
+#X connect 31 0 23 0;
+#X connect 31 0 23 1;
+#X connect 32 0 8 0;
+#X connect 32 0 8 1;
diff --git a/pd/doc/3.audio.examples/70.vibrato.pd b/pd/doc/3.audio.examples/70.vibrato.pd
new file mode 100644
index 00000000..78c38efd
--- /dev/null
+++ b/pd/doc/3.audio.examples/70.vibrato.pd
@@ -0,0 +1,158 @@
+#N canvas 80 10 736 726 12;
+#X obj 27 220 r trigger;
+#X floatatom 65 581 0 0 0;
+#N canvas 159 26 531 288 output 0;
+#X obj 351 166 t b;
+#X obj 351 114 f;
+#X obj 351 62 inlet;
+#X text 358 30 mute;
+#X obj 351 192 f;
+#X msg 442 185 0;
+#X msg 351 88 bang;
+#X obj 351 140 moses 1;
+#X obj 413 122 moses 1;
+#X obj 86 154 dbtorms;
+#X obj 413 96 r master-lvl;
+#X obj 86 44 r master-lvl;
+#X obj 351 218 s master-lvl;
+#X obj 24 163 inlet~;
+#X obj 207 42 inlet;
+#X text 207 19 level;
+#X obj 207 104 s master-lvl;
+#X msg 100 67 set \$1;
+#X obj 100 93 outlet;
+#X msg 222 66 \; pd dsp 1;
+#X obj 86 202 line~;
+#X obj 23 221 *~;
+#X obj 23 250 dac~;
+#X obj 86 178 pack 0 50;
+#X text 22 140 audio;
+#X obj 442 159 t b;
+#X obj 21 191 hip~ 1;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 25 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 26 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 25 0 5 0;
+#X connect 26 0 21 0;
+#X restore 26 610 pd output;
+#X msg 102 581 MUTE;
+#X obj 27 446 *~;
+#X obj 27 474 *~;
+#X floatatom 62 277 3 0 100;
+#X msg 484 482 \; trigger 0;
+#X obj 27 246 unpack;
+#X floatatom 27 277 1 0 100;
+#X text 463 668 updated for Pd version 0.34;
+#X obj 26 525 +~ 0.3;
+#X obj 26 551 cos~;
+#X obj 26 499 osc~;
+#X text 88 9 PORTAMENTO AND VIBRATO;
+#X obj 62 300 mtof;
+#X obj 62 325 sqrt;
+#X obj 62 350 sqrt;
+#X text 619 402 <-- midC;
+#X text 607 444 <-- octave up;
+#X msg 484 388 \; trigger 1 60;
+#X msg 483 435 \; trigger 1 72;
+#X text 584 488 <-- release;
+#X text 590 506 is optional;
+#X obj 27 416 *~;
+#X obj 236 396 +~ 1;
+#X graph graph1 0 -1 130 1 433 643 633 543;
+#X array array62 131 float 1;
+#A 0 0.970031 1 0.970031 0.881921 0.740952 0.555571 0.336891 0.0980184
+-0.146729 -0.382682 -0.595698 -0.773009 -0.88 -0.9 -0.92 -0.92 -0.85773
+-0.707109 -0.514106 -0.290288 -0.0490716 0.195086 0.427551 0.63439
+0.803205 0.86 0.88 0.88 0.88 0.84 0.82 0.471402 0.242986 6.63397e-06
+-0.242974 -0.471391 -0.671554 -0.831465 -0.941541 -0.995184 -0.989178
+-0.923883 -0.803213 -0.68 -0.42 -0.24 0.1 0.4 0.6 0.7071 0.857723 0.956937
+0.998795 0.980787 0.903994 0.773018 0.595708 0.382694 0.146742 -0.0980052
+-0.336878 -0.55556 -0.7 -0.8 -0.88 -0.88 -0.88 -0.84 -0.82 -0.555582
+-0.336903 -0.0980316 0.146716 0.38267 0.595687 0.773001 0.903983 0.980782
+0.998796 0.956945 0.857737 0.707119 0.514117 0.290301 0.0490849 -0.195073
+-0.427539 -0.63438 -0.803197 -0.923873 -0.989174 -0.995187 -0.94155
+-0.83148 -0.671573 -0.471414 -0.242999 -1.99019e-05 0.242961 0.471379
+0.671544 0.831458 0.88 0.9 0.9 0.88 0.803221 0.63441 0.08 -0.14 -0.28
+-0.48 -0.64 -0.72 -0.857717 -0.956933 -0.998794 -0.98079 -0.904 -0.773026
+-0.595719 -0.382706 -0.146755 0.097992 0.336866 0.555549 0.740934 0.881909
+0.970025 1 0.970038;
+#X pop;
+#X obj 236 342 tabosc4~ array62;
+#X floatatom 236 286 3 0 0;
+#X obj 236 313 / 6;
+#X obj 236 370 *~;
+#X floatatom 390 323 3 0 0;
+#X text 235 421 since we'll multiply \,;
+#X text 234 436 vibrato output should;
+#X text 234 453 be centered at 1 \, not 0;
+#X text 275 372 multiply by vib depth;
+#X obj 390 350 / 6923;
+#X text 28 35 Portamento can be treated as a special case of an ADSR
+envelope \, with 100 percent sustain. Vibrato is properly computed
+in units of pitch \, but it's also easy to add vibrato to the envelope--before
+raising it to the fourth power \, so that it acts pseudo-logarithmically.
+Rather than add to the ADSR output \, we multiply a signal which controls
+relative frequency. The relative frequency change is one plus an oscillator.
+;
+#X text 61 417 apply vibrato;
+#X text 65 445 fourth;
+#X text 68 461 power;
+#X text 96 529 waveform;
+#X text 95 509 simple;
+#X text 465 344 4/(exp(log(2)/1200)-1);
+#X text 469 325 conversion factor is;
+#X text 383 279 vibrato depth;
+#X text 382 296 in cents;
+#X text 233 245 vibrato speed;
+#X text 232 262 in Hertz;
+#X text 152 168 I made a table with 6 cycles of vibrato and made small
+changes with the mouse to get a not-exactly-repeating vibrato \, and
+thus have to divide vibrato frequency by 6 You can just use a sine
+or triangle wave if you prefer.;
+#X obj 27 375 adsr 0 100 200 100 300;
+#X connect 0 0 8 0;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 4 0 5 0;
+#X connect 4 0 5 1;
+#X connect 5 0 13 0;
+#X connect 6 0 15 0;
+#X connect 8 0 9 0;
+#X connect 8 1 6 0;
+#X connect 9 0 50 0;
+#X connect 11 0 12 0;
+#X connect 12 0 2 0;
+#X connect 13 0 11 0;
+#X connect 15 0 16 0;
+#X connect 16 0 17 0;
+#X connect 17 0 50 1;
+#X connect 24 0 4 0;
+#X connect 24 0 4 1;
+#X connect 25 0 24 1;
+#X connect 27 0 30 0;
+#X connect 28 0 29 0;
+#X connect 29 0 27 0;
+#X connect 30 0 25 0;
+#X connect 31 0 36 0;
+#X connect 36 0 30 1;
+#X connect 50 0 24 0;
diff --git a/pd/doc/3.audio.examples/71.adsr.sequenced.pd b/pd/doc/3.audio.examples/71.adsr.sequenced.pd
new file mode 100644
index 00000000..26300054
--- /dev/null
+++ b/pd/doc/3.audio.examples/71.adsr.sequenced.pd
@@ -0,0 +1,217 @@
+#N canvas 32 15 950 613 12;
+#X obj 33 220 r trigger;
+#X floatatom 70 520 0 0 0;
+#N canvas 159 26 584 307 output 0;
+#X obj 390 189 t b;
+#X obj 390 129 f;
+#X obj 390 69 inlet;
+#X text 397 32 mute;
+#X obj 390 219 f;
+#X msg 451 217 0;
+#X msg 390 99 bang;
+#X obj 390 159 moses 1;
+#X obj 460 137 moses 1;
+#X obj 100 178 dbtorms;
+#X obj 460 107 r master-lvl;
+#X obj 100 50 r master-lvl;
+#X obj 390 249 s master-lvl;
+#X obj 26 217 inlet~;
+#X obj 239 49 inlet;
+#X text 239 22 level;
+#X obj 239 120 s master-lvl;
+#X msg 115 78 set \$1;
+#X obj 115 107 outlet;
+#X msg 257 77 \; pd dsp 1;
+#X obj 100 233 line~;
+#X obj 26 254 *~;
+#X obj 26 289 dac~;
+#X obj 100 205 pack 0 50;
+#X text 24 190 audio;
+#X text 112 132 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 21 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X restore 32 546 pd output;
+#X msg 108 521 MUTE;
+#X obj 33 438 *~;
+#X obj 33 331 *~ 0.01;
+#X obj 33 366 *~;
+#X obj 33 396 *~;
+#X obj 80 360 r pitch;
+#X obj 80 410 mtof;
+#X floatatom 80 385 4 0 0;
+#X floatatom 57 272 4 0 0;
+#X obj 57 247 r level;
+#X floatatom 131 272 4 0 0;
+#X obj 131 247 r attack;
+#X floatatom 216 272 4 0 0;
+#X obj 216 247 r decay;
+#X floatatom 291 272 4 0 0;
+#X floatatom 385 272 4 0 0;
+#X obj 291 247 r sustain;
+#X obj 385 247 r release;
+#X obj 509 106 r note;
+#X msg 510 195 \; trigger 1;
+#X obj 634 185 del;
+#X msg 634 210 \; trigger 0;
+#X obj 9 167 qlist;
+#X obj 8 6 r qlist;
+#X msg 30 35 bang;
+#X msg 30 60 rewind;
+#X obj 37 89 r tempo;
+#X floatatom 37 114 4 0 0;
+#X msg 37 139 tempo \$1;
+#X obj 509 156 t b f;
+#X obj 564 157 s pitch;
+#X obj 656 120 r duration;
+#X floatatom 656 145 4 0 0;
+#X floatatom 509 131 4 0 0;
+#X obj 289 320 r trigger;
+#X floatatom 315 376 4 0 0;
+#X floatatom 387 406 4 0 0;
+#X floatatom 477 406 4 0 0;
+#X floatatom 563 406 4 0 0;
+#X floatatom 659 406 4 0 0;
+#X obj 315 351 r level2;
+#X obj 387 381 r attack2;
+#X obj 477 381 r decay2;
+#X obj 563 381 r sustain2;
+#X obj 659 381 r release2;
+#X obj 80 435 tabosc4~ array1;
+#X floatatom 239 366 4 0 0;
+#X obj 33 482 vcf~;
+#X floatatom 140 488 4 0 0;
+#X obj 140 463 r q;
+#X obj 33 306 adsr 0 0 0 0 0;
+#X obj 289 444 adsr 0 0 0 0 0;
+#X obj 315 401 / 69.23;
+#X obj 239 391 mtof;
+#X obj 239 416 sqrt;
+#X obj 239 441 sqrt;
+#X obj 197 336 r filter;
+#X obj 240 494 *~;
+#X obj 240 519 *~;
+#X obj 289 469 +~ 1;
+#X obj 239 466 *~;
+#X text 139 215 ADSR for amplitude:;
+#X text 402 300 ADSR for filter. Here \, I thought it better to make
+the envelope modify a constant "filter pitch"--so the "filter" receive
+gets the "mtof" treatment and the ADSR is an offset in halftones (thus
+the "/ 69.23" as compared to the previous patch.);
+#X text 141 5 USING QLIST TO MAKE SEQUENCES OF "NOTES";
+#N canvas -10 258 703 380 otherstuff 0;
+#X obj 289 86 loadbang;
+#X obj 418 85 loadbang;
+#X graph graph2 0 -1 66 1 62 221 262 81;
+#X array array1 67 float 1;
+#A 0 0 0 0 0 0.714286 0.742857 0.757143 0.771429 0.778571 0.785714
+0.785714 0.785714 0.785714 0.790476 0.795238 0.614286 0.585714 0.442857
+0.271429 -0.128571 -0.142857 -0.157143 -0.171429 -0.642857 -0.528571
+-0.614286 -0.685714 -0.828571 -0.828571 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.557143 0.571429 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
+#X pop;
+#X msg 418 115 \; qlist read qlist2.txt;
+#X msg 289 111 \; level 100 \; attack 20 \; decay 300 \; sustain 70
+\; release 300 \; duration 300 \; pitch 72 \; filter 38 \; level2 49
+\; attack2 19 \; decay2 300 \; sustain2 17 \; release2 700 \; q 3 \;
+tempo 1;
+#X connect 0 0 4 0;
+#X connect 1 0 3 0;
+#X restore 32 571 pd otherstuff;
+#X text 82 34 <--start loop;
+#X text 99 62 <--stop loop;
+#X text 85 114 <--set tempo;
+#X text 248 34 The qlist reads the file \, "qlist2.txt" \, which contains
+four "note" messages and a message at the end that restarts the qlist
+at the beginning. The "note" messages are translated into a pitch change
+and triggers for the ADSRs:;
+#X text 694 573 updated for Pd version 0.34;
+#X text 155 573 <--loadbangs and table;
+#X msg 468 518 \; qlist read qlist2.txt;
+#X text 462 494 click to reload qlist2.txt;
+#X text 149 521 <--output;
+#X connect 0 0 53 0;
+#X connect 1 0 2 1;
+#X connect 2 0 1 0;
+#X connect 3 0 2 2;
+#X connect 4 0 50 0;
+#X connect 5 0 6 0;
+#X connect 5 0 6 1;
+#X connect 6 0 7 0;
+#X connect 6 0 7 1;
+#X connect 7 0 4 0;
+#X connect 8 0 10 0;
+#X connect 9 0 48 0;
+#X connect 10 0 9 0;
+#X connect 11 0 53 1;
+#X connect 12 0 11 0;
+#X connect 13 0 53 2;
+#X connect 14 0 13 0;
+#X connect 15 0 53 3;
+#X connect 16 0 15 0;
+#X connect 17 0 53 4;
+#X connect 18 0 53 5;
+#X connect 19 0 17 0;
+#X connect 20 0 18 0;
+#X connect 21 0 36 0;
+#X connect 23 0 24 0;
+#X connect 26 0 25 0;
+#X connect 27 0 25 0;
+#X connect 28 0 25 0;
+#X connect 29 0 30 0;
+#X connect 30 0 31 0;
+#X connect 31 0 25 0;
+#X connect 32 0 23 0;
+#X connect 32 0 22 0;
+#X connect 32 1 33 0;
+#X connect 34 0 35 0;
+#X connect 35 0 23 1;
+#X connect 36 0 32 0;
+#X connect 37 0 54 0;
+#X connect 38 0 55 0;
+#X connect 39 0 54 2;
+#X connect 40 0 54 3;
+#X connect 41 0 54 4;
+#X connect 42 0 54 5;
+#X connect 43 0 38 0;
+#X connect 44 0 39 0;
+#X connect 45 0 40 0;
+#X connect 46 0 41 0;
+#X connect 47 0 42 0;
+#X connect 48 0 4 1;
+#X connect 49 0 56 0;
+#X connect 50 0 2 0;
+#X connect 51 0 50 2;
+#X connect 52 0 51 0;
+#X connect 53 0 5 0;
+#X connect 54 0 62 0;
+#X connect 55 0 54 1;
+#X connect 56 0 57 0;
+#X connect 57 0 58 0;
+#X connect 58 0 63 0;
+#X connect 59 0 49 0;
+#X connect 60 0 61 0;
+#X connect 60 0 61 1;
+#X connect 61 0 50 1;
+#X connect 62 0 63 1;
+#X connect 63 0 60 0;
+#X connect 63 0 60 1;
diff --git a/pd/doc/3.audio.examples/72.execution.order.pd b/pd/doc/3.audio.examples/72.execution.order.pd
new file mode 100644
index 00000000..2bea8e92
--- /dev/null
+++ b/pd/doc/3.audio.examples/72.execution.order.pd
@@ -0,0 +1,127 @@
+#N canvas 100 17 724 631 12;
+#X floatatom 448 290 0 0 0;
+#X obj 70 432 +~;
+#X obj 91 401 vd~ delay1;
+#X floatatom 108 559 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 397 110 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 85 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 18 152 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 197 104 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 16 129 audio;
+#X text 93 110 show level;
+#X obj 18 179 hip~ 5;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 26 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 21 0;
+#X restore 70 585 pd output;
+#X msg 145 559 MUTE;
+#X text 184 558 <-- output amplitude;
+#X text 86 9 ORDER OF EXECUTION OF DELWRITE~ AND DELREAD~/VD~;
+#X text 43 35 If you're writing to and reading from a delay line \,
+you have to get the write sorted before the read or else you'll never
+get less than a block's delay. This patch compares a "wrong" flanger
+with a "right" one:;
+#X obj 69 299 noise~;
+#X obj 91 375 line~;
+#X obj 448 344 pack 0 100;
+#X text 518 292 <-- delay in samples;
+#X obj 92 505 *~;
+#X obj 92 481 -~;
+#X floatatom 175 505 1 0 0;
+#X obj 293 297 noise~;
+#X obj 423 398 line~;
+#N canvas 0 0 600 400 delay-writer 0;
+#X obj 96 107 inlet~;
+#X obj 96 180 outlet~;
+#X obj 116 144 delwrite~ delay2 1000;
+#X connect 0 0 1 0;
+#X connect 0 0 2 0;
+#X restore 293 325 pd delay-writer;
+#N canvas 0 0 280 330 delay-reader 0;
+#X obj 96 107 inlet~;
+#X obj 89 267 outlet~;
+#X obj 112 163 inlet~;
+#X obj 112 198 vd~ delay2;
+#X obj 89 237 +~;
+#X connect 0 0 4 0;
+#X connect 2 0 3 0;
+#X connect 3 0 4 1;
+#X connect 4 0 1 0;
+#X restore 293 427 pd delay-reader;
+#X obj 70 533 +~;
+#X text 194 505 <-- 0 to hear left-hand side \, 1 to hear right hand
+side.;
+#X text 46 105 All it took was to put the delread~ and vd~ objects
+in subpatches. The audio connections between the subpatches force the
+"reader" to be sorted after the "writer". DSP sorting in Pd follows
+the hierarchy of windows.;
+#X obj 447 318 / 44.1;
+#X obj 82 329 delwrite~ delay1 1000;
+#X text 450 596 updated for Pd version 0.34;
+#X text 43 173 To hear the difference scroll the delay time between
+0 and 100 samples. The patch at left doesn't let you get below 64 samples.
+;
+#X text 43 228 You can use the same strategy to avoid picking up 64-sample
+delays in send~/receive~ and throw~/catch~ pairs.;
+#X connect 0 0 23 0;
+#X connect 1 0 14 1;
+#X connect 1 0 20 0;
+#X connect 2 0 1 1;
+#X connect 3 0 4 1;
+#X connect 4 0 3 0;
+#X connect 5 0 4 2;
+#X connect 9 0 1 0;
+#X connect 9 0 24 0;
+#X connect 10 0 2 0;
+#X connect 11 0 10 0;
+#X connect 11 0 17 0;
+#X connect 13 0 20 1;
+#X connect 14 0 13 0;
+#X connect 15 0 13 1;
+#X connect 16 0 18 0;
+#X connect 17 0 19 1;
+#X connect 18 0 19 0;
+#X connect 19 0 14 0;
+#X connect 20 0 4 0;
+#X connect 23 0 11 0;
diff --git a/pd/doc/3.audio.examples/73.control.blocksize.pd b/pd/doc/3.audio.examples/73.control.blocksize.pd
new file mode 100644
index 00000000..05bad0d2
--- /dev/null
+++ b/pd/doc/3.audio.examples/73.control.blocksize.pd
@@ -0,0 +1,111 @@
+#N canvas 100 17 662 466 12;
+#X floatatom 130 389 0 0 0;
+#N canvas 159 26 495 266 output 0;
+#X obj 338 160 t b;
+#X obj 338 110 f;
+#X obj 338 60 inlet;
+#X text 344 29 mute;
+#X obj 338 185 f;
+#X msg 425 178 0;
+#X msg 338 85 bang;
+#X obj 338 135 moses 1;
+#X obj 397 110 moses 1;
+#X obj 83 148 dbtorms;
+#X obj 397 85 r master-lvl;
+#X obj 83 42 r master-lvl;
+#X obj 338 210 s master-lvl;
+#X obj 18 152 inlet~;
+#X obj 199 41 inlet;
+#X text 199 18 level;
+#X obj 197 104 s master-lvl;
+#X msg 96 65 set \$1;
+#X obj 96 89 outlet;
+#X msg 214 64 \; pd dsp 1;
+#X obj 83 194 line~;
+#X obj 22 212 *~;
+#X obj 22 241 dac~;
+#X obj 83 171 pack 0 50;
+#X text 16 129 audio;
+#X text 93 110 show level;
+#X obj 18 179 hip~ 5;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 12 0;
+#X connect 5 0 12 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 5 0;
+#X connect 8 1 4 1;
+#X connect 9 0 23 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 17 0;
+#X connect 13 0 26 0;
+#X connect 14 0 16 0;
+#X connect 14 0 19 0;
+#X connect 17 0 18 0;
+#X connect 20 0 21 1;
+#X connect 21 0 22 0;
+#X connect 21 0 22 1;
+#X connect 23 0 20 0;
+#X connect 26 0 21 0;
+#X restore 92 415 pd output;
+#X msg 167 389 MUTE;
+#X text 206 388 <-- output amplitude;
+#X obj 51 203 noise~;
+#N canvas 0 0 760 350 delay-writer 0;
+#X obj 75 100 inlet~;
+#X obj 79 250 outlet~;
+#X obj 90 194 delwrite~ delay3 1000;
+#X obj 379 97 block~ 1;
+#X obj 145 131 delread~ delay3;
+#X obj 144 159 *~ 0.99;
+#X obj 79 164 +~;
+#X obj 146 100 inlet;
+#X text 84 22 Because of the feedback \, the delwrite~ has to go after
+the delread~. So we set the blocksize to 1 to minimize the resulting
+delay.;
+#X connect 0 0 6 0;
+#X connect 4 0 5 0;
+#X connect 5 0 6 1;
+#X connect 6 0 2 0;
+#X connect 6 0 1 0;
+#X connect 7 0 4 0;
+#X restore 91 328 pd delay-writer;
+#X text 401 435 updated for Pd version 0.34;
+#X obj 77 285 *~;
+#X obj 273 275 expr 1000/$f1;
+#X obj 273 249 mtof;
+#X msg 176 220 1;
+#X msg 176 282 0;
+#X obj 177 164 metro 500;
+#X obj 273 195 random 60;
+#X obj 177 135 loadbang;
+#X obj 216 217 del 2;
+#X obj 273 221 + 30;
+#X obj 51 228 lop~ 1000;
+#X text 86 9 CONTROLLING DELAY WITH BLOCK~;
+#X text 75 52 In situations where a delay read feeds pack to a delay
+write \, you can shorten the minimum delay by changing the block size.
+Do this in a subpatch...;
+#X text 238 328 <-- here is the delay loop;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 4 0 17 0;
+#X connect 5 0 1 0;
+#X connect 7 0 5 0;
+#X connect 8 0 5 1;
+#X connect 9 0 8 0;
+#X connect 10 0 7 1;
+#X connect 11 0 7 1;
+#X connect 12 0 10 0;
+#X connect 12 0 13 0;
+#X connect 12 0 15 0;
+#X connect 13 0 16 0;
+#X connect 14 0 12 0;
+#X connect 15 0 11 0;
+#X connect 16 0 9 0;
+#X connect 17 0 7 0;
diff --git a/pd/doc/3.audio.examples/74.up.downsampling.pd b/pd/doc/3.audio.examples/74.up.downsampling.pd
new file mode 100644
index 00000000..cf50f9b9
--- /dev/null
+++ b/pd/doc/3.audio.examples/74.up.downsampling.pd
@@ -0,0 +1,191 @@
+#N canvas 32 25 1089 690 10;
+#X obj 57 567 osc~ 412;
+#X floatatom 58 543 5 0 0;
+#X obj 122 595 tabwrite~ scope;
+#X msg 122 571 bang;
+#X msg 205 43 bang;
+#X obj 42 191 tabwrite~ scope;
+#X msg 54 165 bang;
+#N canvas 316 181 600 400 simple 0;
+#X obj 185 46 inlet;
+#X obj 185 102 tabwrite~ scope;
+#X obj 78 38 inlet~;
+#X obj 78 258 outlet~;
+#X obj 317 103 block~ 64 1 0.25;
+#X connect 0 0 1 0;
+#X connect 2 0 1 0;
+#X connect 2 0 3 0;
+#X restore 42 64 pd simple downsampling 4;
+#X msg 451 42 bang;
+#X obj 275 190 tabwrite~ scope;
+#X msg 287 164 bang;
+#X graph graph2 0 -1 511 1 297 629 897 489;
+#X array scope 512 float 0;
+#X pop;
+#X msg 683 45 bang;
+#X obj 520 193 tabwrite~ scope;
+#X msg 532 167 bang;
+#X msg 929 44 bang;
+#X obj 753 192 tabwrite~ scope;
+#X msg 765 166 bang;
+#N canvas 165 168 600 400 simple 0;
+#X obj 185 74 inlet;
+#X obj 185 102 tabwrite~ scope;
+#X obj 78 38 inlet~;
+#X obj 78 258 outlet~;
+#X text 130 38 zero-padding upsampling;
+#X obj 317 103 block~ 64 1 4;
+#X connect 0 0 1 0;
+#X connect 2 0 1 0;
+#X connect 2 0 3 0;
+#X restore 520 66 pd simple upsampling 4;
+#X obj 42 38 r~ sine;
+#X obj 275 39 r~ sine;
+#X obj 57 595 s~ sine;
+#X obj 520 41 r~ sine;
+#X obj 753 41 r~ sine;
+#X obj 41 415 tabwrite~ scope;
+#X msg 47 369 bang;
+#X msg 804 265 bang;
+#X obj 711 415 tabwrite~ scope;
+#X msg 723 389 bang;
+#X obj 41 262 r~ sine;
+#X obj 711 264 r~ sine;
+#X obj 152 416 tabwrite~ scope;
+#X msg 158 370 bang;
+#X text 43 431 zero-padded;
+#N canvas 290 149 600 400 downsampling 0;
+#X obj 78 38 inlet~;
+#X obj 78 258 outlet~;
+#X obj 152 258 outlet~ hold;
+#X obj 317 103 block~ 64 1 0.25;
+#X connect 0 0 1 0;
+#X connect 0 0 2 0;
+#X restore 41 288 pd downsampling 4 (mixed);
+#N canvas 261 147 600 400 bad 0;
+#X obj 185 81 inlet;
+#X obj 185 102 tabwrite~ scope;
+#X text 172 257 "lin" is for linear upsampling;
+#X text 160 39 "lin" has no meaning when downsampling;
+#X obj 317 103 block~ 64 2;
+#X obj 78 258 outlet~;
+#X obj 78 38 inlet~;
+#X connect 0 0 1 0;
+#X connect 6 0 1 0;
+#X connect 6 0 5 0;
+#X restore 711 288 pd bad overlap;
+#X msg 1018 263 bang;
+#X obj 841 415 tabwrite~ scope;
+#X msg 853 389 bang;
+#X obj 841 264 r~ sine;
+#N canvas 121 72 600 400 bad 0;
+#X obj 185 81 inlet;
+#X obj 185 102 tabwrite~ scope;
+#X obj 78 258 outlet~ lin;
+#X obj 78 38 inlet~ lin;
+#X text 167 38 "lin" is for linear upsampling;
+#X text 166 259 "lin" has no meaning when downsampling;
+#X obj 317 103 block~ 64 2 2;
+#X connect 0 0 1 0;
+#X connect 3 0 1 0;
+#X connect 3 0 2 0;
+#X restore 841 288 pd bad overlap (upsampled);
+#X msg 458 267 bang;
+#X obj 323 417 tabwrite~ scope;
+#X msg 335 391 bang;
+#X obj 323 266 r~ sine;
+#X text 155 433 sample&hold;
+#X msg 653 265 bang;
+#X obj 511 416 tabwrite~ scope;
+#X msg 523 390 bang;
+#X obj 511 265 r~ sine;
+#N canvas 249 128 600 400 downsampled 0;
+#X obj 185 81 inlet;
+#X obj 185 102 tabwrite~ scope;
+#X obj 78 258 outlet~ lin;
+#X obj 78 230 *~ 0.5;
+#X obj 78 38 inlet~;
+#X obj 317 103 block~ 128 2 0.25;
+#X connect 0 0 1 0;
+#X connect 3 0 2 0;
+#X connect 4 0 1 0;
+#X connect 4 0 3 0;
+#X restore 511 289 pd downsampled overlap;
+#N canvas 175 94 600 400 upsampled 0;
+#X obj 185 81 inlet;
+#X obj 185 102 tabwrite~ scope;
+#X obj 78 258 outlet~ lin;
+#X obj 78 38 inlet~ lin;
+#X obj 78 230 *~ 0.5;
+#X obj 317 103 block~ 256 2 2;
+#X connect 0 0 1 0;
+#X connect 3 0 1 0;
+#X connect 3 0 4 0;
+#X connect 4 0 2 0;
+#X restore 323 290 pd upsampled overlap;
+#N canvas 350 164 600 400 upsampling 0;
+#X obj 185 81 inlet;
+#X obj 185 102 tabwrite~ scope;
+#X obj 78 38 inlet~ hold;
+#X obj 78 258 outlet~;
+#X text 160 39 "hold" is for sample&hold upsampling;
+#X obj 317 103 block~ 64 1 16;
+#X connect 0 0 1 0;
+#X connect 2 0 1 0;
+#X connect 2 0 3 0;
+#X restore 753 65 pd upsampling 16 (sample&hold);
+#N canvas 236 170 600 400 downsampling 0;
+#X obj 185 81 inlet;
+#X obj 185 102 tabwrite~ scope;
+#X obj 78 258 outlet~ lin;
+#X obj 78 38 inlet~ lin;
+#X text 172 257 "lin" is for linear upsampling;
+#X obj 320 102 block~ 64 1 0.125;
+#X text 330 137 0.125 = 1/8 = 8*downsampling;
+#X text 160 39 "lin" has (still !) no meaning when downsampling;
+#X connect 0 0 1 0;
+#X connect 3 0 1 0;
+#X connect 3 0 2 0;
+#X restore 275 63 pd downsampling 8 (linear);
+#X text 718 314 a pd-bug !;
+#X connect 0 0 2 0;
+#X connect 0 0 21 0;
+#X connect 1 0 0 0;
+#X connect 3 0 2 0;
+#X connect 4 0 7 1;
+#X connect 6 0 5 0;
+#X connect 7 0 5 0;
+#X connect 8 0 53 1;
+#X connect 10 0 9 0;
+#X connect 12 0 18 1;
+#X connect 14 0 13 0;
+#X connect 15 0 52 1;
+#X connect 17 0 16 0;
+#X connect 18 0 13 0;
+#X connect 19 0 7 0;
+#X connect 20 0 53 0;
+#X connect 22 0 18 0;
+#X connect 23 0 52 0;
+#X connect 25 0 24 0;
+#X connect 26 0 35 1;
+#X connect 28 0 27 0;
+#X connect 29 0 34 0;
+#X connect 30 0 35 0;
+#X connect 32 0 31 0;
+#X connect 34 0 24 0;
+#X connect 34 1 31 0;
+#X connect 35 0 27 0;
+#X connect 36 0 40 1;
+#X connect 38 0 37 0;
+#X connect 39 0 40 0;
+#X connect 40 0 37 0;
+#X connect 41 0 51 1;
+#X connect 43 0 42 0;
+#X connect 44 0 51 0;
+#X connect 46 0 50 1;
+#X connect 48 0 47 0;
+#X connect 49 0 50 0;
+#X connect 50 0 47 0;
+#X connect 51 0 42 0;
+#X connect 52 0 16 0;
+#X connect 53 0 9 0;
diff --git a/pd/doc/3.audio.examples/adsr.pd b/pd/doc/3.audio.examples/adsr.pd
new file mode 100644
index 00000000..b6b2d7d2
--- /dev/null
+++ b/pd/doc/3.audio.examples/adsr.pd
@@ -0,0 +1,78 @@
+#N canvas 112 36 785 655 12;
+#X obj 205 119 inlet;
+#X obj 412 147 inlet;
+#X text 201 94 trigger;
+#X obj 205 147 sel 0;
+#X obj 258 157 t b;
+#X obj 134 335 f \$1;
+#X obj 134 360 pack 0 \$2;
+#X obj 476 148 inlet;
+#X obj 403 281 del \$2;
+#X obj 458 429 line~;
+#X obj 432 304 f \$4;
+#X obj 466 379 pack 0 \$3;
+#X obj 537 149 inlet;
+#X obj 605 149 inlet;
+#X obj 678 148 inlet;
+#X msg 205 178 stop;
+#X obj 576 301 pack 0 \$5;
+#X text 410 124 level;
+#X obj 466 355 * \$1;
+#X text 31 306 ATTACK;
+#X obj 458 454 outlet~;
+#X text 6 329 recall level;
+#X text 6 349 and pack with;
+#X text 7 369 attack time;
+#X text 131 134 if zero;
+#X text 132 151 release;
+#X text 112 168 and cancel;
+#X text 143 185 decay;
+#X text 262 139 bang if attack;
+#X text 245 272 on attack \, set a;
+#X text 200 286 delay to go to sustain;
+#X text 242 303 recall sustain value;
+#X text 237 354 multiply by overall level;
+#X text 281 375 pack with decay time;
+#X text 569 327 on release ramp;
+#X text 570 344 back to zero;
+#X text 17 487 When you send this patch a nonzero trigger it schedules
+a line~ to do an attack and decay \, and if zero \, it starts the release
+ramp.;
+#X obj 432 329 * 0.01;
+#X text 16 539 Objects such as "f" and "pack" can be given dollar sign
+arguments to initialize their contents from adsr's creation arguments.
+Inlets are supplied to change them on the fly.;
+#X text 505 613 Updated for Pd version 0.34;
+#X text 86 4 ADSR ENVELOPE;
+#X text 245 327 convert from percent;
+#X text 76 24 Arguments: level \, attack time \, decay time \, sustain
+level \, release time. A \, D \, and R are in msec and S is in percent.
+This patch is used as an abstraction in 25.envelope.pd and others.
+;
+#X text 472 127 attack;
+#X text 538 126 decay;
+#X text 598 127 sustain;
+#X text 675 127 release;
+#X connect 0 0 3 0;
+#X connect 1 0 5 1;
+#X connect 1 0 18 1;
+#X connect 3 0 15 0;
+#X connect 3 0 16 0;
+#X connect 3 1 4 0;
+#X connect 4 0 5 0;
+#X connect 4 0 8 0;
+#X connect 5 0 6 0;
+#X connect 6 0 9 0;
+#X connect 7 0 6 1;
+#X connect 7 0 8 1;
+#X connect 8 0 10 0;
+#X connect 9 0 20 0;
+#X connect 10 0 37 0;
+#X connect 11 0 9 0;
+#X connect 12 0 11 1;
+#X connect 13 0 10 1;
+#X connect 14 0 16 1;
+#X connect 15 0 8 0;
+#X connect 16 0 9 0;
+#X connect 18 0 11 0;
+#X connect 37 0 18 0;
diff --git a/pd/doc/3.audio.examples/adsr2.pd b/pd/doc/3.audio.examples/adsr2.pd
new file mode 100644
index 00000000..157a9c51
--- /dev/null
+++ b/pd/doc/3.audio.examples/adsr2.pd
@@ -0,0 +1,110 @@
+#N canvas 207 5 728 714 12;
+#X obj 101 90 inlet;
+#X obj 328 115 inlet;
+#X text 101 69 trigger;
+#X obj 101 126 sel 0;
+#X obj 309 321 f \$1;
+#X obj 394 117 inlet;
+#X obj 474 385 line~;
+#X obj 456 242 f \$4;
+#X obj 474 334 pack 0 \$3;
+#X obj 461 117 inlet;
+#X obj 537 118 inlet;
+#X obj 616 117 inlet;
+#X msg 76 150 stop;
+#X obj 594 311 pack 0 \$5;
+#X text 328 94 level;
+#X obj 456 281 * \$1;
+#X text 309 377 ATTACK;
+#X obj 474 408 outlet~;
+#X text 44 125 release;
+#X text 54 47 This patch is used as an abstraction in 27.envelope.slew.pd
+;
+#X obj 295 421 snapshot~;
+#X obj 259 135 t b b;
+#X text 587 333 RELEASE;
+#X obj 181 261 * -1;
+#X text 480 353 DECAY;
+#X text 180 71 attack;
+#X text 2 238 correct the attack;
+#X text 12 254 multiplying by;
+#X text 5 275 (target-current)/;
+#X text 83 289 target;
+#X text 8 321 which is the;
+#X text 8 341 relative amount;
+#X text 8 361 of slew in the attack.;
+#X text 20 468 This version of the adsr envelope generator maintains
+a constant rate of climb in the attact portion rather than a constant
+attack time as the previous one did.;
+#X obj 174 290 / \$1;
+#X obj 174 313 * \$2;
+#X obj 309 355 pack;
+#X obj 181 238 - \$1;
+#X obj 174 336 abs;
+#X text 8 401 current value is greater than;
+#X text 6 421 the target.;
+#X text 8 381 The "abs" is in case the;
+#X text 19 519 The "attack time" input here controls the time needed
+to rist from 0 to the output level. When we get an attack \, we take
+a snapshot~ of ouy current output level and adjust the attack time
+accordingly.;
+#X obj 183 89 moses;
+#X text 267 106 from;
+#X text 269 118 here;
+#X obj 174 136 t b b;
+#X text 182 107 from;
+#X text 184 119 zero;
+#X msg 223 136 0;
+#X text 19 584 Another feature: if you give a negative number for the
+trigger \, the output jumps to zero and slews from there \, instead
+of slewing from the current level. This is often appropriate for pitch
+envelopes.;
+#X obj 456 305 * 0.01;
+#X text 457 679 updated for Pd version 0.34;
+#X obj 455 217 del;
+#X text 391 95 attack;
+#X text 458 93 decay;
+#X text 55 4 adsr2 - arguments: level \, attack time \, decay time
+\, sustain percentage \, release time;
+#X text 533 91 sustain;
+#X text 609 93 release;
+#X connect 0 0 3 0;
+#X connect 1 0 4 1;
+#X connect 1 0 15 1;
+#X connect 1 0 34 1;
+#X connect 1 0 37 1;
+#X connect 3 0 12 0;
+#X connect 3 0 13 0;
+#X connect 3 1 43 0;
+#X connect 4 0 36 0;
+#X connect 5 0 35 1;
+#X connect 6 0 17 0;
+#X connect 6 0 20 0;
+#X connect 7 0 15 0;
+#X connect 8 0 6 0;
+#X connect 9 0 8 1;
+#X connect 10 0 7 1;
+#X connect 11 0 13 1;
+#X connect 12 0 53 0;
+#X connect 13 0 6 0;
+#X connect 15 0 51 0;
+#X connect 20 0 37 0;
+#X connect 21 0 4 0;
+#X connect 21 0 53 0;
+#X connect 21 1 20 0;
+#X connect 23 0 34 0;
+#X connect 34 0 35 0;
+#X connect 35 0 38 0;
+#X connect 36 0 6 0;
+#X connect 37 0 23 0;
+#X connect 38 0 36 1;
+#X connect 38 0 53 1;
+#X connect 43 0 46 0;
+#X connect 43 1 21 0;
+#X connect 46 0 4 0;
+#X connect 46 0 53 0;
+#X connect 46 1 49 0;
+#X connect 49 0 6 0;
+#X connect 49 0 37 0;
+#X connect 51 0 8 0;
+#X connect 53 0 7 0;
diff --git a/pd/doc/3.audio.examples/echo.pd b/pd/doc/3.audio.examples/echo.pd
new file mode 100644
index 00000000..7666d5a7
--- /dev/null
+++ b/pd/doc/3.audio.examples/echo.pd
@@ -0,0 +1,17 @@
+#N canvas 0 0 600 492 12;
+#X obj 66 95 inlet~;
+#X obj 130 96 inlet~;
+#X obj 69 216 outlet~;
+#X obj 134 217 outlet~;
+#X obj 67 143 +~;
+#X obj 133 143 -~;
+#X obj 134 167 delwrite~ \$1 \$2;
+#X obj 133 193 delread~ \$1 \$2;
+#X text 67 24 This appears as an abstraction in patch 44;
+#X connect 0 0 4 0;
+#X connect 0 0 5 0;
+#X connect 1 0 4 1;
+#X connect 1 0 5 1;
+#X connect 4 0 2 0;
+#X connect 5 0 6 0;
+#X connect 7 0 3 0;
diff --git a/pd/doc/3.audio.examples/osc-voice.pd b/pd/doc/3.audio.examples/osc-voice.pd
new file mode 100644
index 00000000..48bb81ea
--- /dev/null
+++ b/pd/doc/3.audio.examples/osc-voice.pd
@@ -0,0 +1,89 @@
+#N canvas 153 209 946 576 12;
+#X obj 163 390 line~;
+#X obj 401 438 line~;
+#X obj 163 511 *~;
+#X obj 383 229 r \$1;
+#X obj 363 316 dbtorms;
+#X obj 383 281 unpack;
+#X obj 383 255 t l b;
+#X obj 401 412 pack;
+#X obj 447 283 30;
+#X obj 163 286 unpack;
+#X obj 163 260 r \$2;
+#X obj 163 470 osc~;
+#X obj 163 312 mtof;
+#X obj 363 342 sqrt;
+#X obj 363 368 sqrt;
+#X obj 163 338 sqrt;
+#X obj 163 364 sqrt;
+#X obj 163 418 *~;
+#X obj 163 444 *~;
+#X obj 401 464 *~;
+#X obj 400 492 *~;
+#X obj 96 486 inlet~;
+#X obj 96 538 outlet~;
+#X obj 96 512 +~;
+#X floatatom 293 342 0 0 0;
+#X msg 294 316 set \$1;
+#X obj 294 368 s \$1;
+#X floatatom 96 336 0 0 0;
+#X msg 96 310 set \$1;
+#X obj 96 362 s \$2;
+#X text 370 201 amplitude;
+#X text 157 233 pitch;
+#X text 27 36 The amplitude and pitch are controlled by quartic envelopes
+as in the previous example. Here we introduce two new features. First
+\, there are number boxes to show the most recent targets for amplitude
+and frequency \, which you can also use to change the values. Also
+\, if amplitude gets a message without an explicit time value \, we
+supply a default of "30".;
+#X text 27 149 Other small differences from the previous patch: pitch
+and amplitude are now in MIDI and dB \, and there's a summing bus arrangement
+(the inlet~ \, +~ \, and outlet~).;
+#X text 15 295 see or;
+#X text 16 315 change;
+#X text 16 336 pitch-->;
+#X text 233 325 and;
+#X text 233 342 amp-->;
+#X text 488 283 "30" is short for "float 30." This is;
+#X text 495 302 more CPU efficient than a message.;
+#X text 451 403 The "pack" always gets a 30 \, but if you send a pair
+of numbers to amplitude \, the second one overrides the 30;
+#X text 439 254 first bang the "30" \, then pass the list on;
+#X text 62 7 This abstraction is used in patch 68.qlist.pd.;
+#X connect 0 0 17 0;
+#X connect 0 0 17 1;
+#X connect 1 0 19 0;
+#X connect 1 0 19 1;
+#X connect 2 0 23 1;
+#X connect 3 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 4 0;
+#X connect 5 0 25 0;
+#X connect 5 1 7 1;
+#X connect 6 0 5 0;
+#X connect 6 1 8 0;
+#X connect 7 0 1 0;
+#X connect 8 0 7 1;
+#X connect 9 0 12 0;
+#X connect 9 0 28 0;
+#X connect 9 1 0 1;
+#X connect 10 0 9 0;
+#X connect 11 0 2 0;
+#X connect 12 0 15 0;
+#X connect 13 0 14 0;
+#X connect 14 0 7 0;
+#X connect 15 0 16 0;
+#X connect 16 0 0 0;
+#X connect 17 0 18 0;
+#X connect 17 0 18 1;
+#X connect 18 0 11 0;
+#X connect 19 0 20 0;
+#X connect 19 0 20 1;
+#X connect 20 0 2 1;
+#X connect 21 0 23 0;
+#X connect 23 0 22 0;
+#X connect 24 0 26 0;
+#X connect 25 0 24 0;
+#X connect 27 0 29 0;
+#X connect 28 0 27 0;
diff --git a/pd/doc/3.audio.examples/partial.pd b/pd/doc/3.audio.examples/partial.pd
new file mode 100644
index 00000000..dae4a151
--- /dev/null
+++ b/pd/doc/3.audio.examples/partial.pd
@@ -0,0 +1,51 @@
+#N canvas 439 152 649 445 12;
+#X obj 495 257 sqrt;
+#X text 78 85 trigger;
+#X msg 82 143 bang;
+#X obj 81 211 * \$2;
+#X text 126 210 relative frequency;
+#X obj 83 340 *~;
+#X obj 262 307 line~;
+#X obj 262 335 *~;
+#X obj 260 364 *~;
+#X obj 264 229 float;
+#X msg 264 280 0 \$1;
+#X obj 494 280 sqrt;
+#X obj 82 110 r trigger;
+#X obj 495 232 float \$1;
+#X obj 294 206 r duration;
+#X obj 81 179 float;
+#X obj 126 180 r frequency;
+#X text 497 199 amplitude;
+#X obj 83 311 osc~ 0;
+#X obj 278 168 t b b;
+#X obj 81 238 + \$3;
+#X text 119 240 detune;
+#X obj 264 255 * \$4;
+#X text 306 256 relative duration;
+#X text 106 6 partial - arguments: amplitude \, frequency \, detune \, duration;
+#X obj 83 367 throw~ sum;
+#X text 74 35 This patch is used as an abstraction in 15.additive.pd;
+#X connect 0 0 11 0;
+#X connect 2 0 15 0;
+#X connect 2 0 19 0;
+#X connect 3 0 20 0;
+#X connect 5 0 25 0;
+#X connect 6 0 7 0;
+#X connect 6 0 7 1;
+#X connect 7 0 8 0;
+#X connect 7 0 8 1;
+#X connect 8 0 5 1;
+#X connect 9 0 22 0;
+#X connect 10 0 6 0;
+#X connect 11 0 6 0;
+#X connect 12 0 2 0;
+#X connect 13 0 0 0;
+#X connect 14 0 9 1;
+#X connect 15 0 3 0;
+#X connect 16 0 15 1;
+#X connect 18 0 5 0;
+#X connect 19 0 9 0;
+#X connect 19 1 13 0;
+#X connect 20 0 18 0;
+#X connect 22 0 10 0;
diff --git a/pd/doc/3.audio.examples/qlist.txt b/pd/doc/3.audio.examples/qlist.txt
new file mode 100644
index 00000000..fe105141
--- /dev/null
+++ b/pd/doc/3.audio.examples/qlist.txt
@@ -0,0 +1,56 @@
+# This is a qlist for patch number 60, which demonstrates an oscillator
+bank.
+;
+# comments start with a "#" which must be followed by a space. The comment
+is terminated by a semicolon like this: ;
+
+# first an arpeggio. You can group them in lines as you please. The 100s at
+the beginnings of lines are delay times. ;
+
+pit1 89; amp1 80;
+100 pit2 72; amp2 80;
+100 pit3 57; amp3 90;
+100 pit4 84; amp4 78;
+100 pit5 74; amp5 74;
+100 pit6 73; amp6 74;
+100 pit7 100; amp7 78;
+100 pit8 37; amp8 95;
+
+# after a 600-msec rest, gliss four of the oscillators to new frequencies. ;
+600 pit1 70 1000;
+300 pit8 40 1000;
+300 pit4 89 1000;
+300 pit7 95 1000;
+
+# a second later, turn them off with decay time 1500 ;
+1000
+amp1 0 1500;
+amp2 0 1500;
+amp3 0 1500;
+amp4 0 1500;
+amp5 0 1500;
+amp6 0 1500;
+amp7 0 1500;
+amp8 0 1500;
+
+# and re-attack them.. ;
+1000
+amp1 85 5;
+amp2 85 5;
+amp3 85 5;
+amp4 90 5;
+amp5 85 5;
+amp6 85 5;
+amp7 90 5;
+amp8 85 5;
+
+# this time, try varying decay times. ;
+10
+amp1 0 2000;
+amp2 0 2000;
+amp3 0 2000;
+amp4 0 500;
+amp5 0 1000;
+amp6 0 1000;
+amp7 0 500;
+amp8 0 4000;
diff --git a/pd/doc/3.audio.examples/qlist2.txt b/pd/doc/3.audio.examples/qlist2.txt
new file mode 100644
index 00000000..5c272646
--- /dev/null
+++ b/pd/doc/3.audio.examples/qlist2.txt
@@ -0,0 +1,5 @@
+note 36;
+1000 note 34;
+1000 note 33;
+1000 note 31;
+1000 qlist bang;
diff --git a/pd/doc/3.audio.examples/sampvoice.pd b/pd/doc/3.audio.examples/sampvoice.pd
new file mode 100644
index 00000000..dafa3c67
--- /dev/null
+++ b/pd/doc/3.audio.examples/sampvoice.pd
@@ -0,0 +1,105 @@
+#N canvas 231 67 574 676 12;
+#X obj 95 396 line~;
+#X obj 301 396 line~;
+#X obj 285 476 *~;
+#X obj 317 607 outlet~;
+#X obj 144 396 makefilename sample%d;
+#X msg 144 419 set \$1;
+#X obj 144 442 tabread4~ sample1;
+#X obj 349 419 dbtorms;
+#X obj 349 396 unpack;
+#X obj 349 442 sqrt;
+#X obj 349 465 sqrt;
+#X obj 349 488 line~;
+#X obj 333 559 *~;
+#X obj 349 511 *~;
+#X obj 349 534 *~;
+#X msg 201 49 bang;
+#X obj 201 72 delay 5;
+#X obj 289 95 unpack 0 0 0 0 0 0 0;
+#X obj 421 184 f;
+#X obj 367 161 f;
+#X obj 309 161 f;
+#X obj 278 161 f;
+#X obj 252 161 f;
+#X obj 173 161 f;
+#X obj 173 184 mtof;
+#X obj 173 207 / 261.62;
+#X obj 173 230 * 4.41e+08;
+#X obj 173 253 +;
+#X obj 394 161 delay;
+#X obj 252 303 pack 0 0 0 0 0;
+#X obj 201 95 t b b b;
+#X obj 309 207 + 1;
+#X obj 309 184 * 44.1;
+#X msg 85 337 0 5;
+#X msg 286 337 1 5;
+#X msg 319 337 0 \, \$1 \$2;
+#X msg 158 337 \$3 \, \$4 1e+07;
+#X msg 253 337 \$5;
+#X msg 393 337 0 \$1;
+#X obj 289 72 inlet;
+#X obj 61 565 inlet~;
+#X obj 317 583 +~;
+#X text 82 21 This is an abstraction used by a polyphonic sampler (example 24.);
+#X connect 0 0 6 0;
+#X connect 1 0 2 1;
+#X connect 2 0 12 0;
+#X connect 4 0 5 0;
+#X connect 5 0 6 0;
+#X connect 6 0 2 0;
+#X connect 7 0 9 0;
+#X connect 8 0 7 0;
+#X connect 8 1 11 1;
+#X connect 9 0 10 0;
+#X connect 10 0 11 0;
+#X connect 11 0 13 0;
+#X connect 11 0 13 1;
+#X connect 12 0 41 1;
+#X connect 13 0 14 0;
+#X connect 13 0 14 1;
+#X connect 14 0 12 1;
+#X connect 15 0 16 0;
+#X connect 15 0 33 0;
+#X connect 16 0 30 0;
+#X connect 17 0 23 1;
+#X connect 17 0 15 0;
+#X connect 17 1 22 1;
+#X connect 17 2 28 1;
+#X connect 17 3 21 1;
+#X connect 17 4 20 1;
+#X connect 17 5 19 1;
+#X connect 17 6 18 1;
+#X connect 18 0 38 0;
+#X connect 19 0 29 1;
+#X connect 20 0 32 0;
+#X connect 21 0 29 4;
+#X connect 22 0 29 0;
+#X connect 23 0 24 0;
+#X connect 24 0 25 0;
+#X connect 25 0 26 0;
+#X connect 26 0 27 0;
+#X connect 27 0 29 3;
+#X connect 28 0 18 0;
+#X connect 29 0 34 0;
+#X connect 29 0 35 0;
+#X connect 29 0 36 0;
+#X connect 29 0 37 0;
+#X connect 30 0 22 0;
+#X connect 30 1 23 0;
+#X connect 30 2 19 0;
+#X connect 30 2 20 0;
+#X connect 30 2 21 0;
+#X connect 30 2 28 0;
+#X connect 31 0 29 2;
+#X connect 31 0 27 1;
+#X connect 32 0 31 0;
+#X connect 33 0 1 0;
+#X connect 34 0 1 0;
+#X connect 35 0 8 0;
+#X connect 36 0 0 0;
+#X connect 37 0 4 0;
+#X connect 38 0 8 0;
+#X connect 39 0 17 0;
+#X connect 40 0 41 0;
+#X connect 41 0 3 0;
diff --git a/pd/doc/3.audio.examples/spectrum-partial.pd b/pd/doc/3.audio.examples/spectrum-partial.pd
new file mode 100644
index 00000000..584d3489
--- /dev/null
+++ b/pd/doc/3.audio.examples/spectrum-partial.pd
@@ -0,0 +1,57 @@
+#N canvas 211 116 826 530 12;
+#X obj 28 412 osc~;
+#X obj 94 197 r poll-table;
+#X obj 78 311 moses 0;
+#X obj 129 337 + 50;
+#X obj 129 363 dbtorms;
+#X msg 78 339 0;
+#X obj 78 392 pack 0 30;
+#X obj 78 422 line~;
+#X obj 28 471 throw~ sum-bus;
+#X obj 28 442 *~;
+#X obj 28 87 r pitch;
+#X obj 28 114 mtof;
+#X obj 78 230 f;
+#X obj 28 142 * \$1;
+#X obj 37 168 ftom;
+#X obj 79 256 -;
+#X obj 121 255 r whammybar;
+#X text 28 9 This abstraction is used by the spectrum drawing example
+\, number 16...;
+#X text 61 46 \$1 is the partial number.;
+#X text 79 114 pitch to frequency;
+#X text 78 141 then get the frequency of this specific partial;
+#X text 81 167 ... and then convert back to pitch.;
+#X text 115 230 ... at which time we get the pitch back...;
+#X text 233 249 ... and transpose \, effectively shifting the spectral
+envelope left and right.;
+#X text 203 341 The vertical scale is dB from 1 to 50 \, but we want
+true zero when the table value is 0 or less.;
+#X text 172 398 Amplitude control via pack \, line~ \, and *~.;
+#X text 171 444 Finally \, add to a summing bus via throw~. All the
+throw~s in the instantiations of this abstraction will add into the
+one "catch~ sum-bus" at the output.;
+#X text 216 195 the calling patch bangs "poll-table" every 30 msec.
+;
+#X obj 78 284 tabread4 spectrum-tab;
+#X text 285 288 Finally get the strength from the table. Note that
+we use the control object \, tabread4 \, not tabread4~.;
+#X connect 0 0 9 0;
+#X connect 1 0 12 0;
+#X connect 2 0 5 0;
+#X connect 2 1 3 0;
+#X connect 3 0 4 0;
+#X connect 4 0 6 0;
+#X connect 5 0 6 0;
+#X connect 6 0 7 0;
+#X connect 7 0 9 1;
+#X connect 9 0 8 0;
+#X connect 10 0 11 0;
+#X connect 11 0 13 0;
+#X connect 12 0 15 0;
+#X connect 13 0 14 0;
+#X connect 13 0 0 0;
+#X connect 14 0 12 1;
+#X connect 15 0 28 0;
+#X connect 16 0 15 1;
+#X connect 28 0 2 0;
diff --git a/pd/doc/4.fft.examples/00.INTRO.txt b/pd/doc/4.fft.examples/00.INTRO.txt
new file mode 100644
index 00000000..b5218793
--- /dev/null
+++ b/pd/doc/4.fft.examples/00.INTRO.txt
@@ -0,0 +1,65 @@
+These patches demonstrate how to use Pd's short-time Fourier transform objects,
+rfft~ and rifft~, to do a variety of things. The patches can be quite
+expensive; the phase vocoder, for instance, requires a 300MHz Pentium 2 to run
+at 44100 Hz. By default Pd runs at 44100 Hz, but you can specify a different
+sample rate on the command line, for instance:
+
+pd -r 16000 09.pvoc.pd
+
+On SGI, Pd will check whether your audio system is running at the correct
+sample rate and will print out a warning if not. In NT or W95, Pd's behavior
+will depend on your audio driver. In Linux Pd usually tries to set the rate
+of the conversion hardware.
+
+Included in this directory are:
+
+01.fftanalysis.pd -- does a windowed FFT analysis and resynthesis of a sine
+tone, showing how to specify block size and overlap.
+
+02.noisefft.pd -- Fourier analysis of white noise. This patch also shows how
+you can average power spectra over time.
+
+03.denoise.pd -- using the technique from the previous patch, finds the noise
+floor in a recorded sample and attempts to scrub it away.
+
+04.shifts.pd -- tests the leftshift and rightshift "externs" used in the next
+patch.
+
+05.sheepgoat.pd -- tries to discriminate between "pitched" and "unpitched"
+components of a sound.
+
+06.sheepgoat2.pd -- another attempt at the same thing.
+
+07.tinbell.pd -- a spectral flattener, which can make the sound of a large bell
+turn into the sound of a tamtam.
+
+08.convobros.pd -- spectral cross synthesis between two sounds
+
+09.pvoc.pd -- phase vocoder
+
+---------- after this point, the patches haven't been cleaned up -----------
+
+10.phaselockedvoc.pd -- phase locked vocoder; see Laroche&Dolson in ICMC97 for
+a discussion of something that works better than this.
+
+11.pianorev.pd -- an attempt at a phase-coherent reverberation algorithm to
+imitate the "piano reverb" obtained by putting a speaker under the sound board
+of a piano and picking up the sympathetic vibrations.
+
+12.sinedecomposer.pd -- estimate the frequencies and amplitudes of the
+components of a sound
+
+13.tracemaker.pd -- show how to use Pd to combine snapshots of a spectrum into
+continuous spectral traces.
+
+14.partialtracer.pd -- sinusoidal analysis/resynthesis of a time-verying sound.
+
+15.waveformgrab.pd -- bash a sample into phase-coherent windows and make a
+pitched resynthesis.
+
+
+
+
+
+
+
diff --git a/pd/doc/4.fft.examples/01.fftanalysis.pd b/pd/doc/4.fft.examples/01.fftanalysis.pd
new file mode 100644
index 00000000..9b5e1e4c
--- /dev/null
+++ b/pd/doc/4.fft.examples/01.fftanalysis.pd
@@ -0,0 +1,143 @@
+#N canvas 11 0 944 595 12;
+#X text 60 368 frequency;
+#X obj 20 238 tabwrite~ array2;
+#X msg 141 117 0;
+#N canvas 245 0 864 679 fft-analysis 0;
+#X obj 57 75 *~;
+#X obj 98 191 *~;
+#X graph graph1 0 -1 256 1 397 304 797 4;
+#X array array1 256 float 0;
+#X pop;
+#X graph graph2 0 -1 256 1 397 631 797 331;
+#X array array2 256 float 0;
+#X pop;
+#X obj 57 4 inlet~;
+#X obj 57 192 *~;
+#X obj 57 310 tabsend~ array1;
+#X msg 442 673 \; array1 resize 256 \; array2 resize 256;
+#X obj 76 48 tabreceive~ array2;
+#X obj 57 136 rfft~;
+#X obj 57 222 rifft~;
+#X obj 80 283 outlet~;
+#X obj 229 216 print~;
+#X msg 229 188 bang;
+#X obj 94 632 block~ 256 2;
+#X text 82 30 Hanning window;
+#X text 61 106 forward real FFT;
+#X text 74 243 inverse real FFT;
+#X text 121 126 renormalize by 1/256;
+#X obj 120 144 sig~ 0.0039;
+#X connect 0 0 9 0;
+#X connect 1 0 10 1;
+#X connect 4 0 0 0;
+#X connect 5 0 10 0;
+#X connect 8 0 0 1;
+#X connect 9 0 5 0;
+#X connect 9 0 12 0;
+#X connect 9 1 1 0;
+#X connect 10 0 11 0;
+#X connect 10 0 6 0;
+#X connect 13 0 12 0;
+#X connect 19 0 5 1;
+#X connect 19 0 1 1;
+#X restore 133 430 pd fft-analysis;
+#X floatatom 133 379 0 0 0;
+#X obj 134 525 dac~;
+#X graph graph1 0 -1 256 1 352 461 608 261;
+#X array array3 256 float 0;
+#X pop;
+#X obj 141 462 tabwrite~ array3;
+#X msg 77 442 bang;
+#X obj 133 324 * 44100;
+#X obj 133 352 / 256;
+#X floatatom 133 298 0 0 0;
+#X text 71 165 sample rate / 256;
+#X text 17 23 CLICK HERE TO;
+#X text 15 41 CALCULATE HANNING;
+#X text 14 58 WINDOW TABLE;
+#X msg 20 79 bang \; pd dsp 1;
+#X text 40 289 frequency;
+#X text 50 305 in bins;
+#X text 62 385 in Hz.;
+#X text 14 440 scope;
+#X text 236 23 A patch to test the rfft~ and rifft~ modules. An incoming
+sine wave is Hanning windowed and transformed. The inverse transform
+is then output.;
+#X text 235 70 The block size (256) and overlap (2) are set by the
+block~ object in the sub-patch. The output is automatically overlap-added
+by the outlet~ object.;
+#X floatatom 778 111 0 0 0;
+#N canvas 194 37 397 591 output 0;
+#X obj 72 249 t b;
+#X obj 72 173 f;
+#X obj 73 89 inlet;
+#X text 86 54 mute;
+#X obj 72 280 f;
+#X msg 149 292 0;
+#X msg 72 125 bang;
+#X obj 73 197 moses 1;
+#X obj 149 263 t b f;
+#X obj 109 543 outlet;
+#X msg 108 503 set \$1;
+#X obj 92 337 s master-amp;
+#X obj 206 153 r master-amp;
+#X obj 107 424 r master-amp;
+#X obj 206 186 moses 1;
+#X obj 249 494 dbtorms;
+#X obj 251 520 pack 0 100;
+#X obj 252 548 s master-out;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 11 0;
+#X connect 5 0 11 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 12 0 1 1;
+#X connect 12 0 14 0;
+#X connect 13 0 10 0;
+#X connect 13 0 15 0;
+#X connect 14 1 4 1;
+#X connect 15 0 16 0;
+#X connect 16 0 17 0;
+#X restore 778 83 pd output;
+#X msg 778 55 mute;
+#X text 821 115 AMPLITUDE;
+#X obj 778 142 s master-amp;
+#X obj 191 522 line~;
+#X obj 134 495 *~;
+#X text 236 124 You can close the sub-patch if table updates are interfering
+with your audio output.;
+#X obj 191 495 r master-out;
+#X obj 36 166 osc~;
+#X obj 35 118 samplerate~;
+#X obj 36 142 / 256;
+#X obj 36 189 *~ -0.5;
+#X obj 36 209 +~ 0.5;
+#X obj 133 405 osc~;
+#X connect 2 0 32 1;
+#X connect 3 0 7 0;
+#X connect 3 0 29 0;
+#X connect 4 0 37 0;
+#X connect 8 0 7 0;
+#X connect 9 0 10 0;
+#X connect 10 0 4 0;
+#X connect 11 0 9 0;
+#X connect 16 0 1 0;
+#X connect 16 0 2 0;
+#X connect 16 0 33 0;
+#X connect 23 0 27 0;
+#X connect 24 0 23 0;
+#X connect 25 0 24 0;
+#X connect 28 0 29 1;
+#X connect 29 0 5 0;
+#X connect 31 0 28 0;
+#X connect 32 0 35 0;
+#X connect 33 0 34 0;
+#X connect 34 0 32 0;
+#X connect 35 0 36 0;
+#X connect 36 0 1 0;
+#X connect 37 0 3 0;
diff --git a/pd/doc/4.fft.examples/02.noisefft.pd b/pd/doc/4.fft.examples/02.noisefft.pd
new file mode 100644
index 00000000..65b4d92f
--- /dev/null
+++ b/pd/doc/4.fft.examples/02.noisefft.pd
@@ -0,0 +1,267 @@
+#N canvas 24 0 884 580 12;
+#X obj 279 320 sig~;
+#X obj 187 375 *~;
+#X floatatom 279 274 0 0 0;
+#X floatatom 183 273 0 0 0;
+#X obj 183 319 sig~;
+#X text 10 246 frequency;
+#N canvas 45 63 925 609 fft-analysis 0;
+#X obj 241 228 + 1;
+#X msg 313 322 0;
+#X obj 127 190 *~;
+#X obj 38 69 *~;
+#X obj 37 38 *~;
+#X obj 72 436 *~;
+#X graph graph1 0 0 1024 1 476 244 876 44;
+#X array fftout 1024 float 0;
+#X pop;
+#X obj 37 4 inlet~;
+#X obj 37 436 *~;
+#X obj 94 271 tabsend~ fftout;
+#X obj 76 38 tabreceive~ hanning;
+#X obj 38 116 rfft~;
+#X obj 37 461 rifft~;
+#X obj 37 517 outlet~;
+#X text 88 21 Hanning window;
+#X text 31 94 forward real FFT;
+#X text 52 479 inverse real FFT;
+#X obj 36 555 block~ 1024 2;
+#X obj 72 69 sig~ 0.03125;
+#X graph graph1 0 0 1024 1 476 554 876 354;
+#X array mask 1024 float 0;
+#X pop;
+#X obj 94 190 *~;
+#X obj 94 214 +~;
+#X floatatom 241 253 0 0 0;
+#X msg 247 105 0;
+#X obj 175 184 float;
+#X obj 226 184 + 1;
+#X obj 175 123 bang~;
+#X obj 175 148 spigot;
+#X floatatom 303 86 0 0 0;
+#X obj 101 314 tabreceive~ mask;
+#X obj 241 277 t b b f;
+#X obj 266 322 /;
+#X msg 266 298 1;
+#X obj 78 344 -~;
+#X obj 266 358 sig~;
+#X obj 313 298 sel 0;
+#X obj 247 388 *~;
+#X obj 212 419 +~;
+#X obj 227 446 tabsend~ mask;
+#X floatatom 313 358 0 0 0;
+#X obj 175 209 t f f;
+#X text 100 231 power is square;
+#X text 100 245 modulus of FFT;
+#X obj 303 36 loadbang;
+#X obj 298 181 <;
+#X msg 253 36 bang;
+#X text 237 11 click to make an average;
+#X obj 91 408 sig~ 0.03125;
+#X text 574 249 power spectrum of this window;
+#X text 338 70 Set the number;
+#X text 340 85 of frames to;
+#X text 342 101 average;
+#X text 593 573 average power spectrum;
+#X text 272 253 current frame;
+#X text 284 378 weight of new;
+#X text 275 391 frame in moving;
+#X text 304 404 average;
+#X msg 303 61 100;
+#X connect 0 0 22 0;
+#X connect 1 0 34 0;
+#X connect 1 0 39 0;
+#X connect 2 0 21 1;
+#X connect 3 0 11 0;
+#X connect 4 0 3 0;
+#X connect 5 0 12 1;
+#X connect 7 0 4 0;
+#X connect 8 0 12 0;
+#X connect 10 0 4 1;
+#X connect 11 0 8 0;
+#X connect 11 0 20 0;
+#X connect 11 0 20 1;
+#X connect 11 1 5 0;
+#X connect 11 1 2 0;
+#X connect 11 1 2 1;
+#X connect 12 0 13 0;
+#X connect 18 0 3 1;
+#X connect 20 0 21 0;
+#X connect 21 0 9 0;
+#X connect 21 0 33 0;
+#X connect 22 0 30 0;
+#X connect 23 0 24 1;
+#X connect 23 0 44 0;
+#X connect 24 0 40 0;
+#X connect 24 0 25 0;
+#X connect 25 0 24 1;
+#X connect 26 0 27 0;
+#X connect 27 0 24 0;
+#X connect 28 0 44 1;
+#X connect 29 0 33 1;
+#X connect 29 0 37 1;
+#X connect 30 1 32 0;
+#X connect 30 2 31 1;
+#X connect 31 0 34 0;
+#X connect 31 0 39 0;
+#X connect 32 0 31 0;
+#X connect 33 0 36 0;
+#X connect 34 0 36 1;
+#X connect 35 0 1 0;
+#X connect 36 0 37 0;
+#X connect 37 0 38 0;
+#X connect 40 0 44 0;
+#X connect 40 1 0 0;
+#X connect 43 0 57 0;
+#X connect 44 0 27 1;
+#X connect 44 0 35 0;
+#X connect 45 0 23 0;
+#X connect 47 0 8 1;
+#X connect 47 0 5 1;
+#X connect 57 0 28 0;
+#X restore 99 412 pd fft-analysis;
+#X obj 99 275 sig~;
+#X obj 99 297 phasor~;
+#X obj 99 319 cos~;
+#X floatatom 99 253 0 0 0;
+#X graph graph1 0 -1 1024 1 431 525 687 325;
+#X array scope 1024 float 0;
+#X pop;
+#X obj 105 439 tabwrite~ scope;
+#X msg 49 418 bang;
+#X obj 99 230 / 256;
+#X floatatom 98 165 0 0 0;
+#X text 10 155 frequency;
+#X text 21 175 in bins;
+#X text 16 259 in Hz.;
+#X text 42 398 scope;
+#N canvas 84 23 767 580 hanning-window 0;
+#X obj 92 198 phasor~;
+#X obj 92 234 cos~;
+#X obj 23 328 tabwrite~ hanning;
+#X obj 30 252 -~;
+#X obj 28 218 sig~ 1;
+#X msg 37 171 0;
+#X text 48 59 CALCULATE HANNING;
+#X text 45 71 WINDOW TABLE;
+#X graph graph1 0 -1 1024 1 290 509 690 209;
+#X array hanning 1024 float 0;
+#X pop;
+#X msg 290 521 \; hanning resize 1024;
+#X obj 114 145 / 1024;
+#X obj 114 168 sig~;
+#X text 175 148 sample rate / window size;
+#X msg 23 94 bang;
+#X obj 66 269 sig~ 0.5;
+#X obj 49 300 *~;
+#X obj 22 38 loadbang;
+#X obj 113 117 samplerate~;
+#X connect 0 0 1 0;
+#X connect 1 0 3 1;
+#X connect 3 0 15 0;
+#X connect 4 0 3 0;
+#X connect 5 0 0 1;
+#X connect 10 0 11 0;
+#X connect 11 0 0 0;
+#X connect 13 0 2 0;
+#X connect 13 0 5 0;
+#X connect 13 0 17 0;
+#X connect 14 0 15 1;
+#X connect 15 0 2 0;
+#X connect 16 0 13 0;
+#X connect 17 0 10 0;
+#X restore 328 203 pd hanning-window;
+#X obj 99 346 *~;
+#X text 177 250 oscillator;
+#X obj 166 349 noise~;
+#X text 278 249 noise;
+#X obj 99 374 +~;
+#X text 214 232 amplitudes;
+#X msg 26 84 \; pd dsp 1;
+#X obj 98 207 *;
+#X obj 135 181 loadbang;
+#X obj 135 205 samplerate~;
+#X text 22 51 CLICK HERE;
+#X floatatom 673 67 0 0 0;
+#N canvas 194 37 397 591 output 0;
+#X obj 61 212 t b;
+#X obj 61 147 f;
+#X obj 62 76 inlet;
+#X text 73 46 mute;
+#X obj 61 238 f;
+#X msg 127 248 0;
+#X msg 61 106 bang;
+#X obj 62 167 moses 1;
+#X obj 127 224 t b f;
+#X obj 93 462 outlet;
+#X msg 92 428 set \$1;
+#X obj 78 286 s master-amp;
+#X obj 175 130 r master-amp;
+#X obj 91 360 r master-amp;
+#X obj 175 158 moses 1;
+#X obj 212 420 dbtorms;
+#X obj 213 442 pack 0 100;
+#X obj 214 466 s master-out;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 11 0;
+#X connect 5 0 11 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 12 0 1 1;
+#X connect 12 0 14 0;
+#X connect 13 0 10 0;
+#X connect 13 0 15 0;
+#X connect 14 1 4 1;
+#X connect 15 0 16 0;
+#X connect 16 0 17 0;
+#X restore 673 43 pd output;
+#X msg 673 19 mute;
+#X text 710 70 AMPLITUDE;
+#X obj 673 93 s master-amp;
+#X text 241 9 NOISE FLOOR MEASUREMENT;
+#X text 149 31 This patch shows how you can average the RMS spectra
+of many windows of an incoming "noise floor" signal to estimate its
+spectrum. We'll use this in the "denoiser" patch \, next.;
+#X text 30 65 TO START;
+#X obj 99 515 dac~;
+#X obj 132 467 r master-out;
+#X text 148 100 The controls below let you send a mixture of a sine
+wave and white noise. The analysis is done in a subwindow.;
+#X obj 132 490 line~;
+#X obj 99 490 *~;
+#X obj 183 295 dbtorms;
+#X obj 279 297 dbtorms;
+#X connect 0 0 1 1;
+#X connect 1 0 25 1;
+#X connect 2 0 46 0;
+#X connect 3 0 45 0;
+#X connect 4 0 21 1;
+#X connect 6 0 12 0;
+#X connect 6 0 44 0;
+#X connect 7 0 8 0;
+#X connect 8 0 9 0;
+#X connect 9 0 21 0;
+#X connect 10 0 7 0;
+#X connect 13 0 12 0;
+#X connect 14 0 10 0;
+#X connect 15 0 28 0;
+#X connect 21 0 25 0;
+#X connect 23 0 1 0;
+#X connect 25 0 6 0;
+#X connect 28 0 14 0;
+#X connect 29 0 30 0;
+#X connect 30 0 28 1;
+#X connect 32 0 36 0;
+#X connect 33 0 32 0;
+#X connect 34 0 33 0;
+#X connect 41 0 43 0;
+#X connect 43 0 44 1;
+#X connect 44 0 40 0;
+#X connect 44 0 40 1;
+#X connect 45 0 4 0;
+#X connect 46 0 0 0;
diff --git a/pd/doc/4.fft.examples/03.denoise.pd b/pd/doc/4.fft.examples/03.denoise.pd
new file mode 100644
index 00000000..09e1244e
--- /dev/null
+++ b/pd/doc/4.fft.examples/03.denoise.pd
@@ -0,0 +1,364 @@
+#N canvas 54 36 882 556 12;
+#X floatatom 306 344;
+#X floatatom 68 337;
+#N canvas 72 0 896 679 fft-analysis 0;
+#X obj 140 447 *~;
+#X obj 54 705 *~;
+#X obj 139 612 *~;
+#X floatatom 206 267;
+#X obj 185 349 *~;
+#X obj 356 188 + 1;
+#X msg 447 257 0;
+#X obj 173 181 *~;
+#X obj 57 89 *~;
+#X obj 95 641 *~;
+#X obj 57 49 inlet~;
+#X obj 54 642 *~;
+#X obj 126 68 tabreceive~ hanning;
+#X obj 57 136 rfft~;
+#X obj 54 672 rifft~;
+#X obj 55 743 outlet~;
+#X text 132 50 Hanning window;
+#X text 63 115 forward real FFT;
+#X text 72 721 inverse real FFT;
+#X graph graph1 0 0 1024 20 491 631 891 331;
+#X array mask 1024 float;
+#X pop;
+#X obj 134 180 *~;
+#X obj 134 209 +~;
+#X msg 447 132 0;
+#X obj 317 129 float;
+#X obj 377 149 + 1;
+#X obj 318 72 bang~;
+#X obj 318 101 spigot;
+#X obj 449 186 < 10;
+#X floatatom 497 185;
+#X obj 256 306 tabreceive~ mask;
+#X obj 357 267 /;
+#X msg 357 237 1;
+#X obj 230 350 -~;
+#X obj 370 331 sig~;
+#X obj 447 232 sel 0;
+#X obj 348 356 *~;
+#X obj 331 387 +~;
+#X obj 330 412 tabsend~ mask;
+#X floatatom 448 287;
+#X obj 316 156 t f f;
+#X obj 446 56 r make-mask;
+#X obj 206 296 sig~;
+#X obj 206 241 r mask-level;
+#X obj 140 414 /~;
+#X obj 356 212 t b f;
+#X obj 177 612 sig~ 0.001;
+#X obj 29 781 block~ 1024 4;
+#X obj 663 76 r window-size;
+#X obj 447 84 t b f;
+#X obj 676 137 t b f;
+#X obj 663 171 /;
+#X obj 661 194 * 250;
+#X text 728 202 hop size in msec;
+#X obj 497 107 /;
+#X text 497 206 number of;
+#X text 505 221 frames;
+#X text 439 12 calculate a mask using N msec of;
+#X text 438 30 background noise;
+#X obj 176 415 sig~ 1e-20;
+#X text 152 467 multiply the signal by the;
+#X text 153 485 quantity s/(s+m) where "s";
+#X text 152 505 is signal power and "m" is mask.;
+#X text 153 527 The multiplier is close to 1 if;
+#X text 155 549 s>>m \, but close to 0 if s<<m.;
+#X text 157 572 We gratuitously square it.;
+#X floatatom 657 237;
+#X obj 699 109 r srate;
+#X connect 0 0 2 0;
+#X connect 1 0 15 0;
+#X connect 2 0 11 1;
+#X connect 2 0 9 1;
+#X connect 3 0 41 0;
+#X connect 4 0 43 1;
+#X connect 5 0 44 0;
+#X connect 6 0 38 0;
+#X connect 7 0 21 1;
+#X connect 8 0 13 0;
+#X connect 9 0 14 1;
+#X connect 10 0 8 0;
+#X connect 11 0 14 0;
+#X connect 12 0 8 1;
+#X connect 12 0 1 1;
+#X connect 13 0 11 0;
+#X connect 13 0 20 0;
+#X connect 13 0 20 1;
+#X connect 13 1 9 0;
+#X connect 13 1 7 0;
+#X connect 13 1 7 1;
+#X connect 14 0 1 0;
+#X connect 20 0 21 0;
+#X connect 21 0 32 0;
+#X connect 21 0 43 0;
+#X connect 21 0 43 1;
+#X connect 22 0 23 1;
+#X connect 22 0 27 0;
+#X connect 23 0 39 0;
+#X connect 23 0 24 0;
+#X connect 24 0 23 1;
+#X connect 25 0 26 0;
+#X connect 26 0 23 0;
+#X connect 27 0 26 1;
+#X connect 27 0 34 0;
+#X connect 28 0 27 1;
+#X connect 29 0 32 1;
+#X connect 29 0 4 0;
+#X connect 29 0 36 0;
+#X connect 30 0 38 0;
+#X connect 31 0 30 0;
+#X connect 32 0 35 0;
+#X connect 33 0 35 1;
+#X connect 34 0 6 0;
+#X connect 35 0 36 1;
+#X connect 36 0 37 0;
+#X connect 38 0 33 0;
+#X connect 39 0 27 0;
+#X connect 39 1 5 0;
+#X connect 40 0 48 0;
+#X connect 41 0 4 1;
+#X connect 42 0 3 0;
+#X connect 43 0 0 0;
+#X connect 43 0 0 1;
+#X connect 44 0 31 0;
+#X connect 44 1 30 1;
+#X connect 45 0 2 1;
+#X connect 47 0 50 0;
+#X connect 48 0 22 0;
+#X connect 48 1 53 0;
+#X connect 49 0 50 0;
+#X connect 49 1 50 1;
+#X connect 50 0 51 0;
+#X connect 51 0 53 1;
+#X connect 51 0 65 0;
+#X connect 53 0 28 0;
+#X connect 58 0 43 1;
+#X connect 66 0 49 0;
+#X restore 67 412 pd fft-analysis;
+#X obj 67 470 dac~;
+#N canvas 99 31 767 592 hanning-window 0;
+#X obj 108 242 phasor~;
+#X obj 108 275 cos~;
+#X obj 27 386 tabwrite~ hanning;
+#X obj 35 297 -~;
+#X obj 33 257 sig~ 1;
+#X msg 44 212 0;
+#X text 166 16 CALCULATE HANNING;
+#X text 166 32 WINDOW TABLE;
+#X graph graph1 0 -1 1024 1 308 538 708 238;
+#X array hanning 1024 float;
+#X pop;
+#X msg 308 550 \; hanning resize 1024;
+#X obj 109 154 / 1024;
+#X obj 109 201 sig~;
+#X text 206 174 sample rate / window size;
+#X msg 27 169 bang;
+#X obj 78 316 sig~ 0.5;
+#X obj 58 353 *~;
+#X obj 79 113 samplerate~;
+#X obj 29 27 r window-size;
+#X obj 29 62 t b f;
+#X connect 0 0 1 0;
+#X connect 1 0 3 1;
+#X connect 3 0 15 0;
+#X connect 4 0 3 0;
+#X connect 5 0 0 1;
+#X connect 10 0 11 0;
+#X connect 11 0 0 0;
+#X connect 13 0 2 0;
+#X connect 13 0 5 0;
+#X connect 14 0 15 1;
+#X connect 15 0 2 0;
+#X connect 16 0 10 0;
+#X connect 17 0 18 0;
+#X connect 18 0 16 0;
+#X connect 18 0 13 0;
+#X connect 18 1 10 1;
+#X restore 459 435 pd hanning-window;
+#X text 157 311 noise;
+#X text 91 289 amplitudes;
+#X obj 476 467 loadbang;
+#N canvas 132 255 634 335 insample 0;
+#X graph graph1 0 -1 155947 1 200 170 600 20;
+#X array sample 155948 float;
+#X pop;
+#X obj 21 78 r read-sample;
+#X obj 21 106 unpack s f;
+#X obj 59 134 s insamprate;
+#X obj 21 190 soundfiler;
+#X msg 21 163 read -resize \$1 sample;
+#X obj 21 223 s insamplength;
+#X msg 397 219 \; sample resize 220500 \; insamplength 220500;
+#X connect 1 0 2 0;
+#X connect 2 0 5 0;
+#X connect 2 1 3 0;
+#X connect 4 0 6 0;
+#X connect 5 0 4 0;
+#X restore 459 407 pd insample;
+#X obj 306 369 s mask-level;
+#X obj 119 474 line~;
+#X obj 67 442 *~;
+#X obj 476 495 samplerate~;
+#X obj 476 523 s srate;
+#X floatatom 164 338;
+#X obj 676 312 hip~ 5;
+#X obj 676 284 adc~ 1;
+#X msg 749 248 bang;
+#X obj 782 284 r srate;
+#X obj 749 284 f;
+#X obj 749 312 s insamprate;
+#X msg 24 209 \; window-size 1024 \; pd dsp 1;
+#X obj 676 340 tabwrite~ sample;
+#X msg 451 343 \; play-sample 0 5000;
+#X floatatom 451 277;
+#X floatatom 691 80;
+#N canvas 194 37 397 591 output 0;
+#X obj 95 230 t b;
+#X obj 95 174 f;
+#X obj 95 118 inlet;
+#X text 101 94 mute;
+#X obj 95 258 f;
+#X msg 154 284 0;
+#X msg 95 146 bang;
+#X obj 95 202 moses 1;
+#X obj 154 256 t b f;
+#X obj 107 490 outlet;
+#X msg 107 462 set \$1;
+#X obj 228 182 moses 1;
+#X obj 249 493 dbtorms;
+#X obj 249 521 pack 0 100;
+#X obj 228 154 r master-lvl;
+#X obj 107 424 r master-lvl;
+#X obj 95 315 s master-lvl;
+#X obj 249 549 s master-out;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X restore 691 52 pd output;
+#X msg 691 24 mute;
+#X obj 119 446 r master-out;
+#X text 734 71 MASTER;
+#X text 733 85 LEVEL;
+#X obj 691 108 s master-lvl;
+#X msg 305 314 6;
+#X text 306 291 on;
+#X text 351 292 off;
+#X text 23 189 click here first;
+#X text 301 189 read a sample;
+#X text 670 221 record your own sample;
+#X text 305 270 masking;
+#X text 481 322 play sample back;
+#X text 290 5 DENOISER;
+#X text 74 22 This patch attempts to scrub the noise floor from a sample in two steps. First using the "make-mask" message (which is caught in the "fft-analysis" window) \, you estimate the background spectrum. Do this while only the background noise is playing.;
+#X msg 350 315 0;
+#X obj 451 301 metro 2000;
+#X msg 201 213 \; read-sample ../sound/bell.aiff 44100;
+#N canvas 190 43 534 552 test-signal 0;
+#X obj 149 326 tabread4~ sample;
+#X obj 149 298 line~;
+#X obj 106 162 f;
+#X obj 204 137 r insamprate;
+#X obj 356 422 *~;
+#X obj 151 389 *~;
+#X obj 359 290 noise~;
+#X obj 151 441 +~;
+#X obj 182 390 dbtorms;
+#X obj 384 422 dbtorms;
+#X obj 182 365 inlet;
+#X obj 384 398 inlet;
+#X obj 359 375 *~;
+#X obj 359 315 bp~ 2000 7;
+#X obj 151 499 outlet~;
+#X obj 122 109 r insamplength;
+#X msg 149 274 0 \, \$1 \$2;
+#X obj 149 246 pack 0 0;
+#X obj 200 221 /;
+#X obj 204 163 * 0.001;
+#X obj 200 190 t b f;
+#X text 347 260 nasty non-flat noise;
+#X obj 151 469 hip~ 5;
+#X obj 100 8 loadbang;
+#X text 269 15 sample playback;
+#X msg 100 30 1;
+#X obj 99 74 metro 1000;
+#X floatatom 99 51;
+#X obj 385 349 phasor~ 3000;
+#X connect 0 0 5 0;
+#X connect 1 0 0 0;
+#X connect 2 0 17 0;
+#X connect 2 0 18 0;
+#X connect 3 0 19 0;
+#X connect 4 0 7 1;
+#X connect 5 0 7 0;
+#X connect 6 0 13 0;
+#X connect 7 0 22 0;
+#X connect 8 0 5 1;
+#X connect 9 0 4 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 12 0 4 0;
+#X connect 13 0 12 0;
+#X connect 15 0 2 1;
+#X connect 16 0 1 0;
+#X connect 17 0 16 0;
+#X connect 18 0 17 1;
+#X connect 18 0 26 1;
+#X connect 19 0 20 0;
+#X connect 20 0 18 0;
+#X connect 20 1 18 1;
+#X connect 22 0 14 0;
+#X connect 23 0 25 0;
+#X connect 25 0 27 0;
+#X connect 26 0 2 0;
+#X connect 27 0 26 0;
+#X connect 28 0 12 1;
+#X restore 70 375 pd test-signal;
+#X text 56 310 sampler;
+#X msg 507 213 \; make-mask 1000;
+#X msg 564 492 \; window-size 1024;
+#X text 488 186 calculate noise mask;
+#X text 74 98 Then turn both the sample and the noise on together \, set the "mask-level" to 6 or so and enjoy the result. Alternatively \, you could try to mask the bell out of the noise...;
+#X connect 0 0 9 0;
+#X connect 1 0 45 0;
+#X connect 2 0 11 0;
+#X connect 7 0 12 0;
+#X connect 7 0 48 0;
+#X connect 10 0 11 1;
+#X connect 11 0 3 0;
+#X connect 11 0 3 1;
+#X connect 12 0 13 0;
+#X connect 14 0 45 1;
+#X connect 15 0 22 0;
+#X connect 16 0 15 0;
+#X connect 17 0 19 0;
+#X connect 17 0 22 0;
+#X connect 18 0 19 1;
+#X connect 19 0 20 0;
+#X connect 24 0 43 0;
+#X connect 25 0 31 0;
+#X connect 26 0 25 0;
+#X connect 27 0 26 0;
+#X connect 28 0 10 0;
+#X connect 32 0 0 0;
+#X connect 42 0 0 0;
+#X connect 43 0 23 0;
+#X connect 45 0 2 0;
diff --git a/pd/doc/4.fft.examples/04.shifts.pd b/pd/doc/4.fft.examples/04.shifts.pd
new file mode 100644
index 00000000..9a03efbf
--- /dev/null
+++ b/pd/doc/4.fft.examples/04.shifts.pd
@@ -0,0 +1,27 @@
+#N canvas 213 9 614 383 12;
+#X msg 225 315 bang;
+#X obj 201 341 print~;
+#X graph graph1 0 -1 63 1 367 335 567 185;
+#X array array1 64 float;
+#X pop;
+#X msg 155 317 bang;
+#X obj 45 206 tabreceive~ array1;
+#X obj 131 343 print~;
+#X text 48 110 click here first;
+#X msg 74 314 bang;
+#X obj 50 340 print~;
+#X text 93 23 This is a test of the "lrshift~" object \, which can be used for calculations that compare neighboring bins of an FFT. The "tabreceive~" picks up the contents of "array1' (consisting of 0 \, 1 \, 0 \, 0 \, ...) and the three print~ objects show the input and the two outputs.;
+#X text 155 224 shift left;
+#X text 182 260 shift right;
+#X obj 128 240 ../../extra/lrshift~ 1;
+#X obj 149 276 ../../extra/lrshift~ -1;
+#X msg 49 131 \; pd dsp 1 \; array1 1 1;
+#X text 223 5 SAMPLE SHIFTS;
+#X connect 0 0 1 0;
+#X connect 3 0 5 0;
+#X connect 4 0 8 0;
+#X connect 4 0 12 0;
+#X connect 4 0 13 0;
+#X connect 7 0 8 0;
+#X connect 12 0 5 0;
+#X connect 13 0 1 0;
diff --git a/pd/doc/4.fft.examples/05.sheepgoat.pd b/pd/doc/4.fft.examples/05.sheepgoat.pd
new file mode 100644
index 00000000..255b2a14
--- /dev/null
+++ b/pd/doc/4.fft.examples/05.sheepgoat.pd
@@ -0,0 +1,395 @@
+#N canvas 69 23 831 481 12;
+#X floatatom 189 254;
+#N canvas 31 3 710 622 fft-analysis 0;
+#X obj 58 523 *~;
+#X obj 306 397 +~;
+#X obj 264 397 +~;
+#X obj 232 427 *~;
+#X obj 197 427 *~;
+#X obj 197 452 +~;
+#X obj 197 477 sqrt~;
+#X obj 528 342 *~;
+#X obj 493 342 *~;
+#X obj 493 367 +~;
+#X obj 399 256 *~;
+#X obj 269 147 *~;
+#X obj 234 147 *~;
+#X obj 199 147 *~;
+#X obj 163 147 *~;
+#X obj 163 172 +~;
+#X obj 21 573 *~;
+#X obj 368 141 *~;
+#X obj 80 80 *~;
+#X obj 71 289 *~;
+#X obj 99 56 inlet~;
+#X obj 6 289 *~;
+#X obj 28 32 tabreceive~ hanning;
+#X obj 80 105 rfft~;
+#X obj 21 548 rifft~;
+#X obj 21 597 outlet~;
+#X text 34 16 Hanning window;
+#X text 106 88 forward real FFT;
+#X obj 333 141 *~;
+#X obj 333 167 +~;
+#X obj 43 263 sig~ 0.001;
+#X obj 601 576 block~ 2048 4;
+#X obj 271 51 tabreceive~ last-real;
+#X text 368 23 previous analysis;
+#X obj 449 51 tabreceive~ last-imag;
+#X obj 333 192 rsqrt~;
+#X text 388 192 inverse modulus;
+#X obj 234 172 -~;
+#X obj 347 256 *~;
+#X text 435 254 encoded as a single complex number;
+#X text 435 229 amplitude and phase change;
+#X obj 165 240 *~;
+#X obj 128 289 tabsend~ last-real;
+#X obj 165 266 tabsend~ last-imag;
+#X text 191 240 sic;
+#X text 543 393 modulus;
+#X obj 493 448 +~;
+#X text 522 447 sum of moduli;
+#X obj 493 393 sqrt~;
+#X text 244 472 modulus of sum;
+#X obj 197 503 /~;
+#X obj 197 529 -~;
+#X obj 197 554 *~;
+#X obj 197 579 clip~ 0 1;
+#X obj 83 321 r threshold;
+#X obj 83 371 t b f;
+#X obj 83 422 -;
+#X msg 83 397 1;
+#X obj 83 472 sig~;
+#X obj 10 523 *~;
+#X obj 251 509 sig~ 1.5e-20;
+#X obj 350 512 r flip;
+#X obj 350 538 sel 0;
+#X msg 350 563 1e+20;
+#X msg 395 563 -1e+20;
+#X floatatom 83 447;
+#X obj 83 346 / 100;
+#X obj 136 373 lrshift~ 1;
+#X obj 210 373 lrshift~ -1;
+#X obj 331 373 lrshift~ 1;
+#X obj 405 373 lrshift~ -1;
+#X obj 510 419 lrshift~ 1;
+#X obj 593 422 lrshift~ -1;
+#X connect 0 0 24 1;
+#X connect 1 0 3 0;
+#X connect 1 0 3 1;
+#X connect 2 0 4 0;
+#X connect 2 0 4 1;
+#X connect 3 0 5 1;
+#X connect 4 0 5 0;
+#X connect 5 0 6 0;
+#X connect 6 0 50 0;
+#X connect 7 0 9 1;
+#X connect 8 0 9 0;
+#X connect 9 0 48 0;
+#X connect 10 0 7 1;
+#X connect 10 0 1 0;
+#X connect 10 0 7 0;
+#X connect 10 0 69 0;
+#X connect 10 0 70 0;
+#X connect 11 0 37 1;
+#X connect 12 0 37 0;
+#X connect 13 0 15 1;
+#X connect 14 0 15 0;
+#X connect 15 0 38 0;
+#X connect 16 0 25 0;
+#X connect 17 0 29 1;
+#X connect 18 0 23 0;
+#X connect 19 0 0 0;
+#X connect 20 0 18 1;
+#X connect 21 0 59 0;
+#X connect 22 0 16 1;
+#X connect 22 0 18 0;
+#X connect 23 0 12 0;
+#X connect 23 0 14 0;
+#X connect 23 0 42 0;
+#X connect 23 0 21 0;
+#X connect 23 1 13 0;
+#X connect 23 1 11 0;
+#X connect 23 1 43 0;
+#X connect 23 1 19 0;
+#X connect 24 0 16 0;
+#X connect 28 0 29 0;
+#X connect 29 0 35 0;
+#X connect 30 0 21 1;
+#X connect 30 0 19 1;
+#X connect 32 0 14 1;
+#X connect 32 0 11 1;
+#X connect 32 0 28 0;
+#X connect 32 0 28 1;
+#X connect 34 0 13 1;
+#X connect 34 0 12 1;
+#X connect 34 0 17 0;
+#X connect 34 0 17 1;
+#X connect 35 0 10 1;
+#X connect 35 0 38 1;
+#X connect 35 0 41 0;
+#X connect 37 0 10 0;
+#X connect 38 0 8 0;
+#X connect 38 0 8 1;
+#X connect 38 0 2 1;
+#X connect 38 0 67 0;
+#X connect 38 0 68 0;
+#X connect 41 0 43 0;
+#X connect 41 0 42 0;
+#X connect 46 0 50 1;
+#X connect 48 0 46 0;
+#X connect 48 0 71 0;
+#X connect 48 0 72 0;
+#X connect 50 0 51 0;
+#X connect 51 0 52 0;
+#X connect 52 0 53 0;
+#X connect 53 0 0 1;
+#X connect 53 0 59 1;
+#X connect 54 0 66 0;
+#X connect 55 0 57 0;
+#X connect 55 1 56 1;
+#X connect 56 0 65 0;
+#X connect 57 0 56 0;
+#X connect 58 0 51 1;
+#X connect 59 0 24 0;
+#X connect 60 0 50 1;
+#X connect 61 0 62 0;
+#X connect 62 0 63 0;
+#X connect 62 1 64 0;
+#X connect 63 0 52 1;
+#X connect 64 0 52 1;
+#X connect 65 0 58 0;
+#X connect 66 0 55 0;
+#X connect 67 0 2 0;
+#X connect 68 0 2 0;
+#X connect 69 0 1 1;
+#X connect 70 0 1 1;
+#X connect 71 0 46 1;
+#X connect 72 0 46 1;
+#X restore 106 400 pd fft-analysis;
+#X obj 106 468 dac~;
+#X obj 106 443 *~;
+#N canvas 211 72 625 525 previous-analysis 0;
+#X graph graph1 0 -1 2048 1 310 246 510 96;
+#X array last-real 2048 float;
+#X pop;
+#X graph graph2 0 -1 2048 1 309 433 509 283;
+#X array last-imag 2048 float;
+#X pop;
+#X msg 67 202 \; last-real resize 2048 \; last-imag resize 2048;
+#X restore 649 457 pd previous-analysis;
+#X obj 189 279 s threshold;
+#X obj 149 468 line~;
+#X floatatom 622 72;
+#N canvas 194 37 397 591 output 0;
+#X obj 66 202 t b;
+#X obj 66 152 f;
+#X obj 66 102 inlet;
+#X text 71 80 mute;
+#X obj 66 228 f;
+#X msg 134 244 0;
+#X msg 66 127 bang;
+#X obj 66 177 moses 1;
+#X obj 134 219 t b f;
+#X obj 96 441 outlet;
+#X msg 96 416 set \$1;
+#X obj 185 163 moses 1;
+#X obj 224 444 dbtorms;
+#X obj 224 469 pack 0 100;
+#X obj 185 138 r master-lvl;
+#X obj 96 382 r master-lvl;
+#X obj 83 285 s master-lvl;
+#X obj 224 494 s master-amp;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X restore 622 47 pd output;
+#X msg 622 22 mute;
+#X text 661 64 MASTER;
+#X text 660 78 LEVEL;
+#X obj 622 98 s master-lvl;
+#X obj 149 443 r master-amp;
+#X obj 571 310 loadbang;
+#X obj 571 336 samplerate~;
+#X text 27 215 click here first;
+#X obj 470 364 hip~ 5;
+#X obj 470 338 adc~ 1;
+#X msg 535 306 bang;
+#X obj 535 338 f;
+#X obj 535 364 s insamprate;
+#X obj 470 389 tabwrite~ sample;
+#X text 476 279 record your own sample;
+#N canvas 132 61 699 413 hanning-window 0;
+#X obj 97 218 phasor~;
+#X obj 97 247 cos~;
+#X obj 24 347 tabwrite~ hanning;
+#X obj 31 267 -~;
+#X obj 30 231 sig~ 1;
+#X msg 40 191 0;
+#X text 149 14 CALCULATE HANNING;
+#X text 149 29 WINDOW TABLE;
+#X graph graph1 0 -1 2047 1 454 213 654 63;
+#X array hanning 2048 float;
+#X pop;
+#X obj 98 139 / 1024;
+#X obj 98 181 sig~;
+#X text 185 156 sample rate / window size;
+#X msg 24 152 bang;
+#X obj 70 284 sig~ 0.5;
+#X obj 52 318 *~;
+#X obj 71 102 samplerate~;
+#X obj 26 24 r window-size;
+#X obj 26 56 t b f;
+#X msg 468 265 \; hanning resize 2048;
+#X connect 0 0 1 0;
+#X connect 1 0 3 1;
+#X connect 3 0 14 0;
+#X connect 4 0 3 0;
+#X connect 5 0 0 1;
+#X connect 9 0 10 0;
+#X connect 10 0 0 0;
+#X connect 12 0 2 0;
+#X connect 12 0 5 0;
+#X connect 13 0 14 1;
+#X connect 14 0 2 0;
+#X connect 15 0 9 0;
+#X connect 16 0 17 0;
+#X connect 17 0 15 0;
+#X connect 17 0 12 0;
+#X connect 17 1 9 1;
+#X restore 649 432 pd hanning-window;
+#X text 190 0 PITCHED/UNPITCHED SEPARATION;
+#X text 99 18 This is the first of two attempts to separate "pitched" sound from noisy sound. We perform the usual windowed FFT \, and estimate the frequency of the signal in each bin \, If a bin's frequency agrees with those of its two neighbors \, we conclude that that bin belongs to a "pitched" sound.;
+#X floatatom 320 258;
+#X obj 320 283 s flip;
+#X msg 320 230 0;
+#X text 324 192 select:;
+#X text 293 211 pitched;
+#X text 365 211 noisy;
+#X msg 365 231 1;
+#X text 182 204 threshold;
+#X text 186 217 (range;
+#X text 190 229 0-100);
+#X text 97 90 A low threshold means to let only very coherent signals through. A high one permits more of the signal to be considered "pitched". You can select either to hear the pitched or the noisy part.;
+#X text 97 147 You might agree with me that the subsequent patch works better than this one...;
+#X msg 22 238 \; window-size 2048 \; flip 0 \; pd dsp 1;
+#X floatatom 104 339;
+#X text 184 316 noise;
+#X text 125 296 amplitudes;
+#X floatatom 191 340;
+#N canvas 190 43 534 552 test-signal 0;
+#X obj 134 293 tabread4~ sample;
+#X obj 134 268 line~;
+#X obj 95 146 f;
+#X obj 184 123 r insamprate;
+#X obj 320 380 *~;
+#X obj 136 350 *~;
+#X obj 323 261 noise~;
+#X obj 136 397 +~;
+#X obj 164 351 dbtorms;
+#X obj 346 380 dbtorms;
+#X obj 164 328 inlet;
+#X obj 346 358 inlet;
+#X obj 323 337 *~;
+#X obj 323 283 bp~ 2000 7;
+#X obj 136 449 outlet~;
+#X obj 110 98 r insamplength;
+#X msg 134 247 0 \, \$1 \$2;
+#X obj 134 221 pack 0 0;
+#X obj 180 199 /;
+#X obj 184 147 * 0.001;
+#X obj 180 171 t b f;
+#X text 312 234 nasty non-flat noise;
+#X obj 136 422 hip~ 5;
+#X obj 90 7 loadbang;
+#X text 242 13 sample playback;
+#X msg 90 27 1;
+#X obj 89 67 metro 1000;
+#X floatatom 89 46;
+#X obj 346 314 phasor~ 3000;
+#X connect 0 0 5 0;
+#X connect 1 0 0 0;
+#X connect 2 0 17 0;
+#X connect 2 0 18 0;
+#X connect 3 0 19 0;
+#X connect 4 0 7 1;
+#X connect 5 0 7 0;
+#X connect 6 0 13 0;
+#X connect 7 0 22 0;
+#X connect 8 0 5 1;
+#X connect 9 0 4 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 12 0 4 0;
+#X connect 13 0 12 0;
+#X connect 15 0 2 1;
+#X connect 16 0 1 0;
+#X connect 17 0 16 0;
+#X connect 18 0 17 1;
+#X connect 18 0 26 1;
+#X connect 19 0 20 0;
+#X connect 20 0 18 0;
+#X connect 20 1 18 1;
+#X connect 22 0 14 0;
+#X connect 23 0 25 0;
+#X connect 25 0 27 0;
+#X connect 26 0 2 0;
+#X connect 27 0 26 0;
+#X connect 28 0 12 1;
+#X restore 106 373 pd test-signal;
+#X text 94 315 sampler;
+#N canvas 132 255 634 331 insample 0;
+#X graph graph1 0 -1 155947 1 180 168 580 18;
+#X array sample 155948 float;
+#X pop;
+#X obj 19 70 r read-sample;
+#X obj 19 95 unpack s f;
+#X obj 53 121 s insamprate;
+#X obj 19 171 soundfiler;
+#X msg 19 147 read -resize \$1 sample;
+#X obj 19 201 s insamplength;
+#X msg 357 197 \; sample resize 220500 \; insamplength 220500;
+#X connect 1 0 2 0;
+#X connect 2 0 5 0;
+#X connect 2 1 3 0;
+#X connect 4 0 6 0;
+#X connect 5 0 4 0;
+#X restore 647 409 pd insample;
+#X text 536 219 read a sample;
+#X msg 446 240 \; read-sample ../sound/bell.aiff 44100;
+#X connect 0 0 5 0;
+#X connect 1 0 3 0;
+#X connect 3 0 2 0;
+#X connect 3 0 2 1;
+#X connect 6 0 3 1;
+#X connect 7 0 12 0;
+#X connect 8 0 7 0;
+#X connect 9 0 8 0;
+#X connect 13 0 6 0;
+#X connect 14 0 15 0;
+#X connect 15 0 20 1;
+#X connect 17 0 22 0;
+#X connect 18 0 17 0;
+#X connect 19 0 20 0;
+#X connect 19 0 22 0;
+#X connect 20 0 21 0;
+#X connect 27 0 28 0;
+#X connect 29 0 27 0;
+#X connect 33 0 27 0;
+#X connect 40 0 44 0;
+#X connect 43 0 44 1;
+#X connect 44 0 1 0;
diff --git a/pd/doc/4.fft.examples/06.sheepgoat2.pd b/pd/doc/4.fft.examples/06.sheepgoat2.pd
new file mode 100644
index 00000000..b62cc7a5
--- /dev/null
+++ b/pd/doc/4.fft.examples/06.sheepgoat2.pd
@@ -0,0 +1,365 @@
+#N canvas 5 22 761 478 12;
+#N canvas 6 0 911 637 fft-analysis 0;
+#X obj 581 379 *~;
+#X obj 542 379 *~;
+#X obj 542 407 -~;
+#X obj 62 529 *~;
+#X obj 318 304 *~;
+#X obj 243 145 *~;
+#X obj 279 144 *~;
+#X obj 205 144 *~;
+#X obj 166 144 *~;
+#X obj 166 172 +~;
+#X obj 25 597 *~;
+#X floatatom 643 486;
+#X obj 357 221 *~;
+#X obj 94 77 *~;
+#X obj 114 50 inlet~;
+#X obj 37 21 tabreceive~ hanning;
+#X obj 94 101 rfft~;
+#X obj 25 573 rifft~;
+#X obj 25 623 outlet~;
+#X text 43 3 Hanning window;
+#X text 123 84 forward real FFT;
+#X obj 318 220 *~;
+#X obj 318 249 +~;
+#X obj 783 612 block~ 2048 4;
+#X obj 283 45 tabreceive~ last-real;
+#X text 390 13 previous analysis;
+#X obj 478 45 tabreceive~ last-imag;
+#X obj 318 276 rsqrt~;
+#X text 390 224 inverse modulus;
+#X obj 244 173 -~;
+#X obj 260 304 *~;
+#X obj 146 453 *~;
+#X obj 100 505 tabsend~ last-real;
+#X obj 138 481 tabsend~ last-imag;
+#X text 174 449 sic;
+#X obj 626 559 -~;
+#X obj 627 586 *~;
+#X obj 628 612 clip~ 0 1;
+#X obj 644 435 r threshold;
+#X obj 14 548 *~;
+#X text 328 145 quotient (current/last);
+#X obj 643 510 t f f;
+#X obj 643 535 *;
+#X obj 277 510 tabsend~ precess-real;
+#X obj 535 327 tabreceive~ precess-real;
+#X obj 724 327 tabreceive~ precess-imag;
+#X obj 317 480 tabsend~ precess-imag;
+#X text 621 364 imaginary part of quotient;
+#X obj 539 440 *~;
+#X obj 643 462 / 100;
+#X text 374 270 normalized quotient \, which encodes the phase precession in the bin between two successive analyses.;
+#X text 623 381 of successive phase precession;
+#X text 621 397 values gives pitch stability;
+#X obj 731 492 r flip;
+#X obj 731 517 sel 0;
+#X msg 798 547 1e+20;
+#X msg 731 548 -1e+20;
+#X obj 9 370 *~ 0.001;
+#X obj 73 371 *~ 0.001;
+#X connect 0 0 2 1;
+#X connect 1 0 2 0;
+#X connect 2 0 31 0;
+#X connect 2 0 48 0;
+#X connect 2 0 48 1;
+#X connect 3 0 17 1;
+#X connect 4 0 46 0;
+#X connect 4 0 1 0;
+#X connect 5 0 29 0;
+#X connect 6 0 29 1;
+#X connect 7 0 9 1;
+#X connect 8 0 9 0;
+#X connect 9 0 30 0;
+#X connect 9 0 21 0;
+#X connect 9 0 21 1;
+#X connect 10 0 18 0;
+#X connect 11 0 41 0;
+#X connect 12 0 22 1;
+#X connect 13 0 16 0;
+#X connect 14 0 13 1;
+#X connect 15 0 10 1;
+#X connect 15 0 13 0;
+#X connect 16 0 6 0;
+#X connect 16 0 8 0;
+#X connect 16 0 32 0;
+#X connect 16 0 57 0;
+#X connect 16 1 7 0;
+#X connect 16 1 5 0;
+#X connect 16 1 33 0;
+#X connect 16 1 58 0;
+#X connect 17 0 10 0;
+#X connect 21 0 22 0;
+#X connect 22 0 27 0;
+#X connect 24 0 8 1;
+#X connect 24 0 5 1;
+#X connect 26 0 7 1;
+#X connect 26 0 6 1;
+#X connect 27 0 4 1;
+#X connect 27 0 30 1;
+#X connect 29 0 4 0;
+#X connect 29 0 12 0;
+#X connect 29 0 12 1;
+#X connect 30 0 43 0;
+#X connect 30 0 0 0;
+#X connect 31 0 33 0;
+#X connect 31 0 32 0;
+#X connect 31 0 43 0;
+#X connect 31 0 46 0;
+#X connect 35 0 36 0;
+#X connect 36 0 37 0;
+#X connect 37 0 3 1;
+#X connect 37 0 39 1;
+#X connect 38 0 49 0;
+#X connect 39 0 17 0;
+#X connect 41 0 42 0;
+#X connect 41 1 42 1;
+#X connect 42 0 35 1;
+#X connect 44 0 1 1;
+#X connect 45 0 0 1;
+#X connect 48 0 35 0;
+#X connect 49 0 11 0;
+#X connect 53 0 54 0;
+#X connect 54 0 56 0;
+#X connect 54 1 55 0;
+#X connect 55 0 36 1;
+#X connect 56 0 36 1;
+#X connect 57 0 39 0;
+#X connect 58 0 3 0;
+#X restore 58 368 pd fft-analysis;
+#X floatatom 207 217;
+#X obj 58 416 dac~;
+#X obj 58 388 *~;
+#N canvas 662 12 796 785 previous-analysis 0;
+#X graph graph1 0 -1 2048 1 344 407 744 107;
+#X array last-real 2048 float;
+#X pop;
+#X graph graph2 0 -1 2048 1 349 719 749 419;
+#X array last-imag 2048 float;
+#X pop;
+#X msg 43 217 \; last-real resize 2048 \; last-imag resize 2048;
+#X restore 262 368 pd previous-analysis;
+#X obj 207 245 s threshold;
+#X obj 106 421 line~;
+#X floatatom 553 78;
+#N canvas 194 37 397 591 output 0;
+#X obj 73 225 t b;
+#X obj 73 169 f;
+#X obj 73 113 inlet;
+#X text 79 89 mute;
+#X obj 73 253 f;
+#X msg 149 271 0;
+#X msg 73 141 bang;
+#X obj 73 197 moses 1;
+#X obj 149 243 t b f;
+#X obj 107 490 outlet;
+#X msg 107 462 set \$1;
+#X obj 206 181 moses 1;
+#X obj 249 493 dbtorms;
+#X obj 249 521 pack 0 100;
+#X obj 206 153 r master-lvl;
+#X obj 107 424 r master-lvl;
+#X obj 92 317 s master-lvl;
+#X obj 249 549 s master-amp;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X restore 553 50 pd output;
+#X msg 553 22 mute;
+#X text 596 69 MASTER;
+#X text 596 84 LEVEL;
+#X obj 553 106 s master-lvl;
+#X obj 106 393 r master-amp;
+#X obj 582 277 loadbang;
+#X obj 582 305 samplerate~;
+#X text 30 161 click here first;
+#X obj 472 334 hip~ 5;
+#X obj 472 306 adc~ 1;
+#X msg 545 265 bang;
+#X obj 545 306 f;
+#X obj 545 334 s insamprate;
+#X obj 472 362 tabwrite~ sample;
+#X text 465 235 record your own sample;
+#N canvas 190 37 605 462 hanning-window 0;
+#X obj 108 242 phasor~;
+#X obj 108 275 cos~;
+#X obj 27 386 tabwrite~ hanning;
+#X obj 35 297 -~;
+#X obj 33 257 sig~ 1;
+#X msg 44 212 0;
+#X text 166 16 CALCULATE HANNING;
+#X text 166 32 WINDOW TABLE;
+#X graph graph1 0 -1 2047 1 369 373 569 223;
+#X array hanning 2048 float;
+#X pop;
+#X obj 109 154 / 1024;
+#X obj 109 201 sig~;
+#X text 171 152 sample rate / window size;
+#X msg 27 169 bang;
+#X obj 78 316 sig~ 0.5;
+#X obj 58 353 *~;
+#X obj 79 113 samplerate~;
+#X obj 29 27 r window-size;
+#X obj 29 62 t b f;
+#X msg 385 402 \; hanning resize 2048;
+#X connect 0 0 1 0;
+#X connect 1 0 3 1;
+#X connect 3 0 14 0;
+#X connect 4 0 3 0;
+#X connect 5 0 0 1;
+#X connect 9 0 10 0;
+#X connect 10 0 0 0;
+#X connect 12 0 2 0;
+#X connect 12 0 5 0;
+#X connect 13 0 14 1;
+#X connect 14 0 2 0;
+#X connect 15 0 9 0;
+#X connect 16 0 17 0;
+#X connect 17 0 15 0;
+#X connect 17 0 12 0;
+#X connect 17 1 9 1;
+#X restore 262 340 pd hanning-window;
+#X text 121 15 PITCHED/UNPITCHED SEPARATION;
+#X floatatom 356 205;
+#X obj 356 233 s flip;
+#X msg 356 177 0;
+#X text 363 129 select:;
+#X text 326 157 pitched;
+#X text 391 156 noisy;
+#X msg 391 176 1;
+#X text 198 155 threshold;
+#X text 204 171 (range;
+#X text 207 189 0-100);
+#X text 21 73 A low threshold means to let only very coherent signals through. A high one permits more of the signal to be considered "pitched". You can select either to hear the pitched or the noisy part.;
+#N canvas 0 0 843 816 phase-precession 0;
+#X graph graph1 0 -1 2048 1 349 423 749 123;
+#X array precess-real 2048 float;
+#X pop;
+#X graph graph2 0 -1 2048 1 348 749 748 449;
+#X array precess-imag 2048 float;
+#X pop;
+#X msg 45 91 \; precess-real resize 2048 \; precess-imag resize 2048;
+#X restore 262 396 pd phase-precession;
+#X msg 23 182 \; window-size 2048 \; flip 0 \; pd dsp 1;
+#X text 20 35 Another patch to separate pitched from unpitched signals. This one does a frequency estimate for each bin and checks whether the frequency seems to be changing quickly or not.;
+#X floatatom 58 323;
+#X text 143 302 noise;
+#X text 84 282 amplitudes;
+#X floatatom 143 319;
+#N canvas 190 43 534 552 test-signal 0;
+#X obj 134 293 tabread4~ sample;
+#X obj 134 268 line~;
+#X obj 95 146 f;
+#X obj 184 123 r insamprate;
+#X obj 320 380 *~;
+#X obj 136 350 *~;
+#X obj 323 261 noise~;
+#X obj 136 397 +~;
+#X obj 164 351 dbtorms;
+#X obj 346 380 dbtorms;
+#X obj 164 328 inlet;
+#X obj 346 358 inlet;
+#X obj 323 337 *~;
+#X obj 323 283 bp~ 2000 7;
+#X obj 136 449 outlet~;
+#X obj 110 98 r insamplength;
+#X msg 134 247 0 \, \$1 \$2;
+#X obj 134 221 pack 0 0;
+#X obj 180 199 /;
+#X obj 184 147 * 0.001;
+#X obj 180 171 t b f;
+#X text 312 234 nasty non-flat noise;
+#X obj 136 422 hip~ 5;
+#X obj 90 7 loadbang;
+#X text 242 13 sample playback;
+#X msg 90 27 1;
+#X obj 89 67 metro 1000;
+#X floatatom 89 46;
+#X obj 346 314 phasor~ 3000;
+#X connect 0 0 5 0;
+#X connect 1 0 0 0;
+#X connect 2 0 17 0;
+#X connect 2 0 18 0;
+#X connect 3 0 19 0;
+#X connect 4 0 7 1;
+#X connect 5 0 7 0;
+#X connect 6 0 13 0;
+#X connect 7 0 22 0;
+#X connect 8 0 5 1;
+#X connect 9 0 4 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 12 0 4 0;
+#X connect 13 0 12 0;
+#X connect 15 0 2 1;
+#X connect 16 0 1 0;
+#X connect 17 0 16 0;
+#X connect 18 0 17 1;
+#X connect 18 0 26 1;
+#X connect 19 0 20 0;
+#X connect 20 0 18 0;
+#X connect 20 1 18 1;
+#X connect 22 0 14 0;
+#X connect 23 0 25 0;
+#X connect 25 0 27 0;
+#X connect 26 0 2 0;
+#X connect 27 0 26 0;
+#X connect 28 0 12 1;
+#X restore 58 345 pd test-signal;
+#X text 53 301 sampler;
+#X text 549 158 read a sample;
+#X msg 459 179 \; read-sample ../sound/bell.aiff 44100;
+#N canvas 132 255 634 335 insample 0;
+#X graph graph1 0 -1 155947 1 180 168 580 18;
+#X array sample 155948 float;
+#X pop;
+#X obj 19 70 r read-sample;
+#X obj 19 95 unpack s f;
+#X obj 53 121 s insamprate;
+#X obj 19 171 soundfiler;
+#X msg 19 147 read -resize \$1 sample;
+#X obj 19 201 s insamplength;
+#X msg 357 197 \; sample resize 220500 \; insamplength 220500;
+#X connect 1 0 2 0;
+#X connect 2 0 5 0;
+#X connect 2 1 3 0;
+#X connect 4 0 6 0;
+#X connect 5 0 4 0;
+#X restore 261 310 pd insample;
+#X connect 0 0 3 0;
+#X connect 1 0 5 0;
+#X connect 3 0 2 0;
+#X connect 3 0 2 1;
+#X connect 6 0 3 1;
+#X connect 7 0 12 0;
+#X connect 8 0 7 0;
+#X connect 9 0 8 0;
+#X connect 13 0 6 0;
+#X connect 14 0 15 0;
+#X connect 15 0 20 1;
+#X connect 17 0 22 0;
+#X connect 18 0 17 0;
+#X connect 19 0 20 0;
+#X connect 19 0 22 0;
+#X connect 20 0 21 0;
+#X connect 26 0 27 0;
+#X connect 28 0 26 0;
+#X connect 32 0 26 0;
+#X connect 40 0 44 0;
+#X connect 43 0 44 1;
+#X connect 44 0 0 0;
diff --git a/pd/doc/4.fft.examples/07.tinbell.pd b/pd/doc/4.fft.examples/07.tinbell.pd
new file mode 100644
index 00000000..59ca1970
--- /dev/null
+++ b/pd/doc/4.fft.examples/07.tinbell.pd
@@ -0,0 +1,248 @@
+#N canvas 175 36 759 457 12;
+#N canvas 147 -3 718 530 fft-analysis 0;
+#X obj 138 257 *~;
+#X obj 122 286 *~;
+#X obj 87 286 *~;
+#X obj 86 71 *~;
+#X obj 117 71 inlet~;
+#X obj 86 94 rfft~;
+#X obj 60 351 *~;
+#X obj 190 136 *~;
+#X obj 63 40 tabreceive~ hanning;
+#X obj 87 309 rifft~;
+#X obj 60 374 outlet~;
+#X obj 156 136 *~;
+#X obj 156 159 +~;
+#X obj 166 257 sig~ 0.001;
+#X obj 185 349 block~ 1024 4;
+#X obj 156 184 rsqrt~;
+#X obj 217 187 sig~ 1e-20;
+#X obj 156 211 clip~;
+#X text 37 2 What does this do?;
+#X floatatom 462 156;
+#X obj 462 67 r squelch;
+#X obj 462 177 moses;
+#X msg 461 207 0;
+#X obj 461 90 t f f;
+#X obj 462 113 *;
+#X obj 462 134 * 0.01;
+#X connect 0 0 1 1;
+#X connect 0 0 2 1;
+#X connect 1 0 9 1;
+#X connect 2 0 9 0;
+#X connect 3 0 5 0;
+#X connect 4 0 3 1;
+#X connect 5 0 11 0;
+#X connect 5 0 11 1;
+#X connect 5 0 2 0;
+#X connect 5 1 7 0;
+#X connect 5 1 7 1;
+#X connect 5 1 1 0;
+#X connect 6 0 10 0;
+#X connect 7 0 12 1;
+#X connect 8 0 3 0;
+#X connect 8 0 6 0;
+#X connect 9 0 6 1;
+#X connect 11 0 12 0;
+#X connect 12 0 15 0;
+#X connect 13 0 0 1;
+#X connect 15 0 17 0;
+#X connect 16 0 15 0;
+#X connect 17 0 0 0;
+#X connect 19 0 21 0;
+#X connect 20 0 23 0;
+#X connect 21 0 22 0;
+#X connect 21 1 17 2;
+#X connect 22 0 17 2;
+#X connect 23 0 24 0;
+#X connect 23 0 24 1;
+#X connect 24 0 25 0;
+#X connect 25 0 19 0;
+#X restore 74 366 pd fft-analysis;
+#X floatatom 192 206;
+#X obj 74 414 dac~;
+#X obj 74 390 *~;
+#X obj 119 414 line~;
+#X floatatom 587 68;
+#N canvas 194 37 397 591 output 0;
+#X obj 62 191 t b;
+#X obj 62 144 f;
+#X obj 62 96 inlet;
+#X text 67 76 mute;
+#X obj 62 215 f;
+#X msg 127 230 0;
+#X msg 62 120 bang;
+#X obj 62 167 moses 1;
+#X obj 127 207 t b f;
+#X obj 91 417 outlet;
+#X msg 91 393 set \$1;
+#X obj 175 154 moses 1;
+#X obj 212 419 dbtorms;
+#X obj 212 443 pack 0 100;
+#X obj 175 130 r master-lvl;
+#X obj 91 360 r master-lvl;
+#X obj 78 269 s master-lvl;
+#X obj 212 467 s master-amp;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X restore 587 44 pd output;
+#X msg 587 20 mute;
+#X text 624 60 MASTER;
+#X text 623 74 LEVEL;
+#X obj 587 92 s master-lvl;
+#X obj 119 390 r master-amp;
+#X obj 664 177 loadbang;
+#X obj 664 201 samplerate~;
+#X text 30 187 click here first;
+#X text 361 166 read a sample;
+#X obj 562 225 hip~ 5;
+#X obj 562 201 adc~ 1;
+#X msg 624 170 bang;
+#X obj 624 201 f;
+#X obj 624 225 s insamprate;
+#X obj 562 248 tabwrite~ sample;
+#N canvas 107 33 607 436 hanning-window 0;
+#X obj 92 206 phasor~;
+#X obj 92 234 cos~;
+#X obj 23 328 tabwrite~ hanning;
+#X obj 30 252 -~;
+#X obj 28 218 sig~ 1;
+#X msg 37 180 0;
+#X text 141 13 CALCULATE HANNING;
+#X text 141 27 WINDOW TABLE;
+#X graph graph1 0 -1 1024 1 295 345 495 195;
+#X array hanning 1024 float;
+#X pop;
+#X msg 304 374 \; hanning resize 1024;
+#X obj 93 131 / 1024;
+#X obj 93 171 sig~;
+#X text 175 148 sample rate / window size;
+#X msg 23 144 bang;
+#X obj 66 269 sig~ 0.5;
+#X obj 49 300 *~;
+#X obj 67 96 samplerate~;
+#X obj 25 23 r window-size;
+#X obj 25 53 t b f;
+#X connect 0 0 1 0;
+#X connect 1 0 3 1;
+#X connect 3 0 15 0;
+#X connect 4 0 3 0;
+#X connect 5 0 0 1;
+#X connect 10 0 11 0;
+#X connect 11 0 0 0;
+#X connect 13 0 2 0;
+#X connect 13 0 5 0;
+#X connect 14 0 15 1;
+#X connect 15 0 2 0;
+#X connect 16 0 10 0;
+#X connect 17 0 18 0;
+#X connect 18 0 16 0;
+#X connect 18 0 13 0;
+#X connect 18 1 10 1;
+#X restore 521 332 pd hanning-window;
+#X msg 24 205 \; window-size 1024 \; pd dsp 1;
+#X obj 192 230 s squelch;
+#X text 116 3 BOOSTING SMALL AMPLITUDES;
+#X text 614 146 record;
+#X floatatom 75 316;
+#N canvas 190 43 405 461 test-signal 0;
+#X obj 134 293 tabread4~ sample;
+#X obj 134 268 line~;
+#X obj 95 146 f;
+#X obj 254 46 r insamprate;
+#X obj 136 350 *~;
+#X obj 164 351 dbtorms;
+#X obj 164 328 inlet;
+#X obj 135 415 outlet~;
+#X obj 146 33 r insamplength;
+#X msg 134 247 0 \, \$1 \$2;
+#X obj 134 221 pack 0 0;
+#X obj 209 190 /;
+#X obj 299 99 * 0.001;
+#X obj 135 388 hip~ 5;
+#X obj 33 5 loadbang;
+#X text 242 13 sample playback;
+#X msg 33 25 1;
+#X obj 33 69 metro 1000;
+#X floatatom 33 48;
+#X obj 255 75 t b b f;
+#X obj 161 84 t b f;
+#X connect 0 0 4 0;
+#X connect 1 0 0 0;
+#X connect 2 0 10 0;
+#X connect 3 0 19 0;
+#X connect 4 0 13 0;
+#X connect 5 0 4 1;
+#X connect 6 0 5 0;
+#X connect 8 0 20 0;
+#X connect 9 0 1 0;
+#X connect 10 0 9 0;
+#X connect 11 0 10 1;
+#X connect 11 0 17 1;
+#X connect 12 0 11 1;
+#X connect 13 0 7 0;
+#X connect 14 0 16 0;
+#X connect 16 0 18 0;
+#X connect 17 0 2 0;
+#X connect 18 0 17 0;
+#X connect 19 0 16 0;
+#X connect 19 1 11 0;
+#X connect 19 2 12 0;
+#X connect 20 0 16 0;
+#X connect 20 1 11 0;
+#X connect 20 1 2 1;
+#X restore 75 339 pd test-signal;
+#X text 76 294 amplitude;
+#N canvas 132 255 634 331 insample 0;
+#X graph graph1 0 -1 155947 1 199 168 599 18;
+#X array sample 155948 float;
+#X pop;
+#X obj 19 70 r read-sample;
+#X obj 19 95 unpack s f;
+#X obj 53 121 s insamprate;
+#X obj 19 171 soundfiler;
+#X msg 19 147 read -resize \$1 sample;
+#X obj 19 201 s insamplength;
+#X msg 357 197 \; sample resize 220500 \; insamplength 220500;
+#X connect 1 0 2 0;
+#X connect 2 0 5 0;
+#X connect 2 1 3 0;
+#X connect 4 0 6 0;
+#X connect 5 0 4 0;
+#X restore 521 357 pd insample;
+#X msg 282 189 \; read-sample ../sound/bell.aiff 44100;
+#X text 38 27 the "fft analysis" window here simply divides each complex bin of the FFT by its own magnitude to "flatten" the spectrum. The "squelch" control limits the amplitude boost the algorithm will apply. If infinite \, you'll get a white spectrum \; if less \, the louder parts of the spectrum will be flattened but the quieter ones will only be boosetd by the squelch value.;
+#X msg 281 229 \; read-sample ../sound/voice.wav 32000;
+#X connect 0 0 3 0;
+#X connect 1 0 24 0;
+#X connect 3 0 2 0;
+#X connect 3 0 2 1;
+#X connect 4 0 3 1;
+#X connect 5 0 10 0;
+#X connect 6 0 5 0;
+#X connect 7 0 6 0;
+#X connect 11 0 4 0;
+#X connect 12 0 13 0;
+#X connect 13 0 19 1;
+#X connect 16 0 21 0;
+#X connect 17 0 16 0;
+#X connect 18 0 19 0;
+#X connect 18 0 21 0;
+#X connect 19 0 20 0;
+#X connect 27 0 28 0;
+#X connect 28 0 0 0;
diff --git a/pd/doc/4.fft.examples/08.convobros.pd b/pd/doc/4.fft.examples/08.convobros.pd
new file mode 100644
index 00000000..8e49ef3d
--- /dev/null
+++ b/pd/doc/4.fft.examples/08.convobros.pd
@@ -0,0 +1,344 @@
+#N canvas 16 29 916 517 12;
+#N canvas 147 -3 669 492 fft-analysis 0;
+#X obj 265 254 *~;
+#X obj 249 284 *~;
+#X obj 214 284 *~;
+#X obj 114 120 *~;
+#X obj 80 114 *~;
+#X obj 80 137 +~;
+#X obj 213 68 *~;
+#X obj 245 68 inlet~;
+#X obj 213 91 rfft~;
+#X obj 59 304 *~;
+#X floatatom 462 156 0 0 0;
+#X obj 317 133 *~;
+#X obj 80 68 *~;
+#X obj 113 68 inlet~;
+#X obj 61 34 tabreceive~ hanning;
+#X obj 80 91 rfft~;
+#X obj 214 307 rifft~;
+#X obj 59 327 outlet~;
+#X text 37 2 Hanning window;
+#X obj 284 133 *~;
+#X obj 284 156 +~;
+#X obj 293 254 sig~ 0.001;
+#X text 129 165 modulus;
+#X obj 80 160 sqrt~;
+#X obj 265 231 *~;
+#X obj 462 67 r squelch;
+#X obj 313 346 block~ 1024 4;
+#X obj 284 182 rsqrt~;
+#X obj 344 184 sig~ 1e-20;
+#X obj 284 208 clip~;
+#X obj 462 177 moses;
+#X msg 457 221 0;
+#X obj 461 90 t f f;
+#X obj 462 113 *;
+#X obj 462 134 * 0.01;
+#X connect 0 0 1 1;
+#X connect 0 0 2 1;
+#X connect 1 0 16 1;
+#X connect 2 0 16 0;
+#X connect 3 0 5 1;
+#X connect 4 0 5 0;
+#X connect 5 0 23 0;
+#X connect 6 0 8 0;
+#X connect 7 0 6 1;
+#X connect 8 0 19 0;
+#X connect 8 0 19 1;
+#X connect 8 0 2 0;
+#X connect 8 1 11 0;
+#X connect 8 1 11 1;
+#X connect 8 1 1 0;
+#X connect 9 0 17 0;
+#X connect 10 0 30 0;
+#X connect 11 0 20 1;
+#X connect 12 0 15 0;
+#X connect 13 0 12 1;
+#X connect 14 0 12 0;
+#X connect 14 0 6 0;
+#X connect 14 0 9 0;
+#X connect 15 0 4 0;
+#X connect 15 0 4 1;
+#X connect 15 1 3 0;
+#X connect 15 1 3 1;
+#X connect 16 0 9 1;
+#X connect 19 0 20 0;
+#X connect 20 0 27 0;
+#X connect 21 0 0 1;
+#X connect 23 0 24 0;
+#X connect 24 0 0 0;
+#X connect 25 0 32 0;
+#X connect 27 0 29 0;
+#X connect 28 0 27 0;
+#X connect 29 0 24 1;
+#X connect 30 0 31 0;
+#X connect 30 1 29 2;
+#X connect 31 0 29 2;
+#X connect 32 0 33 0;
+#X connect 32 0 33 1;
+#X connect 33 0 34 0;
+#X connect 34 0 10 0;
+#X restore 119 405 pd fft-analysis;
+#X floatatom 190 159 0 0 0;
+#X obj 119 453 dac~;
+#X obj 119 429 *~;
+#X obj 164 453 line~;
+#X floatatom 587 68 0 0 0;
+#N canvas 194 37 397 591 output 0;
+#X obj 62 191 t b;
+#X obj 62 144 f;
+#X obj 62 96 inlet;
+#X text 67 76 mute;
+#X obj 62 215 f;
+#X msg 127 230 0;
+#X msg 62 120 bang;
+#X obj 62 167 moses 1;
+#X obj 127 207 t b f;
+#X obj 91 417 outlet;
+#X msg 91 393 set \$1;
+#X obj 175 154 moses 1;
+#X obj 212 419 dbtorms;
+#X obj 212 443 pack 0 100;
+#X obj 175 130 r master-lvl;
+#X obj 91 360 r master-lvl;
+#X obj 78 269 s master-lvl;
+#X obj 212 467 s master-amp;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X restore 587 44 pd output;
+#X msg 587 20 mute;
+#X text 624 61 MASTER;
+#X text 624 75 LEVEL;
+#X obj 587 92 s master-lvl;
+#X obj 164 429 r master-amp;
+#X obj 424 420 loadbang;
+#X obj 424 443 samplerate~;
+#X obj 424 467 s srate;
+#X text 31 137 click here first;
+#X obj 724 237 hip~ 5;
+#X obj 724 213 adc~ 1;
+#X msg 786 182 bang;
+#X obj 814 213 r srate;
+#X obj 786 213 f;
+#X obj 786 237 s insamprate;
+#X obj 724 260 tabwrite~ sample;
+#N canvas 275 71 767 761 hanning-window 0;
+#X obj 92 206 phasor~;
+#X obj 92 234 cos~;
+#X obj 23 328 tabwrite~ hanning;
+#X obj 30 252 -~;
+#X obj 28 218 sig~ 1;
+#X msg 37 180 0;
+#X text 141 13 CALCULATE HANNING;
+#X text 141 27 WINDOW TABLE;
+#X graph graph1 0 -1 1024 1 275 581 675 281;
+#X array hanning 1024 float 0;
+#X pop;
+#X msg 275 547 \; hanning resize 1024;
+#X obj 93 131 / 1024;
+#X obj 93 171 sig~;
+#X text 175 148 sample rate / window size;
+#X msg 23 144 bang;
+#X obj 66 269 sig~ 0.5;
+#X obj 49 300 *~;
+#X obj 67 96 samplerate~;
+#X obj 25 23 r window-size;
+#X obj 25 53 t b f;
+#X connect 0 0 1 0;
+#X connect 1 0 3 1;
+#X connect 3 0 15 0;
+#X connect 4 0 3 0;
+#X connect 5 0 0 1;
+#X connect 10 0 11 0;
+#X connect 11 0 0 0;
+#X connect 13 0 2 0;
+#X connect 13 0 5 0;
+#X connect 14 0 15 1;
+#X connect 15 0 2 0;
+#X connect 16 0 10 0;
+#X connect 17 0 18 0;
+#X connect 18 0 16 0;
+#X connect 18 0 13 0;
+#X connect 18 1 10 1;
+#X restore 419 387 pd hanning-window;
+#X msg 25 155 \; window-size 1024 \; pd dsp 1;
+#X obj 190 183 s squelch;
+#X obj 725 388 hip~ 5;
+#X obj 725 364 adc~ 1;
+#X msg 787 333 bang;
+#X obj 815 364 r srate;
+#X obj 787 364 f;
+#X obj 787 388 s insamprate;
+#X obj 725 412 tabwrite~ sample2;
+#X text 177 6 CORT&ZACK's SECRET;
+#X text 190 137 range 1-100;
+#N canvas 190 43 743 445 test-signal 0;
+#X obj 78 265 tabread4~ sample;
+#X obj 78 244 line~;
+#X obj 78 158 f;
+#X obj 238 62 r insamprate;
+#X obj 79 369 outlet~;
+#X obj 130 54 r insamplength;
+#X msg 78 223 0 \, \$1 \$2;
+#X obj 78 202 pack 0 0;
+#X obj 175 196 /;
+#X obj 283 111 * 0.001;
+#X obj 79 346 hip~ 5;
+#X obj 25 29 loadbang;
+#X text 150 24 sample playback;
+#X msg 25 55 1;
+#X obj 25 93 metro 1000;
+#X floatatom 25 75 0 0 0;
+#X obj 239 87 t b b f;
+#X obj 134 105 t b f;
+#X obj 445 256 line~;
+#X obj 445 170 f;
+#X obj 446 381 outlet~;
+#X msg 445 235 0 \, \$1 \$2;
+#X obj 445 214 pack 0 0;
+#X obj 542 208 /;
+#X obj 650 123 * 0.001;
+#X obj 446 358 hip~ 5;
+#X obj 392 41 loadbang;
+#X msg 392 67 1;
+#X obj 392 105 metro 1000;
+#X floatatom 392 87 0 0 0;
+#X obj 606 99 t b b f;
+#X obj 501 117 t b f;
+#X obj 445 277 tabread4~ sample2;
+#X obj 497 66 r insamplength2;
+#X obj 605 74 r insamprate2;
+#X connect 0 0 10 0;
+#X connect 1 0 0 0;
+#X connect 2 0 7 0;
+#X connect 3 0 16 0;
+#X connect 5 0 17 0;
+#X connect 6 0 1 0;
+#X connect 7 0 6 0;
+#X connect 8 0 7 1;
+#X connect 8 0 14 1;
+#X connect 9 0 8 1;
+#X connect 10 0 4 0;
+#X connect 11 0 13 0;
+#X connect 13 0 15 0;
+#X connect 14 0 2 0;
+#X connect 15 0 14 0;
+#X connect 16 0 13 0;
+#X connect 16 1 8 0;
+#X connect 16 2 9 0;
+#X connect 17 0 13 0;
+#X connect 17 1 8 0;
+#X connect 17 1 2 1;
+#X connect 18 0 32 0;
+#X connect 19 0 22 0;
+#X connect 21 0 18 0;
+#X connect 22 0 21 0;
+#X connect 23 0 22 1;
+#X connect 23 0 28 1;
+#X connect 24 0 23 1;
+#X connect 25 0 20 0;
+#X connect 26 0 27 0;
+#X connect 27 0 29 0;
+#X connect 28 0 19 0;
+#X connect 29 0 28 0;
+#X connect 30 0 27 0;
+#X connect 30 1 23 0;
+#X connect 30 2 24 0;
+#X connect 31 0 27 0;
+#X connect 31 1 23 0;
+#X connect 31 1 19 1;
+#X connect 32 0 25 0;
+#X connect 33 0 31 0;
+#X connect 34 0 30 0;
+#X restore 117 371 pd test-signal;
+#N canvas 132 255 634 327 insample 0;
+#X graph graph1 0 -1 155947 1 199 168 599 18;
+#X array sample 155948 float 0;
+#X pop;
+#X obj 19 70 r read-sample;
+#X obj 19 95 unpack s f;
+#X obj 53 121 s insamprate;
+#X obj 19 171 soundfiler;
+#X msg 19 147 read -resize \$1 sample;
+#X obj 19 201 s insamplength;
+#X msg 357 197 \; sample resize 220500 \; insamplength 220500;
+#X connect 1 0 2 0;
+#X connect 2 0 5 0;
+#X connect 2 1 3 0;
+#X connect 4 0 6 0;
+#X connect 5 0 4 0;
+#X restore 417 332 pd insample;
+#N canvas 0 0 647 284 insample2 0;
+#X obj 19 95 unpack s f;
+#X obj 19 171 soundfiler;
+#X msg 357 197 \; sample resize 220500 \; insamplength 220500;
+#X graph graph2 0 -1 63023 1 207 159 607 19;
+#X array sample2 63024 float 0;
+#X pop;
+#X obj 53 121 s insamprate2;
+#X msg 19 147 read -resize \$1 sample2;
+#X obj 19 201 s insamplength2;
+#X obj 19 70 r read-sample2;
+#X connect 0 0 5 0;
+#X connect 0 1 4 0;
+#X connect 1 0 6 0;
+#X connect 5 0 1 0;
+#X connect 7 0 0 0;
+#X restore 418 358 pd insample2;
+#X msg 19 230 \; read-sample ../sound/bell.aiff 44100;
+#X msg 357 230 \; read-sample2 ../sound/bell.aiff 44100;
+#X text 247 364 filter;
+#X text 249 379 input;
+#X text 51 401 source;
+#X text 53 362 filter;
+#X text 52 382 control;
+#X text 719 158 record sample;
+#X text 720 309 record sample2;
+#X text 37 60 To get sound \, you have to "click here first" \, set
+squelch to 10 or so \, read or record two samples \, and ease up the
+master volume.;
+#X msg 20 274 \; read-sample ../sound/voice.wav 32000;
+#X msg 358 274 \; read-sample2 ../sound/voice.wav 32000;
+#X text 37 27 This is an FFT based vocoder such as the one the Convo
+bros showed at the Ann Arbor ICMC.;
+#X connect 0 0 3 0;
+#X connect 1 0 25 0;
+#X connect 3 0 2 0;
+#X connect 3 0 2 1;
+#X connect 4 0 3 1;
+#X connect 5 0 10 0;
+#X connect 6 0 5 0;
+#X connect 7 0 6 0;
+#X connect 11 0 4 0;
+#X connect 12 0 13 0;
+#X connect 13 0 14 0;
+#X connect 16 0 22 0;
+#X connect 17 0 16 0;
+#X connect 18 0 20 0;
+#X connect 18 0 22 0;
+#X connect 19 0 20 1;
+#X connect 20 0 21 0;
+#X connect 26 0 32 0;
+#X connect 27 0 26 0;
+#X connect 28 0 30 0;
+#X connect 28 0 32 0;
+#X connect 29 0 30 1;
+#X connect 30 0 31 0;
+#X connect 35 0 0 0;
+#X connect 35 1 0 1;
diff --git a/pd/doc/4.fft.examples/09.pvoc.pd b/pd/doc/4.fft.examples/09.pvoc.pd
new file mode 100644
index 00000000..495fd535
--- /dev/null
+++ b/pd/doc/4.fft.examples/09.pvoc.pd
@@ -0,0 +1,397 @@
+#N canvas 100 38 719 544 12;
+#X floatatom 283 336;
+#X msg 569 149 bang;
+#X floatatom 197 329;
+#X floatatom 90 330;
+#N canvas 0 2 986 683 fft-analysis 0;
+#X obj 261 297 *~;
+#X obj 230 297 *~;
+#X obj 230 319 -~;
+#X obj 326 298 *~;
+#X obj 295 298 *~;
+#X obj 295 320 +~;
+#X obj 340 128 *~;
+#X obj 309 128 *~;
+#X obj 281 128 *~;
+#X obj 250 128 *~;
+#X obj 250 150 +~;
+#X obj 220 179 *~;
+#X obj 86 556 *~;
+#X obj 366 251 rfft~;
+#X obj 387 53 rfft~;
+#X obj 477 637 *~;
+#X obj 685 288 r window-size;
+#X obj 751 201 r sample-rate;
+#X obj 609 234 f;
+#X obj 603 52 r sample-rate;
+#X obj 578 29 r window-size;
+#X obj 601 98 t b f;
+#X obj 578 121 /;
+#X obj 507 637 *~;
+#X obj 87 536 *~;
+#X obj 103 513 rifft~;
+#X obj 87 582 outlet~;
+#X obj 625 446 print~;
+#X msg 624 418 bang;
+#X text 152 513 inverse real FFT;
+#X obj 589 210 bang~;
+#X obj 516 424 line~;
+#X obj 578 143 * 1000;
+#X text 630 136 window size (msec);
+#X obj 603 76 * 4;
+#X obj 632 159 r speed;
+#X obj 709 160 r location;
+#X obj 640 235 +;
+#X obj 633 200 *;
+#X msg 710 188 0;
+#X obj 609 341 +;
+#X obj 600 298 t f f;
+#X msg 516 396 \$1 \, \$2 \$3;
+#X obj 516 369 pack 0 0 0;
+#X obj 752 229 / 1000;
+#X obj 609 269 *;
+#X text 639 270 reading location (samples);
+#X obj 637 381 / 4;
+#X text 669 386 hop size (samples);
+#X obj 564 464 sig~;
+#X obj 534 463 +~;
+#X text 638 82 (overlap times parent SR);
+#X text 638 69 local sample rate;
+#X obj 23 51 tabreceive~ phase-real;
+#X obj 190 179 *~;
+#X obj 190 201 +~;
+#X obj 190 225 rsqrt~;
+#X obj 309 150 -~;
+#X obj 231 255 *~;
+#X obj 291 255 *~;
+#X obj 198 51 tabreceive~ phase-imag;
+#X obj 105 381 sig~;
+#X obj 88 311 t b f;
+#X msg 88 332 1;
+#X obj 102 355 /;
+#X obj 227 436 tabsend~ phase-real;
+#X obj 256 408 tabsend~ phase-imag;
+#X obj 104 133 sig~ 1.5e-20;
+#X obj 632 180 * 0.01;
+#X obj 691 212 s speed;
+#X obj 468 251 s see-location;
+#X obj 598 618 block~ 2048 4;
+#X floatatom 672 360;
+#X obj 670 339 *;
+#X obj 829 234 r transpo;
+#X obj 829 256 * 0.01;
+#X obj 831 277 + 69;
+#X obj 832 298 mtof;
+#X obj 832 320 / 440;
+#X obj 689 316 t b f;
+#X obj 88 290 r window-size;
+#X floatatom 835 353;
+#X obj 551 543 tabreceive~ hanning;
+#X obj 536 488 tabread4~ sample;
+#X obj 502 513 tabread4~ sample;
+#X connect 0 0 2 1;
+#X connect 1 0 2 0;
+#X connect 2 0 65 0;
+#X connect 2 0 25 0;
+#X connect 3 0 5 1;
+#X connect 4 0 5 0;
+#X connect 5 0 66 0;
+#X connect 5 0 25 1;
+#X connect 6 0 57 1;
+#X connect 7 0 57 0;
+#X connect 8 0 10 1;
+#X connect 9 0 10 0;
+#X connect 10 0 58 1;
+#X connect 10 0 54 0;
+#X connect 10 0 54 1;
+#X connect 11 0 55 1;
+#X connect 12 0 26 0;
+#X connect 13 0 1 1;
+#X connect 13 0 3 1;
+#X connect 13 1 0 1;
+#X connect 13 1 4 1;
+#X connect 14 0 9 1;
+#X connect 14 0 7 1;
+#X connect 14 1 6 1;
+#X connect 14 1 8 1;
+#X connect 15 0 14 0;
+#X connect 16 0 73 0;
+#X connect 17 0 44 0;
+#X connect 18 0 37 0;
+#X connect 18 0 45 0;
+#X connect 18 0 70 0;
+#X connect 19 0 34 0;
+#X connect 20 0 22 0;
+#X connect 21 0 22 0;
+#X connect 21 1 22 1;
+#X connect 22 0 32 0;
+#X connect 23 0 13 0;
+#X connect 24 0 12 0;
+#X connect 25 0 24 1;
+#X connect 28 0 27 0;
+#X connect 30 0 18 0;
+#X connect 31 0 27 0;
+#X connect 31 0 50 0;
+#X connect 31 0 84 0;
+#X connect 32 0 43 2;
+#X connect 32 0 38 1;
+#X connect 34 0 21 0;
+#X connect 35 0 68 0;
+#X connect 36 0 39 0;
+#X connect 36 0 18 1;
+#X connect 37 0 18 1;
+#X connect 38 0 37 1;
+#X connect 39 0 69 0;
+#X connect 40 0 43 1;
+#X connect 41 0 43 0;
+#X connect 41 1 40 0;
+#X connect 42 0 31 0;
+#X connect 43 0 42 0;
+#X connect 44 0 45 1;
+#X connect 45 0 41 0;
+#X connect 47 0 49 0;
+#X connect 49 0 50 1;
+#X connect 50 0 83 0;
+#X connect 53 0 9 0;
+#X connect 53 0 6 0;
+#X connect 54 0 55 0;
+#X connect 55 0 56 0;
+#X connect 56 0 58 0;
+#X connect 56 0 59 0;
+#X connect 57 0 59 1;
+#X connect 57 0 11 0;
+#X connect 57 0 11 1;
+#X connect 58 0 1 0;
+#X connect 58 0 4 0;
+#X connect 59 0 0 0;
+#X connect 59 0 3 0;
+#X connect 60 0 7 0;
+#X connect 60 0 8 0;
+#X connect 61 0 24 0;
+#X connect 62 0 63 0;
+#X connect 62 1 64 1;
+#X connect 63 0 64 0;
+#X connect 64 0 61 0;
+#X connect 67 0 10 0;
+#X connect 68 0 38 0;
+#X connect 72 0 47 0;
+#X connect 72 0 40 1;
+#X connect 73 0 72 0;
+#X connect 74 0 75 0;
+#X connect 75 0 76 0;
+#X connect 76 0 77 0;
+#X connect 77 0 78 0;
+#X connect 78 0 79 0;
+#X connect 78 0 81 0;
+#X connect 79 0 73 0;
+#X connect 79 1 73 1;
+#X connect 80 0 62 0;
+#X connect 82 0 23 1;
+#X connect 82 0 15 1;
+#X connect 82 0 12 1;
+#X connect 83 0 23 0;
+#X connect 84 0 15 0;
+#X restore 43 402 pd fft-analysis;
+#X obj 43 488 dac~;
+#N canvas 260 23 647 768 phase-tables 0;
+#X graph graph2 0 -1 4096 1 172 590 572 290;
+#X array phase-imag 4096 float;
+#X pop;
+#X graph graph3 0 -1 4096 1 170 317 570 17;
+#X array phase-real 4096 float;
+#X pop;
+#X msg 167 564 \; phase-real resize 4096 \; phase-imag resize 4096;
+#X restore 425 441 pd phase-tables;
+#X obj 43 428 hip~ 5;
+#X obj 43 458 *~;
+#N canvas 249 280 600 398 loc&precess 0;
+#X floatatom 160 229;
+#X msg 270 175 set \$1;
+#X obj 269 207 outlet;
+#X obj 83 267 outlet;
+#X obj 171 71 r location;
+#X msg 82 235 set \$1;
+#X obj 269 149 r speed;
+#X obj 55 70 r see-location;
+#X obj 67 102 t b f;
+#X obj 82 214 f;
+#X obj 54 127 int;
+#X obj 54 151 sel 0;
+#X msg 153 102 1;
+#X msg 201 101 0;
+#X obj 110 179 del 300;
+#X connect 1 0 2 0;
+#X connect 4 0 8 0;
+#X connect 5 0 3 0;
+#X connect 6 0 1 0;
+#X connect 7 0 8 0;
+#X connect 8 0 10 0;
+#X connect 8 1 9 1;
+#X connect 9 0 5 0;
+#X connect 10 0 11 0;
+#X connect 11 0 12 0;
+#X connect 11 0 14 0;
+#X connect 12 0 10 1;
+#X connect 13 0 10 1;
+#X connect 14 0 13 0;
+#X connect 14 0 9 0;
+#X restore 90 302 pd loc&precess;
+#N canvas 0 0 600 400 setlocprecess 0;
+#X obj 173 82 inlet;
+#X obj 73 80 inlet;
+#X obj 169 105 s speed;
+#X obj 74 105 s location;
+#X connect 0 0 2 0;
+#X connect 1 0 3 0;
+#X restore 90 353 pd setlocprecess;
+#X msg 47 313 0;
+#X obj 612 162 adc~;
+#X obj 283 361 s transpo;
+#X obj 283 285 r transpo;
+#X msg 283 311 set \$1;
+#X msg 423 268 \; location 0 \; speed 200;
+#N canvas 138 111 767 761 hanning-window 0;
+#X obj 92 206 phasor~;
+#X obj 92 234 cos~;
+#X obj 23 328 tabwrite~ hanning;
+#X obj 30 252 -~;
+#X obj 28 218 sig~ 1;
+#X msg 37 180 0;
+#X text 141 13 CALCULATE HANNING;
+#X text 141 27 WINDOW TABLE;
+#X graph graph1 0 -1 4096 1 275 581 675 281;
+#X array hanning 4096 float;
+#X pop;
+#X obj 93 171 sig~;
+#X text 175 148 sample rate / window size;
+#X msg 23 144 bang;
+#X obj 66 269 sig~ 0.5;
+#X obj 49 300 *~;
+#X obj 94 80 samplerate~;
+#X obj 25 23 r window-size;
+#X obj 25 53 t b f;
+#X msg 275 547 \; hanning resize 4096;
+#X obj 93 132 /;
+#X connect 0 0 1 0;
+#X connect 1 0 3 1;
+#X connect 3 0 13 0;
+#X connect 4 0 3 0;
+#X connect 5 0 0 1;
+#X connect 9 0 0 0;
+#X connect 11 0 2 0;
+#X connect 11 0 5 0;
+#X connect 12 0 13 1;
+#X connect 13 0 2 0;
+#X connect 14 0 18 0;
+#X connect 15 0 16 0;
+#X connect 16 0 14 0;
+#X connect 16 0 11 0;
+#X connect 16 1 18 1;
+#X connect 18 0 9 0;
+#X restore 423 464 pd hanning-window;
+#X msg 31 172 \; window-size 2048 \; transpo 0 \; pd dsp 1;
+#X floatatom 587 68;
+#N canvas 194 37 397 591 output 0;
+#X obj 62 191 t b;
+#X obj 62 144 f;
+#X obj 62 96 inlet;
+#X text 67 76 mute;
+#X obj 62 215 f;
+#X msg 127 230 0;
+#X msg 62 120 bang;
+#X obj 62 167 moses 1;
+#X obj 127 207 t b f;
+#X obj 91 417 outlet;
+#X msg 91 393 set \$1;
+#X obj 175 154 moses 1;
+#X obj 212 419 dbtorms;
+#X obj 212 443 pack 0 100;
+#X obj 175 130 r master-lvl;
+#X obj 91 360 r master-lvl;
+#X obj 78 269 s master-lvl;
+#X obj 212 467 s master-amp;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X restore 587 44 pd output;
+#X msg 587 20 mute;
+#X text 624 60 MASTER;
+#X text 623 74 LEVEL;
+#X obj 587 92 s master-lvl;
+#X text 41 153 click here first;
+#X text 261 3 PHASE VOCODER;
+#X obj 87 486 line~;
+#X obj 87 462 r master-amp;
+#X text 108 30 This is a Fourier-based analysis/resynthesis tool.;
+#X text 55 251 set location;
+#X text 54 265 and stop;
+#X text 54 279 precession;
+#X text 172 250 precession;
+#X text 171 266 speed in;
+#X text 171 280 hundredths;
+#X text 281 244 transposition;
+#X text 282 262 in cents;
+#X text 491 223 examples;
+#X text 428 248 contraction;
+#X text 542 247 expansion;
+#X msg 531 270 \; location 0 \; speed 10;
+#X obj 425 340 loadbang;
+#X obj 429 363 samplerate~;
+#X obj 425 385 s sample-rate;
+#N canvas 132 255 634 327 insample 0;
+#X graph graph1 0 -1 155947 1 199 168 599 18;
+#X array sample 155948 float;
+#X pop;
+#X obj 19 70 r read-sample;
+#X obj 19 95 unpack s f;
+#X obj 53 121 s insamprate;
+#X obj 19 171 soundfiler;
+#X msg 19 147 read -resize \$1 sample;
+#X obj 19 201 s insamplength;
+#X msg 357 197 \; sample resize 220500 \; insamplength 220500;
+#X connect 1 0 2 0;
+#X connect 2 0 5 0;
+#X connect 2 1 3 0;
+#X connect 4 0 6 0;
+#X connect 5 0 4 0;
+#X restore 425 415 pd insample;
+#X msg 196 146 \; read-sample ../sound/bell.aiff \; transpo 0;
+#X msg 196 197 \; read-sample ../sound/voice.wav \; transpo -530;
+#X text 107 99 The second soundfile read button sets the transposition to correct for the 32000 sample rate of the file.;
+#X text 108 50 You can move forward or backward in the sample \, or "freeze" at any point using the "precession" and "location" controls. Transposition is in hundredths of a half-tone.;
+#X obj 569 184 tabwrite~ sample;
+#X connect 0 0 13 0;
+#X connect 1 0 50 0;
+#X connect 2 0 10 1;
+#X connect 3 0 10 0;
+#X connect 4 0 7 0;
+#X connect 7 0 8 0;
+#X connect 8 0 5 0;
+#X connect 8 0 5 1;
+#X connect 9 0 3 0;
+#X connect 9 1 2 0;
+#X connect 11 0 3 0;
+#X connect 12 0 50 0;
+#X connect 14 0 15 0;
+#X connect 15 0 0 0;
+#X connect 19 0 24 0;
+#X connect 20 0 19 0;
+#X connect 21 0 20 0;
+#X connect 27 0 8 1;
+#X connect 28 0 27 0;
+#X connect 42 0 43 0;
+#X connect 43 0 44 0;
diff --git a/pd/doc/4.fft.examples/10.phaselockedvoc.pd b/pd/doc/4.fft.examples/10.phaselockedvoc.pd
new file mode 100644
index 00000000..7eea2f57
--- /dev/null
+++ b/pd/doc/4.fft.examples/10.phaselockedvoc.pd
@@ -0,0 +1,444 @@
+#N canvas 117 53 719 544 12;
+#X floatatom 279 354 0 0 0;
+#X msg 563 169 bang;
+#X floatatom 186 351 0 0 0;
+#X floatatom 84 350 0 0 0;
+#N canvas 5 5 986 679 fft-analysis 0;
+#X obj 177 242 *~;
+#X obj 146 242 *~;
+#X obj 146 264 -~;
+#X obj 242 243 *~;
+#X obj 211 243 *~;
+#X obj 211 265 +~;
+#X obj 256 73 *~;
+#X obj 225 73 *~;
+#X obj 197 73 *~;
+#X obj 166 73 *~;
+#X obj 166 95 +~;
+#X obj 136 124 *~;
+#X obj 49 560 *~;
+#X obj 365 179 rfft~;
+#X obj 291 34 rfft~;
+#X obj 490 598 *~;
+#X obj 685 288 r window-size;
+#X obj 751 201 r sample-rate;
+#X obj 609 234 f;
+#X obj 603 52 r sample-rate;
+#X obj 578 29 r window-size;
+#X obj 601 98 t b f;
+#X obj 578 121 /;
+#X obj 520 598 *~;
+#X obj 50 540 *~;
+#X obj 66 517 rifft~;
+#X obj 50 586 outlet~;
+#X obj 625 446 print~;
+#X msg 624 418 bang;
+#X text 115 517 inverse real FFT;
+#X obj 589 210 bang~;
+#X obj 516 424 line~;
+#X obj 578 143 * 1000;
+#X text 630 136 window size (msec);
+#X obj 603 76 * 4;
+#X obj 632 159 r speed;
+#X obj 709 160 r location;
+#X obj 640 235 +;
+#X obj 633 200 *;
+#X msg 710 188 0;
+#X obj 609 341 +;
+#X obj 600 298 t f f;
+#X msg 516 396 \$1 \, \$2 \$3;
+#X obj 516 369 pack 0 0 0;
+#X obj 752 229 / 1000;
+#X obj 609 269 *;
+#X text 639 270 reading location (samples);
+#X obj 637 381 / 4;
+#X text 669 386 hop size (samples);
+#X obj 564 464 sig~;
+#X obj 534 463 +~;
+#X text 638 82 (overlap times parent SR);
+#X text 638 69 local sample rate;
+#X obj 103 10 tabreceive~ phase-real;
+#X obj 106 124 *~;
+#X obj 106 146 +~;
+#X obj 106 170 rsqrt~;
+#X obj 225 95 -~;
+#X obj 147 200 *~;
+#X obj 207 200 *~;
+#X obj 266 9 tabreceive~ phase-imag;
+#X obj 31 481 sig~;
+#X obj 14 411 t b f;
+#X msg 14 432 1;
+#X obj 28 455 /;
+#X obj 187 496 tabsend~ phase-real;
+#X obj 288 474 tabsend~ phase-imag;
+#X obj 50 71 sig~ 1.5e-20;
+#X obj 632 180 * 0.01;
+#X obj 691 212 s speed;
+#X obj 474 263 s see-location;
+#X obj 598 618 block~ 2048 4;
+#X floatatom 672 360 0 0 0;
+#X obj 670 339 *;
+#X obj 829 234 r transpo;
+#X obj 829 256 * 0.01;
+#X obj 831 277 + 69;
+#X obj 832 298 mtof;
+#X obj 832 320 / 440;
+#X obj 689 316 t b f;
+#X obj 14 390 r window-size;
+#X floatatom 835 353 0 0 0;
+#X obj 551 543 tabreceive~ hanning;
+#X obj 534 489 tabread4~ sample;
+#X obj 516 515 tabread4~ sample;
+#X obj 145 422 -~;
+#X obj 249 385 r lock;
+#X obj 162 291 ../../extra/lrshift~ 1;
+#X obj 154 315 ../../extra/lrshift~ -1;
+#X obj 227 339 ../../extra/lrshift~ 1;
+#X obj 219 363 ../../extra/lrshift~ -1;
+#X obj 210 432 -~;
+#X obj 161 397 *~ 0;
+#X obj 226 408 *~ 0;
+#X connect 0 0 2 1;
+#X connect 1 0 2 0;
+#X connect 2 0 87 0;
+#X connect 2 0 88 0;
+#X connect 2 0 85 0;
+#X connect 3 0 5 1;
+#X connect 4 0 5 0;
+#X connect 5 0 89 0;
+#X connect 5 0 90 0;
+#X connect 5 0 91 0;
+#X connect 6 0 57 1;
+#X connect 7 0 57 0;
+#X connect 8 0 10 1;
+#X connect 9 0 10 0;
+#X connect 10 0 58 1;
+#X connect 10 0 54 0;
+#X connect 10 0 54 1;
+#X connect 11 0 55 1;
+#X connect 12 0 26 0;
+#X connect 13 0 1 1;
+#X connect 13 0 3 1;
+#X connect 13 1 0 1;
+#X connect 13 1 4 1;
+#X connect 14 0 9 1;
+#X connect 14 0 7 1;
+#X connect 14 1 6 1;
+#X connect 14 1 8 1;
+#X connect 15 0 14 0;
+#X connect 16 0 73 0;
+#X connect 17 0 44 0;
+#X connect 18 0 37 0;
+#X connect 18 0 45 0;
+#X connect 18 0 70 0;
+#X connect 19 0 34 0;
+#X connect 20 0 22 0;
+#X connect 21 0 22 0;
+#X connect 21 1 22 1;
+#X connect 22 0 32 0;
+#X connect 23 0 13 0;
+#X connect 24 0 12 0;
+#X connect 25 0 24 1;
+#X connect 28 0 27 0;
+#X connect 30 0 18 0;
+#X connect 31 0 27 0;
+#X connect 31 0 50 0;
+#X connect 31 0 84 0;
+#X connect 32 0 43 2;
+#X connect 32 0 38 1;
+#X connect 34 0 21 0;
+#X connect 35 0 68 0;
+#X connect 36 0 39 0;
+#X connect 36 0 18 1;
+#X connect 37 0 18 1;
+#X connect 38 0 37 1;
+#X connect 39 0 69 0;
+#X connect 40 0 43 1;
+#X connect 41 0 43 0;
+#X connect 41 1 40 0;
+#X connect 42 0 31 0;
+#X connect 43 0 42 0;
+#X connect 44 0 45 1;
+#X connect 45 0 41 0;
+#X connect 47 0 49 0;
+#X connect 49 0 50 1;
+#X connect 50 0 83 0;
+#X connect 53 0 9 0;
+#X connect 53 0 6 0;
+#X connect 54 0 55 0;
+#X connect 55 0 56 0;
+#X connect 56 0 58 0;
+#X connect 56 0 59 0;
+#X connect 57 0 59 1;
+#X connect 57 0 11 0;
+#X connect 57 0 11 1;
+#X connect 58 0 1 0;
+#X connect 58 0 4 0;
+#X connect 59 0 0 0;
+#X connect 59 0 3 0;
+#X connect 60 0 7 0;
+#X connect 60 0 8 0;
+#X connect 61 0 24 0;
+#X connect 62 0 63 0;
+#X connect 62 1 64 1;
+#X connect 63 0 64 0;
+#X connect 64 0 61 0;
+#X connect 67 0 10 0;
+#X connect 68 0 38 0;
+#X connect 72 0 47 0;
+#X connect 72 0 40 1;
+#X connect 73 0 72 0;
+#X connect 74 0 75 0;
+#X connect 75 0 76 0;
+#X connect 76 0 77 0;
+#X connect 77 0 78 0;
+#X connect 78 0 79 0;
+#X connect 78 0 81 0;
+#X connect 79 0 73 0;
+#X connect 79 1 73 1;
+#X connect 80 0 62 0;
+#X connect 82 0 23 1;
+#X connect 82 0 15 1;
+#X connect 82 0 12 1;
+#X connect 83 0 23 0;
+#X connect 84 0 15 0;
+#X connect 85 0 65 0;
+#X connect 85 0 25 0;
+#X connect 86 0 93 1;
+#X connect 86 0 92 1;
+#X connect 87 0 92 0;
+#X connect 88 0 92 0;
+#X connect 89 0 93 0;
+#X connect 90 0 93 0;
+#X connect 91 0 66 0;
+#X connect 91 0 25 1;
+#X connect 92 0 85 1;
+#X connect 93 0 91 1;
+#X restore 37 422 pd fft-analysis;
+#X obj 37 508 dac~;
+#N canvas 260 23 647 768 phase-tables 0;
+#N canvas 0 0 450 300 graph2 0;
+#X array phase-imag 4096 float 0;
+#X coords 0 1 4096 -1 400 300 1;
+#X restore 172 290 graph;
+#N canvas 0 0 450 300 graph3 0;
+#X array phase-real 4096 float 0;
+#X coords 0 1 4096 -1 400 300 1;
+#X restore 170 17 graph;
+#X msg 167 564 \; phase-real resize 4096 \; phase-imag resize 4096
+;
+#X restore 419 489 pd phase-tables;
+#X obj 37 448 hip~ 5;
+#X obj 37 478 *~;
+#N canvas 249 280 600 398 loc&precess 0;
+#X floatatom 160 229 0 0 0;
+#X msg 270 175 set \$1;
+#X obj 269 207 outlet;
+#X obj 83 267 outlet;
+#X obj 171 71 r location;
+#X msg 82 235 set \$1;
+#X obj 269 149 r speed;
+#X obj 55 70 r see-location;
+#X obj 67 102 t b f;
+#X obj 82 214 f;
+#X obj 54 127 int;
+#X obj 54 151 sel 0;
+#X msg 153 102 1;
+#X msg 201 101 0;
+#X obj 110 179 del 300;
+#X connect 1 0 2 0;
+#X connect 4 0 8 0;
+#X connect 5 0 3 0;
+#X connect 6 0 1 0;
+#X connect 7 0 8 0;
+#X connect 8 0 10 0;
+#X connect 8 1 9 1;
+#X connect 9 0 5 0;
+#X connect 10 0 11 0;
+#X connect 11 0 12 0;
+#X connect 11 0 14 0;
+#X connect 12 0 10 1;
+#X connect 13 0 10 1;
+#X connect 14 0 13 0;
+#X connect 14 0 9 0;
+#X restore 84 322 pd loc&precess;
+#N canvas 0 0 600 400 setlocprecess 0;
+#X obj 173 82 inlet;
+#X obj 73 80 inlet;
+#X obj 169 105 s speed;
+#X obj 74 105 s location;
+#X connect 0 0 2 0;
+#X connect 1 0 3 0;
+#X restore 84 373 pd setlocprecess;
+#X msg 41 333 0;
+#X obj 606 182 adc~;
+#X obj 279 379 s transpo;
+#X obj 277 305 r transpo;
+#X msg 277 331 set \$1;
+#X msg 404 287 \; location 0 \; speed 200;
+#N canvas 138 111 767 761 hanning-window 0;
+#X obj 92 206 phasor~;
+#X obj 92 234 cos~;
+#X obj 23 328 tabwrite~ hanning;
+#X obj 30 252 -~;
+#X obj 28 218 sig~ 1;
+#X msg 37 180 0;
+#X text 141 13 CALCULATE HANNING;
+#X text 141 27 WINDOW TABLE;
+#N canvas 0 0 450 300 graph1 0;
+#X array hanning 4096 float 0;
+#X coords 0 1 4096 -1 400 300 1;
+#X restore 275 281 graph;
+#X obj 93 171 sig~;
+#X text 175 148 sample rate / window size;
+#X msg 23 144 bang;
+#X obj 66 269 sig~ 0.5;
+#X obj 49 300 *~;
+#X obj 94 80 samplerate~;
+#X obj 25 23 r window-size;
+#X obj 25 53 t b f;
+#X msg 275 547 \; hanning resize 4096;
+#X obj 93 132 /;
+#X connect 0 0 1 0;
+#X connect 1 0 3 1;
+#X connect 3 0 13 0;
+#X connect 4 0 3 0;
+#X connect 5 0 0 1;
+#X connect 9 0 0 0;
+#X connect 11 0 2 0;
+#X connect 11 0 5 0;
+#X connect 12 0 13 1;
+#X connect 13 0 2 0;
+#X connect 14 0 18 0;
+#X connect 15 0 16 0;
+#X connect 16 0 14 0;
+#X connect 16 0 11 0;
+#X connect 16 1 18 1;
+#X connect 18 0 9 0;
+#X restore 417 512 pd hanning-window;
+#X msg 25 192 \; window-size 2048 \; transpo 0 \; pd dsp 1;
+#X floatatom 587 68 0 0 0;
+#N canvas 194 37 397 591 output 0;
+#X obj 62 191 t b;
+#X obj 62 144 f;
+#X obj 62 96 inlet;
+#X text 67 76 mute;
+#X obj 62 215 f;
+#X msg 127 230 0;
+#X msg 62 120 bang;
+#X obj 62 167 moses 1;
+#X obj 127 207 t b f;
+#X obj 91 417 outlet;
+#X msg 91 393 set \$1;
+#X obj 175 154 moses 1;
+#X obj 212 419 dbtorms;
+#X obj 212 443 pack 0 100;
+#X obj 175 130 r master-lvl;
+#X obj 91 360 r master-lvl;
+#X obj 78 269 s master-lvl;
+#X obj 212 467 s master-amp;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X restore 587 44 pd output;
+#X msg 587 20 mute;
+#X text 624 60 MASTER;
+#X text 623 74 LEVEL;
+#X obj 587 92 s master-lvl;
+#X text 35 173 click here first;
+#X obj 81 506 line~;
+#X obj 81 482 r master-amp;
+#X text 49 271 set location;
+#X text 48 285 and stop;
+#X text 48 299 precession;
+#X text 166 270 precession;
+#X text 165 286 speed in;
+#X text 165 300 hundredths;
+#X text 275 264 transposition;
+#X text 276 282 in cents;
+#X text 512 242 examples;
+#X text 407 266 contraction;
+#X text 623 265 expansion;
+#X msg 612 288 \; location 0 \; speed 10;
+#X obj 419 360 loadbang;
+#X obj 423 383 samplerate~;
+#X obj 419 405 s sample-rate;
+#N canvas 132 255 846 368 insample 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array sample 92615 float 0;
+#X coords 0 1 92614 -1 400 150 1;
+#X restore 406 155 graph;
+#X obj 12 19 r read-sample;
+#X obj 12 132 unpack s f;
+#X obj 46 158 s insamprate;
+#X obj 12 208 soundfiler;
+#X msg 12 184 read -resize \$1 sample;
+#X obj 12 238 s insamplength;
+#X msg 20 289 \; sample resize 220500 \; insamplength 220500;
+#X obj 22 46 inlet;
+#X obj 22 73 openpanel;
+#X obj 22 101 pack s 50;
+#X connect 1 0 2 0;
+#X connect 2 0 5 0;
+#X connect 2 1 3 0;
+#X connect 4 0 6 0;
+#X connect 5 0 4 0;
+#X connect 8 0 9 0;
+#X connect 9 0 10 0;
+#X connect 10 0 2 0;
+#X restore 419 463 pd insample;
+#X msg 190 163 \; read-sample ../sound/bell.aiff \; transpo 0;
+#X msg 190 217 \; read-sample ../sound/voice.wav \; transpo -530;
+#X obj 563 204 tabwrite~ sample;
+#X obj 583 411 s lock;
+#X floatatom 583 386 0 0 0;
+#X msg 584 359 0;
+#X msg 618 358 1;
+#X text 135 18 PHASE LOCKING VOCODER;
+#X text 32 46 This is a spin on the phase vocoder which prevents beating
+between adjacent channels of the FFT. This is described in "Phase Locked
+Vocoder" (reprinted on MSP's web page) and further improved by Laroche
+and Dolson \, reported in ICMC97.;
+#X obj 419 440 bng 15 250 50 0 empty empty read-sample 20 8 0 12 -262144
+-1 -1;
+#X text 528 265 normal;
+#X msg 507 288 \; location 0 \; speed 100;
+#X connect 0 0 13 0;
+#X connect 1 0 46 0;
+#X connect 2 0 10 1;
+#X connect 3 0 10 0;
+#X connect 4 0 7 0;
+#X connect 7 0 8 0;
+#X connect 8 0 5 0;
+#X connect 8 0 5 1;
+#X connect 9 0 3 0;
+#X connect 9 1 2 0;
+#X connect 11 0 3 0;
+#X connect 12 0 46 0;
+#X connect 14 0 15 0;
+#X connect 15 0 0 0;
+#X connect 19 0 24 0;
+#X connect 20 0 19 0;
+#X connect 21 0 20 0;
+#X connect 26 0 8 1;
+#X connect 27 0 26 0;
+#X connect 40 0 41 0;
+#X connect 41 0 42 0;
+#X connect 48 0 47 0;
+#X connect 49 0 48 0;
+#X connect 50 0 48 0;
+#X connect 53 0 43 0;
diff --git a/pd/doc/4.fft.examples/11.pianorev.pd b/pd/doc/4.fft.examples/11.pianorev.pd
new file mode 100644
index 00000000..ad7f8e7f
--- /dev/null
+++ b/pd/doc/4.fft.examples/11.pianorev.pd
@@ -0,0 +1,378 @@
+#N canvas 48 26 570 543 12;
+#N canvas 52 71 774 520 previous-analysis 0;
+#X graph graph1 0 -1 2048 1 103 165 303 15;
+#X array last-real 2048 float;
+#X pop;
+#X graph graph2 0 -1 2048 1 497 156 697 6;
+#X array last-imag 2048 float;
+#X pop;
+#X graph graph1 0 -1 2048 1 105 335 305 185;
+#X array phase-real 2048 float;
+#X pop;
+#X graph graph2 0 -1 2048 1 501 328 701 178;
+#X array phase-imag 2048 float;
+#X pop;
+#X msg 106 541 \; last-real resize 2048 \; last-imag resize 2048 \; phase-real resize 2048 \; phase-imag resize 2048 \; inc-real resize 2048 \; inc-imag resize 2048;
+#X graph graph1 0 -1 2048 1 105 507 305 357;
+#X array inc-real 2048 float;
+#X pop;
+#X graph graph2 0 -1 2048 1 503 492 703 342;
+#X array inc-imag 2048 float;
+#X pop;
+#X restore 411 384 pd previous-analysis;
+#N canvas 29 2 866 679 fft 0;
+#X obj 14 640 *~;
+#X obj 422 576 *~;
+#X obj 396 576 *~;
+#X obj 480 576 *~;
+#X obj 455 576 *~;
+#X obj 455 598 +~;
+#X obj 395 601 -~;
+#X obj 138 480 *~;
+#X obj 531 332 -~;
+#X obj 505 528 +~;
+#X obj 531 528 *~;
+#X obj 458 330 -~;
+#X obj 431 526 +~;
+#X obj 458 526 *~;
+#X obj 159 336 -~;
+#X obj 133 373 +~;
+#X obj 159 373 *~;
+#X obj 201 138 *~;
+#X obj 172 138 *~;
+#X obj 172 159 +~;
+#X obj 351 288 *~;
+#X obj 325 288 *~;
+#X obj 404 115 *~;
+#X obj 379 115 *~;
+#X obj 336 116 *~;
+#X obj 311 116 *~;
+#X obj 181 408 *~;
+#X obj 154 409 *~;
+#X obj 198 37 *~;
+#X obj 14 662 outlet~;
+#X obj 14 615 *~;
+#X obj 18 16 tabreceive~ hanning-window;
+#X obj 537 561 block~ 2048 4;
+#X obj 214 12 inlet~;
+#X obj 198 58 rfft~;
+#X obj 30 591 rifft~;
+#X obj 30 99 tabsend~ last-real;
+#X obj 30 130 tabsend~ last-imag;
+#X obj 154 431 rsqrt~;
+#X obj 311 138 +~;
+#X obj 379 137 -~;
+#X obj 35 255 tabreceive~ inc-real;
+#X obj 69 278 tabreceive~ inc-imag;
+#X obj 430 202 tabreceive~ phase-real;
+#X obj 502 225 tabreceive~ phase-imag;
+#X obj 325 309 +~;
+#X obj 75 335 -~;
+#X obj 354 40 tabreceive~ last-real;
+#X obj 414 62 tabreceive~ last-imag;
+#X obj 48 372 +~;
+#X obj 75 372 *~;
+#X obj 51 479 *~;
+#X obj 191 454 r gain;
+#X obj 46 560 tabsend~ inc-real;
+#X obj 64 519 tabsend~ inc-imag;
+#X obj 391 649 tabsend~ phase-real;
+#X obj 453 620 tabsend~ phase-imag;
+#X obj 52 618 sig~ 0.0002;
+#X obj 60 217 *~ 0;
+#X obj 154 453 *~ 0;
+#N canvas 167 161 699 396 decision 0;
+#X obj 63 32 inlet~;
+#X obj 64 272 outlet~;
+#X obj 64 114 -~;
+#X obj 64 157 clip~ 0 1;
+#X obj 64 135 *~ 1e+20;
+#X obj 135 31 inlet~;
+#X text 139 136 1 if new sig;
+#X text 138 147 stronger than;
+#X text 140 159 old one;
+#X obj 245 130 -~;
+#X obj 259 105 ../../extra/lrshift~ 1;
+#X obj 245 178 clip~ 0 1;
+#X obj 245 156 *~ 1e+20;
+#X obj 421 130 -~;
+#X obj 421 178 clip~ 0 1;
+#X obj 421 156 *~ 1e+20;
+#X obj 435 105 ../../extra/lrshift~ -1;
+#X obj 64 214 *~;
+#X obj 64 243 *~;
+#X connect 0 0 2 0;
+#X connect 0 0 10 0;
+#X connect 0 0 9 0;
+#X connect 0 0 13 0;
+#X connect 0 0 16 0;
+#X connect 2 0 4 0;
+#X connect 3 0 17 0;
+#X connect 4 0 3 0;
+#X connect 5 0 2 1;
+#X connect 9 0 12 0;
+#X connect 10 0 9 1;
+#X connect 11 0 17 1;
+#X connect 12 0 11 0;
+#X connect 13 0 15 0;
+#X connect 14 0 18 1;
+#X connect 15 0 14 0;
+#X connect 16 0 13 1;
+#X connect 17 0 18 0;
+#X connect 18 0 1 0;
+#X restore 253 334 pd decision;
+#X obj 391 626 +~ 1e-15;
+#X obj 46 540 +~ 1e-15;
+#X connect 0 0 29 0;
+#X connect 1 0 6 1;
+#X connect 2 0 6 0;
+#X connect 3 0 5 1;
+#X connect 4 0 5 0;
+#X connect 5 0 56 0;
+#X connect 6 0 61 0;
+#X connect 7 0 54 0;
+#X connect 7 0 1 0;
+#X connect 7 0 3 0;
+#X connect 8 0 10 0;
+#X connect 9 0 35 1;
+#X connect 9 0 1 1;
+#X connect 9 0 4 1;
+#X connect 10 0 9 1;
+#X connect 11 0 13 0;
+#X connect 12 0 35 0;
+#X connect 12 0 2 1;
+#X connect 12 0 3 1;
+#X connect 13 0 12 1;
+#X connect 14 0 16 0;
+#X connect 15 0 26 0;
+#X connect 15 0 26 1;
+#X connect 15 0 7 0;
+#X connect 16 0 15 1;
+#X connect 17 0 19 1;
+#X connect 18 0 19 0;
+#X connect 19 0 60 0;
+#X connect 20 0 45 1;
+#X connect 21 0 45 0;
+#X connect 22 0 40 1;
+#X connect 23 0 40 0;
+#X connect 24 0 39 1;
+#X connect 25 0 39 0;
+#X connect 26 0 38 0;
+#X connect 27 0 38 0;
+#X connect 28 0 34 0;
+#X connect 30 0 0 0;
+#X connect 31 0 28 0;
+#X connect 31 0 30 0;
+#X connect 33 0 28 1;
+#X connect 34 0 25 0;
+#X connect 34 0 22 0;
+#X connect 34 0 18 0;
+#X connect 34 0 18 1;
+#X connect 34 0 11 0;
+#X connect 34 0 36 0;
+#X connect 34 1 24 0;
+#X connect 34 1 23 0;
+#X connect 34 1 17 0;
+#X connect 34 1 17 1;
+#X connect 34 1 8 0;
+#X connect 34 1 37 0;
+#X connect 35 0 30 1;
+#X connect 38 0 59 0;
+#X connect 39 0 46 0;
+#X connect 39 0 58 0;
+#X connect 40 0 14 0;
+#X connect 41 0 49 0;
+#X connect 41 0 46 1;
+#X connect 42 0 15 0;
+#X connect 42 0 14 1;
+#X connect 43 0 21 0;
+#X connect 43 0 21 1;
+#X connect 43 0 12 0;
+#X connect 43 0 11 1;
+#X connect 44 0 20 0;
+#X connect 44 0 20 1;
+#X connect 44 0 9 0;
+#X connect 44 0 8 1;
+#X connect 45 0 60 1;
+#X connect 46 0 50 0;
+#X connect 47 0 25 1;
+#X connect 47 0 23 1;
+#X connect 48 0 24 1;
+#X connect 48 0 22 1;
+#X connect 49 0 27 0;
+#X connect 49 0 27 1;
+#X connect 49 0 51 0;
+#X connect 50 0 49 1;
+#X connect 51 0 2 0;
+#X connect 51 0 4 0;
+#X connect 51 0 62 0;
+#X connect 52 0 59 1;
+#X connect 57 0 0 1;
+#X connect 58 0 37 0;
+#X connect 58 0 36 0;
+#X connect 59 0 51 1;
+#X connect 59 0 7 1;
+#X connect 60 0 50 1;
+#X connect 60 0 16 1;
+#X connect 60 0 13 1;
+#X connect 60 0 10 1;
+#X connect 61 0 55 0;
+#X connect 62 0 53 0;
+#X restore 203 438 pd fft;
+#X floatatom 47 260;
+#X obj 202 518 dac~;
+#N canvas 58 75 719 400 hanning-window 0;
+#X obj 99 231 phasor~;
+#X obj 99 267 cos~;
+#X obj 45 284 -~;
+#X obj 43 250 sig~ 1;
+#X msg 53 212 0;
+#X graph graph1 0 -1 2047 1 393 274 593 124;
+#X array hanning-window 2048 float;
+#X pop;
+#X obj 101 117 r sample-rate;
+#X obj 216 119 r window-size;
+#X obj 124 145 t b f;
+#X obj 101 167 /;
+#X obj 99 204 sig~;
+#X obj 39 71 r make-window;
+#X msg 38 145 bang;
+#X obj 31 349 tabwrite~ hanning-window;
+#X floatatom 184 204;
+#X obj 47 311 *~ 0.5;
+#X msg 371 303 \; hanning-window resize 2048;
+#X connect 0 0 1 0;
+#X connect 1 0 2 1;
+#X connect 2 0 15 0;
+#X connect 3 0 2 0;
+#X connect 4 0 0 1;
+#X connect 6 0 9 0;
+#X connect 7 0 8 0;
+#X connect 8 0 9 0;
+#X connect 8 1 9 1;
+#X connect 9 0 10 0;
+#X connect 10 0 0 0;
+#X connect 11 0 12 0;
+#X connect 12 0 4 0;
+#X connect 12 0 13 0;
+#X connect 15 0 13 0;
+#X restore 411 355 pd hanning-window;
+#X obj 410 419 loadbang;
+#X obj 202 487 *~;
+#X obj 262 464 line~;
+#X obj 47 464 s gain;
+#X msg 47 336 0;
+#X msg 410 445 \; sample-rate 44100 \; window-size 2048 \; make-window bang \; gain 0 \;;
+#X obj 92 341 t b b f;
+#X obj 119 394 /;
+#X msg 89 394 1;
+#X obj 89 417 -;
+#X floatatom 47 442;
+#X obj 47 288 * 0.1;
+#X floatatom 480 78;
+#N canvas 194 37 397 591 output 0;
+#X obj 62 191 t b;
+#X obj 62 144 f;
+#X obj 62 96 inlet;
+#X text 67 76 mute;
+#X obj 62 215 f;
+#X msg 127 230 0;
+#X msg 62 120 bang;
+#X obj 62 167 moses 1;
+#X obj 127 207 t b f;
+#X obj 91 417 outlet;
+#X msg 91 393 set \$1;
+#X obj 175 154 moses 1;
+#X obj 212 419 dbtorms;
+#X obj 212 443 pack 0 100;
+#X obj 175 130 r master-lvl;
+#X obj 91 360 r master-lvl;
+#X obj 78 269 s master-lvl;
+#X obj 212 467 s master-amp;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X restore 480 54 pd output;
+#X msg 480 30 mute;
+#X text 517 70 MASTER;
+#X text 516 84 LEVEL;
+#X obj 480 102 s master-lvl;
+#X obj 262 437 r master-amp;
+#X obj 203 339 osc~;
+#X obj 282 394 line~;
+#X msg 270 268 bang;
+#X floatatom 203 316;
+#X obj 203 415 *~;
+#X obj 271 343 pack 1 0;
+#X obj 316 370 pack 0 0;
+#X obj 301 316 del;
+#X floatatom 338 283;
+#X obj 203 293 mtof;
+#X floatatom 203 269;
+#X obj 203 364 cos~;
+#X obj 202 461 hip~ 5;
+#X obj 203 388 hip~ 20;
+#X obj 48 313 moses 0.1;
+#X msg 114 366 0.005;
+#X text 131 9 PIANO REVERB;
+#X text 42 29 This is intended as an imitation of how the strings of a piano can act as a reverberator. The sound is more "pitched" and less "whispered" than a standard delay-based reverberator (or for that matter \, than a reverberant room.);
+#X text 41 88 Many pieces in the literature call for just this effect \, notably Boulez's Dialogue de l'ombre double.;
+#X text 40 118 This particular algorithm is actually TOO clean sounding and would have to be chorused to make a serviceable imitation. However \, it might be interesting in its own right.;
+#X text 39 173 The technique is to run a phase vocoder \, but only to "punch" the incoming sound into channels where (1) there's a peak \, and (2) the incoming sound drowns out whatever might already be there.;
+#X text 27 238 reverb time;
+#X text 179 248 test pitch;
+#X text 267 249 trigger;
+#X text 334 264 duration;
+#X connect 1 0 36 0;
+#X connect 2 0 16 0;
+#X connect 5 0 10 0;
+#X connect 6 0 3 0;
+#X connect 6 0 3 1;
+#X connect 7 0 6 1;
+#X connect 9 0 15 0;
+#X connect 11 0 13 0;
+#X connect 11 1 39 0;
+#X connect 11 2 12 1;
+#X connect 12 0 14 1;
+#X connect 13 0 14 0;
+#X connect 14 0 15 0;
+#X connect 15 0 8 0;
+#X connect 16 0 38 0;
+#X connect 17 0 22 0;
+#X connect 18 0 17 0;
+#X connect 19 0 18 0;
+#X connect 23 0 7 0;
+#X connect 24 0 35 0;
+#X connect 25 0 28 1;
+#X connect 26 0 29 0;
+#X connect 26 0 31 0;
+#X connect 27 0 24 0;
+#X connect 28 0 1 0;
+#X connect 29 0 25 0;
+#X connect 30 0 25 0;
+#X connect 31 0 30 0;
+#X connect 32 0 31 1;
+#X connect 32 0 29 1;
+#X connect 32 0 30 1;
+#X connect 33 0 27 0;
+#X connect 34 0 33 0;
+#X connect 35 0 37 0;
+#X connect 36 0 6 0;
+#X connect 37 0 28 0;
+#X connect 38 0 9 0;
+#X connect 38 1 11 0;
+#X connect 39 0 12 0;
diff --git a/pd/doc/4.fft.examples/12.sinedecomposer.pd b/pd/doc/4.fft.examples/12.sinedecomposer.pd
new file mode 100644
index 00000000..52fb5eb9
--- /dev/null
+++ b/pd/doc/4.fft.examples/12.sinedecomposer.pd
@@ -0,0 +1,308 @@
+#N struct peak-template float x float y float amp float ampreal float
+ampimag;
+#N canvas 251 262 858 492 12;
+#X msg 501 258 bang;
+#X obj 30 360 pack 0 100;
+#X obj 30 384 line~;
+#X obj 30 336 dbtorms;
+#N canvas 95 102 724 400 fft 0;
+#X obj 64 67 inlet~;
+#X obj 134 107 print~;
+#X msg 137 71 bang;
+#X obj 64 104 rfft~;
+#N canvas 0 0 450 300 graph3 0;
+#X array array3 4096 float 0;
+#X coords 0 100 4096 -100 400 150 1;
+#X restore 254 14 graph;
+#N canvas 0 0 450 300 graph4 0;
+#X array array4 4096 float 0;
+#X coords 0 100 4096 -100 400 150 1;
+#X restore 256 165 graph;
+#X obj 9 185 tabsend~ array3;
+#X obj 50 158 tabsend~ array4;
+#X msg 37 246 \; array3 resize 4096 \; array4 resize 4096;
+#X obj 62 38 block~ 4096 1;
+#X connect 0 0 1 0;
+#X connect 0 0 3 0;
+#X connect 2 0 1 0;
+#X connect 3 0 6 0;
+#X connect 3 1 7 0;
+#X restore 134 409 pd fft;
+#X obj 30 408 *~;
+#X obj 29 435 dac~;
+#X obj 476 231 adc~;
+#N canvas 204 36 521 368 analysis 0;
+#X obj 206 37 inlet;
+#X msg 207 68 bang;
+#X obj 275 52 r snapshot;
+#X msg 220 169 4096 array3 array4 50;
+#X obj 127 252 print;
+#X obj 206 103 t b b b;
+#X obj 122 140 s done-analysis;
+#X obj 248 133 s start-analysis;
+#X obj 220 219 s found-peak;
+#X obj 154 194 r loud;
+#X obj 128 226 spigot;
+#X obj 220 193 ../../extra/pique;
+#X connect 0 0 1 0;
+#X connect 1 0 5 0;
+#X connect 2 0 1 0;
+#X connect 3 0 11 0;
+#X connect 5 0 6 0;
+#X connect 5 1 3 0;
+#X connect 5 2 7 0;
+#X connect 9 0 10 1;
+#X connect 10 0 4 0;
+#X connect 11 0 8 0;
+#X connect 11 0 10 0;
+#X restore 613 295 pd analysis;
+#X obj 30 313 r loop-amp;
+#X msg 613 271 snapshot;
+#X text 10 215 click here first;
+#X text 613 247 analysis;
+#N canvas 36 255 884 389 peak-saver 0;
+#X floatatom 710 310 0 0 0;
+#X floatatom 633 309 0 0 0;
+#X floatatom 560 305 0 0 0;
+#X floatatom 484 303 0 0 0;
+#X obj 406 251 pointer;
+#X obj 354 150 pointer;
+#X msg 374 60 bang;
+#X obj 142 243 rmstodb;
+#X obj 10 244 * 0.1;
+#X obj 141 268 * -3;
+#X floatatom 416 300 0 0 0;
+#X obj 353 36 r start-analysis;
+#X obj 354 89 t b b;
+#X msg 29 89 50 60 70;
+#X obj 28 120 append peak-template x y amp;
+#X msg 426 225 next;
+#X obj 402 276 get peak-template x y amp ampreal ampimag;
+#X obj 9 293 append peak-template x y amp ampreal ampimag;
+#X obj 116 167 r found-peak;
+#X obj 117 196 unpack 0 0 0 0 0;
+#X msg 76 244 330;
+#X msg 400 87 \; pd-peak-list clear;
+#X msg 354 125 traverse pd-peak-list \, bang;
+#X msg 408 201 traverse pd-peak-list \, next;
+#X connect 4 0 16 0;
+#X connect 5 0 14 3;
+#X connect 5 0 17 5;
+#X connect 6 0 12 0;
+#X connect 7 0 9 0;
+#X connect 8 0 17 0;
+#X connect 9 0 17 2;
+#X connect 11 0 12 0;
+#X connect 12 0 22 0;
+#X connect 12 1 21 0;
+#X connect 13 0 14 0;
+#X connect 15 0 4 0;
+#X connect 16 0 10 0;
+#X connect 16 1 3 0;
+#X connect 16 2 2 0;
+#X connect 16 3 1 0;
+#X connect 16 4 0 0;
+#X connect 18 0 19 0;
+#X connect 19 1 8 0;
+#X connect 19 2 20 0;
+#X connect 19 2 7 0;
+#X connect 19 3 17 3;
+#X connect 19 4 17 4;
+#X connect 20 0 17 1;
+#X connect 22 0 5 0;
+#X connect 23 0 4 0;
+#X restore 339 378 pd peak-saver;
+#N canvas 231 169 656 237 peak-template 0;
+#X obj 45 90 filledpolygon 3 3 3 0 0 0 amp 0 0;
+#X obj 37 16 struct peak-template float x float y float amp float ampreal
+float ampimag;
+#X restore 339 402 pd peak-template;
+#N canvas 0 0 600 382 peak-list 1;
+#X scalar peak-template 6.54702 330 -153.451 -0.00206937 -0.00295808
+\;;
+#X scalar peak-template 13.0873 330 -226.384 0.0527068 -0.0271798 \;
+;
+#X scalar peak-template 26.0747 330 -193.145 -0.004041 0.0160602 \;
+;
+#X scalar peak-template 35.3241 330 -136.068 -0.00162406 0.000891527
+\;;
+#X scalar peak-template 39.3259 330 -172.59 0.00700493 0.00274829 \;
+;
+#X scalar peak-template 50.8632 330 -126.917 0.000847277 0.000991251
+\;;
+#X scalar peak-template 58.0851 330 -130.284 -0.00125865 -0.000785961
+\;;
+#X scalar peak-template 61.7223 330 -118.759 -0.000665894 -0.000682444
+\;;
+#X scalar peak-template 64.533 330 -123.671 -1.31387e-05 0.00115121
+\;;
+#X scalar peak-template 70.7855 330 -192.911 -0.0156921 0.00481118
+\;;
+#X scalar peak-template 83.2529 330 -128.585 0.00138656 -0.000101125
+\;;
+#X scalar peak-template 87.7921 330 -102.858 -0.00011618 -0.000504767
+\;;
+#X scalar peak-template 94.7598 330 -96.8563 -6.587e-06 -0.000411354
+\;;
+#X scalar peak-template 238.251 330 -157.634 -0.00175515 0.00385829
+\;;
+#X scalar peak-template 120.177 330 -149.995 -0.000220136 -0.00315404
+\;;
+#X scalar peak-template 127.613 330 -115.813 0.000761733 0.000380684
+\;;
+#X scalar peak-template 133.372 330 -93.2528 -7.56087e-05 -0.000350204
+\;;
+#X scalar peak-template 140.63 330 -114.426 0.000604011 0.000535798
+\;;
+#X scalar peak-template 147.903 330 -101.806 -0.000357742 0.000345682
+\;;
+#X scalar peak-template 151.501 330 -102.007 0.000501161 -1.29982e-05
+\;;
+#X scalar peak-template 156.14 330 -133.049 -0.00152793 -0.00062287
+\;;
+#X scalar peak-template 168.988 330 -98.4353 1.06068e-05 0.000436979
+\;;
+#X scalar peak-template 176.71 330 -80.3132 7.61609e-05 -0.000204315
+\;;
+#X scalar peak-template 189.731 330 -124.879 -0.00119891 -0.000129939
+\;;
+#X scalar peak-template 202.531 330 -84.7508 -0.000188934 0.000176472
+\;;
+#X scalar peak-template 210.532 330 -103.669 -0.000493027 -0.000206012
+\;;
+#X scalar peak-template 224.973 330 -86.5866 -0.000200658 -0.000191542
+\;;
+#X restore 339 426 pd peak-list;
+#X msg 37 235 \; pd dsp 1;
+#X floatatom 720 273 0 0 0;
+#X obj 720 296 s loud;
+#X floatatom 557 77 0 0 0;
+#N canvas 194 37 730 728 output 0;
+#X obj 77 218 t b;
+#X obj 77 154 f;
+#X obj 66 71 inlet;
+#X text 73 46 mute;
+#X obj 77 244 f;
+#X msg 156 235 0;
+#X msg 69 102 bang;
+#X obj 77 189 moses 1;
+#X obj 169 33 r loop-amp;
+#X obj 78 286 s loop-amp;
+#X obj 156 179 t b f;
+#X obj 93 462 outlet;
+#X msg 92 428 set \$1;
+#X obj 93 400 r loop-amp;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 9 0;
+#X connect 5 0 9 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 10 0;
+#X connect 8 0 1 1;
+#X connect 10 0 5 0;
+#X connect 10 1 4 1;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X restore 557 53 pd output;
+#X obj 557 104 s loop-amp;
+#X msg 557 30 mute;
+#X text 593 80 AMPLITUDE;
+#X text 539 189 live sample;
+#X text 719 234 print out;
+#X text 720 249 peak list;
+#X text 168 7 SPECTRAL SNAPSHOTS.;
+#X text 15 18 This patch reads a soundfile or records a live sound.
+When you click on "snapshot" the peak-list window shows a list of the
+sinusoidal peaks that were found at that instant in the sound. You
+can also ask for the peak lists to be printed out.;
+#N canvas 132 255 634 331 insample 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array sample 155948 float 0;
+#X coords 0 1 155947 -1 400 150 1;
+#X restore 199 18 graph;
+#X obj 19 70 r read-sample;
+#X obj 19 95 unpack s f;
+#X obj 53 121 s insamprate;
+#X obj 19 171 soundfiler;
+#X msg 19 147 read -resize \$1 sample;
+#X obj 19 201 s insamplength;
+#X msg 357 197 \; sample resize 220500 \; insamplength 220500;
+#X connect 1 0 2 0;
+#X connect 2 0 5 0;
+#X connect 2 1 3 0;
+#X connect 4 0 6 0;
+#X connect 5 0 4 0;
+#X restore 336 350 pd insample;
+#X floatatom 134 363 0 0 0;
+#N canvas 190 43 405 461 test-signal 0;
+#X obj 135 296 tabread4~ sample;
+#X obj 135 271 line~;
+#X obj 95 146 f;
+#X obj 254 46 r insamprate;
+#X obj 136 350 *~;
+#X obj 171 350 dbtorms;
+#X obj 171 327 inlet;
+#X obj 135 415 outlet~;
+#X obj 146 33 r insamplength;
+#X msg 134 247 0 \, \$1 \$2;
+#X obj 134 221 pack 0 0;
+#X obj 209 190 /;
+#X obj 299 99 * 0.001;
+#X obj 135 388 hip~ 5;
+#X obj 33 5 loadbang;
+#X text 242 13 sample playback;
+#X msg 33 25 1;
+#X obj 33 69 metro 1000;
+#X floatatom 33 48 0 0 0;
+#X obj 255 75 t b b f;
+#X obj 161 84 t b f;
+#X connect 0 0 4 0;
+#X connect 1 0 0 0;
+#X connect 2 0 10 0;
+#X connect 3 0 19 0;
+#X connect 4 0 13 0;
+#X connect 5 0 4 1;
+#X connect 6 0 5 0;
+#X connect 8 0 20 0;
+#X connect 9 0 1 0;
+#X connect 10 0 9 0;
+#X connect 11 0 10 1;
+#X connect 11 0 17 1;
+#X connect 12 0 11 1;
+#X connect 13 0 7 0;
+#X connect 14 0 16 0;
+#X connect 16 0 18 0;
+#X connect 17 0 2 0;
+#X connect 18 0 17 0;
+#X connect 19 0 16 0;
+#X connect 19 1 11 0;
+#X connect 19 2 12 0;
+#X connect 20 0 16 0;
+#X connect 20 1 11 0;
+#X connect 20 1 2 1;
+#X restore 134 386 pd test-signal;
+#X text 135 341 amplitude;
+#X text 204 212 read a sample;
+#X msg 128 231 \; read-sample ../sound/bell.aiff 44100;
+#X text 12 97 The active ingredient is "pique" in the "analysis" subwindow
+\, which is in the "extras" directory in the Pd release.;
+#X msg 124 275 \; read-sample ../sound/voice.wav 32000;
+#X obj 458 295 tabwrite~ sample;
+#X connect 0 0 37 0;
+#X connect 1 0 2 0;
+#X connect 2 0 5 0;
+#X connect 3 0 1 0;
+#X connect 5 0 6 0;
+#X connect 5 0 6 1;
+#X connect 7 0 37 0;
+#X connect 9 0 3 0;
+#X connect 10 0 8 0;
+#X connect 17 0 18 0;
+#X connect 19 0 21 0;
+#X connect 20 0 19 0;
+#X connect 22 0 20 0;
+#X connect 30 0 31 0;
+#X connect 31 0 4 0;
+#X connect 31 0 5 1;
diff --git a/pd/doc/4.fft.examples/13.partialtracer.pd b/pd/doc/4.fft.examples/13.partialtracer.pd
new file mode 100644
index 00000000..5458f900
--- /dev/null
+++ b/pd/doc/4.fft.examples/13.partialtracer.pd
@@ -0,0 +1,783 @@
+#N canvas 234 52 720 591 12;
+#X floatatom 598 72;
+#N canvas 516 98 663 557 boo 0;
+#X obj 79 108 outlet;
+#X msg 78 80 set \$1;
+#X obj 79 57 r osc-amp;
+#X connect 1 0 0 0;
+#X connect 2 0 1 0;
+#X restore 598 52 pd;
+#X obj 598 93 s osc-amp;
+#X obj 93 513 pack 0 100;
+#X obj 94 533 line~;
+#X obj 93 493 dbtorms;
+#X obj 94 573 *~;
+#X floatatom 624 225;
+#X msg 514 269 0;
+#X floatatom 567 225;
+#X floatatom 645 290;
+#X floatatom 498 71;
+#N canvas 516 98 663 555 boo 0;
+#X obj 79 108 outlet;
+#X msg 78 80 set \$1;
+#X obj 79 57 r grain-amp;
+#X connect 1 0 0 0;
+#X connect 2 0 1 0;
+#X restore 498 51 pd;
+#N canvas 201 90 556 354 analysis 0;
+#X obj 220 45 r snapshot;
+#X obj 62 283 print;
+#X obj 106 154 t b b b;
+#X obj 20 189 s done-analysis;
+#X obj 209 149 s start-analysis;
+#X obj 161 261 s found-peak;
+#X obj 107 263 r loud;
+#X obj 63 262 spigot;
+#X msg 138 101 1;
+#X obj 31 99 r fft-done;
+#X obj 31 129 spigot;
+#X msg 110 101 0;
+#X obj 222 84 del 0.01;
+#X msg 221 66 bang;
+#X text 183 103 Wait for the next FFT to come by before doing the peak search.;
+#X obj 341 176 r errthresh;
+#X msg 343 219 errthresh \$1;
+#X floatatom 341 199;
+#X obj 212 173 r window-size;
+#X obj 169 190 f;
+#X msg 162 211 \$1 fft-real fft-imag 10;
+#X obj 162 231 pique;
+#X connect 0 0 13 0;
+#X connect 2 0 3 0;
+#X connect 2 1 19 0;
+#X connect 2 2 4 0;
+#X connect 2 2 11 0;
+#X connect 6 0 7 1;
+#X connect 7 0 1 0;
+#X connect 8 0 10 1;
+#X connect 9 0 10 0;
+#X connect 10 0 2 0;
+#X connect 11 0 10 1;
+#X connect 12 0 8 0;
+#X connect 13 0 12 0;
+#X connect 15 0 17 0;
+#X connect 16 0 21 0;
+#X connect 17 0 16 0;
+#X connect 18 0 19 1;
+#X connect 19 0 20 0;
+#X connect 20 0 21 0;
+#X connect 21 0 5 0;
+#X connect 21 0 7 0;
+#X restore 270 411 pd analysis;
+#N canvas 168 244 411 214 peak-template 0;
+#X obj 62 52 filledpolygon 3 3 3 0 0 0 amp 0 0;
+#X obj 57 10 template float x float y float amp float ampreal float ampimag float used;
+#X restore 270 516 pd peak-template;
+#N canvas 0 0 600 386 peak-list 0;
+#X restore 270 454 pd peak-list;
+#N canvas 20 23 472 426 trace-list 1;
+#X restore 270 494 pd trace-list;
+#N canvas 56 84 527 179 trace-template 0;
+#X obj 93 72 plot bazoo 0 1 0 500 5;
+#X obj 96 36 template float x float y float voiceno array bazoo point-template;
+#X text 93 93 This template describes a pitch/amplitude trace. The array "bazoo" holds the actual points. In this template \, y is always 0 and x is the starting location in pixels. There are 5 pixels per point.;
+#X restore 270 537 pd trace-template;
+#N canvas 96 258 494 158 point-template 0;
+#X text 98 56 This template describes a single point on a pitch trace (cf. trace-template w describes the trace itself.);
+#X obj 163 14 template float y float amp;
+#X text 98 89 "y" is the field that is shown on the graph \; it's - 4 * pitch. You also get an "amp" field in dB \, which you can't see as a plot (yet).;
+#X restore 270 557 pd point-template;
+#X msg 574 506 bang;
+#X obj 6 425 pack 0 100;
+#X obj 6 449 line~;
+#X obj 6 405 dbtorms;
+#X floatatom 419 70;
+#N canvas 194 37 730 722 output 0;
+#X obj 227 76 t b f;
+#X obj 220 100 +;
+#X obj 396 207 f;
+#X obj 409 165 f;
+#X obj 276 210 f;
+#X obj 64 179 t b;
+#X obj 63 127 f;
+#X obj 55 58 inlet;
+#X text 60 37 mute;
+#X obj 64 201 f;
+#X msg 181 153 0;
+#X msg 57 84 bang;
+#X obj 64 155 moses 1;
+#X obj 178 69 t b f;
+#X obj 171 93 +;
+#X obj 139 27 r loop-amp;
+#X obj 304 26 r osc-amp;
+#X obj 64 236 s loop-amp;
+#X obj 276 235 s osc-amp;
+#X obj 455 125 print;
+#X obj 143 148 f;
+#X obj 288 168 f;
+#X obj 173 120 t b b;
+#X obj 425 23 r grain-amp;
+#X obj 397 232 s grain-amp;
+#X connect 0 0 1 0;
+#X connect 0 1 1 1;
+#X connect 1 0 6 1;
+#X connect 2 0 24 0;
+#X connect 3 0 2 1;
+#X connect 4 0 18 0;
+#X connect 5 0 9 0;
+#X connect 5 0 4 0;
+#X connect 5 0 2 0;
+#X connect 6 0 12 0;
+#X connect 7 0 11 0;
+#X connect 9 0 17 0;
+#X connect 10 0 17 0;
+#X connect 10 0 18 0;
+#X connect 10 0 24 0;
+#X connect 11 0 6 0;
+#X connect 12 0 5 0;
+#X connect 12 1 22 0;
+#X connect 13 0 14 0;
+#X connect 13 1 14 1;
+#X connect 14 0 1 0;
+#X connect 15 0 14 0;
+#X connect 15 0 20 1;
+#X connect 16 0 13 0;
+#X connect 16 0 21 1;
+#X connect 20 0 9 1;
+#X connect 21 0 4 1;
+#X connect 22 0 10 0;
+#X connect 22 1 20 0;
+#X connect 22 1 21 0;
+#X connect 22 1 3 0;
+#X connect 23 0 3 1;
+#X connect 23 0 0 0;
+#X restore 631 69 pd output;
+#N canvas 516 98 663 559 /SUBPATCH/ 0;
+#X obj 79 108 outlet;
+#X msg 78 80 set \$1;
+#X obj 79 57 r loop-amp;
+#X connect 1 0 0 0;
+#X connect 2 0 1 0;
+#X restore 419 50 pd;
+#N canvas 209 96 518 375 fft 0;
+#X floatatom 235 194;
+#X obj 349 160 r sample-rate;
+#X obj 349 180 t b f;
+#X obj 234 169 r window-size;
+#X obj 203 38 r sample-rate;
+#X obj 183 16 r window-size;
+#X obj 203 58 t b f;
+#X obj 183 83 /;
+#X obj 131 103 bang~;
+#X obj 130 175 line~;
+#X obj 183 104 * 1000;
+#X text 229 104 window size (msec);
+#X obj 129 281 rfft~;
+#X obj 131 337 tabsend~ fft-real;
+#X obj 159 307 tabsend~ fft-imag;
+#X obj 330 140 r location;
+#X obj 330 205 *;
+#X obj 330 228 * 0.001;
+#X text 383 228 location (samples);
+#X obj 130 129 f;
+#X msg 130 152 0 \, 1 \$1;
+#X obj 87 201 *~;
+#X obj 87 224 -~;
+#X obj 62 301 *~;
+#X obj 62 324 outlet~;
+#X floatatom 330 252;
+#X obj 41 127 s fft-done;
+#X obj 51 31 block~ 2048 1;
+#X obj 129 255 tabread4~ sample;
+#X obj 130 211 *~ 0;
+#X obj 129 232 +~ 0;
+#X connect 1 0 2 0;
+#X connect 2 0 16 0;
+#X connect 2 1 16 1;
+#X connect 3 0 0 0;
+#X connect 3 0 29 1;
+#X connect 4 0 6 0;
+#X connect 5 0 7 0;
+#X connect 6 0 7 0;
+#X connect 6 1 7 1;
+#X connect 7 0 10 0;
+#X connect 8 0 19 0;
+#X connect 8 0 26 0;
+#X connect 9 0 21 0;
+#X connect 9 0 21 1;
+#X connect 9 0 22 1;
+#X connect 9 0 29 0;
+#X connect 10 0 19 1;
+#X connect 12 0 13 0;
+#X connect 12 1 14 0;
+#X connect 15 0 16 0;
+#X connect 16 0 17 0;
+#X connect 17 0 25 0;
+#X connect 17 0 30 1;
+#X connect 19 0 20 0;
+#X connect 20 0 9 0;
+#X connect 21 0 22 0;
+#X connect 22 0 23 0;
+#X connect 23 0 24 0;
+#X connect 28 0 12 0;
+#X connect 28 0 23 1;
+#X connect 29 0 30 0;
+#X connect 30 0 28 0;
+#X restore 17 470 pd fft;
+#X obj 6 493 *~;
+#X obj 7 544 hip~ 5;
+#X obj 7 571 dac~;
+#X obj 580 529 adc~;
+#X obj 419 91 s loop-amp;
+#X msg 631 48 mute;
+#X text 13 127 click here first;
+#X text 570 489 live sample;
+#X text 521 25 AMPLITUDES;
+#N canvas 5 1 864 622 make-trace 0;
+#X obj 143 220 * -0.33333;
+#X obj 70 216 * 10;
+#X obj 91 136 pointer;
+#X msg 91 115 next;
+#X obj 91 97 until;
+#X obj 298 134 pointer;
+#X msg 299 108 next;
+#X obj 154 330 unpack;
+#X obj 196 332 s amp;
+#X obj 218 378 s frequency;
+#X obj 231 361 s pitch;
+#X obj 193 375 f 0;
+#X obj 154 348 t b b b b f;
+#X obj 601 133 pointer;
+#X obj 572 602 setsize trace-template bazoo;
+#X obj 572 514 random 200;
+#X obj 572 532 + 100;
+#X obj 659 526 pointer;
+#X obj 168 532 pointer;
+#X floatatom 274 524;
+#X floatatom 228 550;
+#X msg 158 509 bang;
+#X floatatom 83 461;
+#X floatatom 31 509;
+#X floatatom 118 590;
+#X floatatom 397 556;
+#X floatatom 274 489;
+#X floatatom 513 511;
+#X obj 370 464 pointer;
+#X msg 391 447 next;
+#X floatatom 409 512;
+#X obj 397 539 getsize trace-template bazoo;
+#X obj 98 563 get point-template y;
+#X obj 31 533 set point-template y;
+#X obj 78 486 element trace-template bazoo;
+#X obj 228 580 setsize trace-template bazoo;
+#X obj 274 507 set trace-template x;
+#X obj 274 542 set trace-template y;
+#X msg 572 497 bang;
+#X obj 572 549 append trace-template x;
+#X obj 399 489 get trace-template x y;
+#X obj 572 567 t b p;
+#X msg 572 584 5;
+#X obj 628 93 s clear-traces;
+#X obj 602 155 s last-in-list;
+#X msg 600 43 bang;
+#X obj 600 60 t b b;
+#X obj 573 156 f 0;
+#X obj 572 176 s nframe;
+#X obj 342 337 r nframe;
+#X obj 330 356 f;
+#X obj 330 373 + 1;
+#X obj 330 391 s nframe;
+#X obj 330 296 r done-frame;
+#X obj 336 315 s done-adding-traces;
+#X obj 15 309 r component;
+#X obj 15 326 unpack;
+#X obj 56 331 s amp;
+#X obj 80 370 s frequency;
+#X obj 92 354 s pitch;
+#X obj 54 388 s added-to-trace;
+#X obj 54 370 f 0;
+#X obj 48 408 s add-to-trace;
+#X obj 15 344 t b b b b f;
+#X obj 600 25 r clear-all;
+#X obj 154 311 r component2;
+#X obj 193 393 s started-new-trace;
+#X obj 185 414 s start-new-trace;
+#X obj 273 41 r done-analysis;
+#X obj 273 62 t b b b b;
+#X obj 225 176 r added-to-trace;
+#X obj 299 89 until;
+#X obj 351 172 get peak-template x amp;
+#X obj 425 195 * -0.33333;
+#X obj 351 190 * 10;
+#X obj 351 210 pack;
+#X obj 351 226 s component;
+#X obj 92 337 ftom;
+#X obj 298 155 t b p p;
+#X obj 197 196 set peak-template used;
+#X obj 199 176 f;
+#X obj 232 344 ftom;
+#X msg 135 31 \; done-frame bang;
+#X obj 16 178 get peak-template used x amp;
+#X obj 27 236 pack 0 0 0;
+#X obj 27 255 route 0;
+#X obj 27 272 s component2;
+#X obj 422 260 print x1;
+#X obj 174 276 print x2;
+#X obj 559 255 add-trace 1;
+#X obj 560 274 add-trace 2;
+#X obj 560 291 add-trace 3;
+#X obj 559 309 add-trace 4;
+#X obj 560 328 add-trace 5;
+#X obj 560 345 add-trace 6;
+#X obj 560 363 add-trace 7;
+#X obj 561 381 add-trace 8;
+#X obj 561 399 add-trace 9;
+#X obj 561 417 add-trace 10;
+#X msg 372 111 traverse pd-peak-list;
+#X msg 601 115 traverse pd-trace-list \, bang;
+#X msg 641 59 \; pd-trace-list clear;
+#X msg 370 430 traverse pd-trace-list \, next;
+#X msg 659 509 traverse pd-trace-list \, bang;
+#X connect 0 0 84 2;
+#X connect 1 0 84 1;
+#X connect 2 0 83 0;
+#X connect 2 1 4 1;
+#X connect 3 0 2 0;
+#X connect 4 0 3 0;
+#X connect 5 0 78 0;
+#X connect 5 1 71 1;
+#X connect 6 0 5 0;
+#X connect 7 0 12 0;
+#X connect 7 1 8 0;
+#X connect 11 0 66 0;
+#X connect 12 2 67 0;
+#X connect 12 3 11 0;
+#X connect 12 4 81 0;
+#X connect 12 4 9 0;
+#X connect 13 0 44 0;
+#X connect 15 0 16 0;
+#X connect 16 0 39 0;
+#X connect 17 0 39 1;
+#X connect 18 0 32 0;
+#X connect 19 0 37 0;
+#X connect 20 0 35 0;
+#X connect 21 0 18 0;
+#X connect 22 0 34 0;
+#X connect 23 0 33 0;
+#X connect 26 0 36 0;
+#X connect 28 0 35 1;
+#X connect 28 0 36 1;
+#X connect 28 0 37 1;
+#X connect 28 0 34 1;
+#X connect 28 0 40 0;
+#X connect 28 0 31 0;
+#X connect 29 0 28 0;
+#X connect 31 0 25 0;
+#X connect 32 0 24 0;
+#X connect 34 0 18 0;
+#X connect 34 0 33 1;
+#X connect 38 0 15 0;
+#X connect 39 0 41 0;
+#X connect 40 0 30 0;
+#X connect 40 1 27 0;
+#X connect 41 0 42 0;
+#X connect 41 1 14 1;
+#X connect 42 0 14 0;
+#X connect 45 0 46 0;
+#X connect 46 0 100 0;
+#X connect 46 0 47 0;
+#X connect 46 1 101 0;
+#X connect 46 1 43 0;
+#X connect 47 0 48 0;
+#X connect 49 0 50 1;
+#X connect 50 0 51 0;
+#X connect 51 0 52 0;
+#X connect 53 0 50 0;
+#X connect 53 0 54 0;
+#X connect 55 0 56 0;
+#X connect 56 0 63 0;
+#X connect 56 1 57 0;
+#X connect 61 0 60 0;
+#X connect 63 2 62 0;
+#X connect 63 3 61 0;
+#X connect 63 4 77 0;
+#X connect 63 4 58 0;
+#X connect 64 0 45 0;
+#X connect 65 0 7 0;
+#X connect 68 0 69 0;
+#X connect 69 0 82 0;
+#X connect 69 1 4 0;
+#X connect 69 2 71 0;
+#X connect 69 3 99 0;
+#X connect 70 0 80 1;
+#X connect 71 0 6 0;
+#X connect 72 0 74 0;
+#X connect 72 1 73 0;
+#X connect 73 0 75 1;
+#X connect 74 0 75 0;
+#X connect 75 0 76 0;
+#X connect 77 0 59 0;
+#X connect 78 0 80 0;
+#X connect 78 1 79 1;
+#X connect 78 2 72 0;
+#X connect 80 0 79 0;
+#X connect 81 0 10 0;
+#X connect 83 0 84 0;
+#X connect 83 1 1 0;
+#X connect 83 2 0 0;
+#X connect 84 0 85 0;
+#X connect 85 0 86 0;
+#X connect 99 0 5 0;
+#X connect 99 0 2 0;
+#X connect 100 0 13 0;
+#X connect 102 0 28 0;
+#X connect 103 0 17 0;
+#X restore 270 474 pd make-trace;
+#X floatatom 4 289;
+#N canvas 0 0 955 721 arrays 0;
+#X msg 30 202 \; fft-real resize 4096 \; fft-imag resize 4096;
+#X graph graph1 0 -1 4096 1 332 341 732 41;
+#X array fft-real 4096 float;
+#X pop;
+#X graph graph2 0 -1 4096 1 322 565 722 265;
+#X array fft-imag 4096 float;
+#X pop;
+#X restore 439 515 pd arrays;
+#X obj 4 309 s location;
+#X obj 95 412 r loop-amp;
+#X obj 567 288 f;
+#X obj 4 248 r location;
+#X msg 4 268 set \$1;
+#X obj 598 288 +;
+#X obj 567 309 moses 900;
+#X msg 535 329 0;
+#X msg 534 247 1;
+#X msg 566 335 \; location \$1 \; snapshot bang;
+#X msg 504 177 bang \; location 0 \; clear-all bang;
+#X floatatom 504 305;
+#X obj 504 225 t b b;
+#X obj 645 270 r incr;
+#X obj 6 385 r grain-amp;
+#X obj 93 473 r osc-amp;
+#X obj 110 553 catch~ osc-sum;
+#N canvas 102 67 751 619 osc-bank 0;
+#X obj 239 433 osc-voice;
+#X obj 223 451 osc-voice;
+#X obj 207 471 osc-voice;
+#X obj 191 490 osc-voice;
+#X obj 175 510 osc-voice;
+#X obj 159 528 osc-voice;
+#X obj 143 547 osc-voice;
+#X obj 127 566 osc-voice;
+#X obj 111 586 osc-voice;
+#X obj 95 410 route 1 2 3 4 5 6 7 8 9 10;
+#X msg 290 269 0;
+#X obj 560 489 pointer;
+#X floatatom 652 417;
+#X obj 479 351 pointer;
+#X msg 422 491 next;
+#X floatatom 479 419;
+#X obj 178 111 pointer;
+#X floatatom 283 104;
+#X floatatom 238 129;
+#X msg 168 88 bang;
+#X floatatom 92 40;
+#X floatatom 41 88;
+#X floatatom 127 169;
+#X floatatom 420 137;
+#X floatatom 283 69;
+#X floatatom 523 90;
+#X obj 425 43 pointer;
+#X msg 447 27 next;
+#X floatatom 419 92;
+#X obj 420 120 getsize trace-template bazoo;
+#X obj 108 142 get point-template y;
+#X obj 41 113 set point-template y;
+#X obj 87 66 element trace-template bazoo;
+#X obj 238 159 setsize trace-template bazoo;
+#X obj 283 86 set trace-template x;
+#X obj 283 121 set trace-template y;
+#X obj 409 68 get trace-template x y;
+#X floatatom 403 312;
+#X msg 403 288 1;
+#X msg 434 288 0;
+#X obj 479 451 <;
+#X obj 479 398 get trace-template x voiceno;
+#X obj 479 374 t p p;
+#X obj 302 337 until;
+#X obj 477 233 r start-resynth;
+#X obj 481 255 t b b;
+#X obj 388 353 f;
+#X obj 388 372 sel 0 1;
+#X obj 514 436 r synth-index;
+#X obj 478 555 pack f p;
+#X obj 479 470 sel 0 1;
+#X obj 235 230 r step-resynth;
+#X obj 291 288 f;
+#X obj 291 308 s synth-index;
+#X obj 489 523 f;
+#X obj 322 289 + 5;
+#X obj 478 490 t b b b;
+#X obj 235 252 t b b b;
+#X obj 216 301 s osc-tick;
+#X obj 95 604 osc-voice;
+#X msg 425 10 traverse pd-trace-list \, next;
+#X msg 478 288 traverse pd-trace-list \, next;
+#X connect 9 0 59 0;
+#X connect 9 1 8 0;
+#X connect 9 2 7 0;
+#X connect 9 3 6 0;
+#X connect 9 4 5 0;
+#X connect 9 5 4 0;
+#X connect 9 6 3 0;
+#X connect 9 7 2 0;
+#X connect 9 8 1 0;
+#X connect 9 9 0 0;
+#X connect 10 0 52 0;
+#X connect 11 0 49 1;
+#X connect 13 0 42 0;
+#X connect 13 1 39 0;
+#X connect 13 1 43 1;
+#X connect 14 0 13 0;
+#X connect 15 0 40 0;
+#X connect 16 0 30 0;
+#X connect 17 0 35 0;
+#X connect 18 0 33 0;
+#X connect 19 0 16 0;
+#X connect 20 0 32 0;
+#X connect 21 0 31 0;
+#X connect 24 0 34 0;
+#X connect 26 0 33 1;
+#X connect 26 0 34 1;
+#X connect 26 0 35 1;
+#X connect 26 0 32 1;
+#X connect 26 0 36 0;
+#X connect 26 0 29 0;
+#X connect 27 0 26 0;
+#X connect 29 0 23 0;
+#X connect 30 0 22 0;
+#X connect 32 0 16 0;
+#X connect 32 0 31 1;
+#X connect 36 0 28 0;
+#X connect 36 1 25 0;
+#X connect 37 0 46 1;
+#X connect 38 0 37 0;
+#X connect 39 0 37 0;
+#X connect 40 0 50 0;
+#X connect 41 0 15 0;
+#X connect 41 1 54 1;
+#X connect 42 0 41 0;
+#X connect 42 1 11 1;
+#X connect 43 0 46 0;
+#X connect 44 0 45 0;
+#X connect 45 0 61 0;
+#X connect 45 1 38 0;
+#X connect 45 1 10 0;
+#X connect 46 0 47 0;
+#X connect 47 0 43 1;
+#X connect 47 1 15 0;
+#X connect 48 0 40 1;
+#X connect 49 0 9 0;
+#X connect 50 0 43 1;
+#X connect 50 1 56 0;
+#X connect 51 0 57 0;
+#X connect 52 0 53 0;
+#X connect 52 0 55 0;
+#X connect 54 0 49 0;
+#X connect 55 0 52 1;
+#X connect 56 0 14 0;
+#X connect 56 1 54 0;
+#X connect 56 2 11 0;
+#X connect 57 0 58 0;
+#X connect 57 1 43 0;
+#X connect 57 2 52 0;
+#X connect 60 0 26 0;
+#X connect 61 0 13 0;
+#X restore 439 494 pd osc-bank;
+#X obj 498 92 s grain-amp;
+#N canvas 31 70 662 326 save-list 0;
+#X floatatom 584 255;
+#X floatatom 521 254;
+#X floatatom 461 251;
+#X floatatom 398 250;
+#X obj 335 206 pointer;
+#X obj 236 121 pointer;
+#X msg 252 47 bang;
+#X obj 117 200 rmstodb;
+#X obj 8 201 * 0.1;
+#X obj 116 220 * -3;
+#X floatatom 342 247;
+#X obj 234 27 r start-analysis;
+#X obj 235 71 t b b;
+#X msg 351 185 next;
+#X obj 331 227 get peak-template x y amp ampreal ampimag;
+#X obj 7 241 append peak-template x y amp ampreal ampimag;
+#X obj 96 138 r found-peak;
+#X obj 97 161 unpack 0 0 0 0 0;
+#X msg 62 201 330;
+#X msg 274 69 \; pd-peak-list clear;
+#X msg 235 100 traverse pd-peak-list \, bang;
+#X msg 336 166 traverse pd-peak-list \, next;
+#X connect 4 0 14 0;
+#X connect 5 0 15 5;
+#X connect 6 0 12 0;
+#X connect 7 0 9 0;
+#X connect 8 0 15 0;
+#X connect 9 0 15 2;
+#X connect 11 0 12 0;
+#X connect 12 0 20 0;
+#X connect 12 1 19 0;
+#X connect 13 0 4 0;
+#X connect 14 0 10 0;
+#X connect 14 1 3 0;
+#X connect 14 2 2 0;
+#X connect 14 3 1 0;
+#X connect 14 4 0 0;
+#X connect 16 0 17 0;
+#X connect 17 1 8 0;
+#X connect 17 2 18 0;
+#X connect 17 2 7 0;
+#X connect 17 3 15 3;
+#X connect 17 4 15 4;
+#X connect 18 0 15 1;
+#X connect 20 0 5 0;
+#X connect 21 0 4 0;
+#X restore 270 431 pd save-list;
+#X msg 6 144 \; pd dsp 1 \; window-size 2048 \; sample-rate 44100 \; f-threshold 40 \; incr 10 \; clear-all bang;
+#X obj 567 245 metro 150;
+#X floatatom 242 309;
+#X floatatom 290 309;
+#X msg 107 349 \; start-resynth bang;
+#X msg 242 350 \; step-resynth bang;
+#X obj 242 329 metro 100;
+#X msg 368 350 \; osc-stop bang;
+#X text 605 107 resynth;
+#X text 491 106 analyzed grains;
+#X text 423 106 original;
+#X text 502 158 ... and here third to analyze;
+#N canvas 0 0 276 216 test 0;
+#X floatatom 43 120;
+#X obj 43 141 s loud;
+#X msg 38 84 \; clear-all bang;
+#X msg 39 52 \; snapshot bang;
+#X connect 0 0 1 0;
+#X restore 438 538 pd test;
+#X text 244 140 read a sample;
+#X msg 165 163 \; read-sample ../sound/bell.aiff 44100;
+#X msg 164 200 \; read-sample ../sound/voice.wav 32000;
+#N canvas 190 43 405 461 test-signal 0;
+#X obj 134 293 tabread4~ sample;
+#X obj 134 268 line~;
+#X obj 95 146 f;
+#X obj 254 46 r insamprate;
+#X obj 136 350 *~;
+#X obj 164 351 dbtorms;
+#X obj 164 328 inlet;
+#X obj 135 415 outlet~;
+#X obj 146 33 r insamplength;
+#X msg 134 247 0 \, \$1 \$2;
+#X obj 134 221 pack 0 0;
+#X obj 209 190 /;
+#X obj 299 99 * 0.001;
+#X obj 135 388 hip~ 5;
+#X obj 33 5 loadbang;
+#X text 242 13 sample playback;
+#X msg 33 25 1;
+#X obj 33 69 metro 1000;
+#X floatatom 33 48;
+#X obj 255 75 t b b f;
+#X obj 161 84 t b f;
+#X connect 0 0 4 0;
+#X connect 1 0 0 0;
+#X connect 2 0 10 0;
+#X connect 3 0 19 0;
+#X connect 4 0 13 0;
+#X connect 5 0 4 1;
+#X connect 6 0 5 0;
+#X connect 8 0 20 0;
+#X connect 9 0 1 0;
+#X connect 10 0 9 0;
+#X connect 11 0 10 1;
+#X connect 11 0 17 1;
+#X connect 12 0 11 1;
+#X connect 13 0 7 0;
+#X connect 14 0 16 0;
+#X connect 16 0 18 0;
+#X connect 17 0 2 0;
+#X connect 18 0 17 0;
+#X connect 19 0 16 0;
+#X connect 19 1 11 0;
+#X connect 19 2 12 0;
+#X connect 20 0 16 0;
+#X connect 20 1 11 0;
+#X connect 20 1 2 1;
+#X restore 96 436 pd test-signal;
+#N canvas 132 255 634 331 insample 0;
+#X graph graph1 0 -1 55408 1 199 168 599 18;
+#X array sample 55409 float;
+#X pop;
+#X obj 19 70 r read-sample;
+#X obj 19 95 unpack s f;
+#X obj 53 121 s insamprate;
+#X obj 19 171 soundfiler;
+#X msg 19 147 read -resize \$1 sample;
+#X obj 19 201 s insamplength;
+#X msg 357 197 \; sample resize 220500 \; insamplength 220500;
+#X connect 1 0 2 0;
+#X connect 2 0 5 0;
+#X connect 2 1 3 0;
+#X connect 4 0 6 0;
+#X connect 5 0 4 0;
+#X restore 438 559 pd insample;
+#X obj 572 553 tabwrite~ sample;
+#X text 117 0 SINUSOID TRACKING;
+#X text 99 259 to resynthesize \, "start" once and "step" ad lib. To stop \, stop stepping and hit osc-stop. Note resynth ampliture control above.;
+#X text 3 17 This patch tries to reconstruct sinusoidal "tracks" from a sampled sound using pique~ and the data structure facilities. It turns out to be quite hard \, not least because pique~ 0.1 puts out all sorts of spurious peaks.;
+#X connect 0 0 2 0;
+#X connect 1 0 0 0;
+#X connect 3 0 4 0;
+#X connect 4 0 6 0;
+#X connect 5 0 3 0;
+#X connect 6 0 28 0;
+#X connect 7 0 60 1;
+#X connect 8 0 41 1;
+#X connect 9 0 60 0;
+#X connect 10 0 44 1;
+#X connect 11 0 57 0;
+#X connect 12 0 11 0;
+#X connect 19 0 77 0;
+#X connect 20 0 21 0;
+#X connect 21 0 27 0;
+#X connect 22 0 20 0;
+#X connect 23 0 31 0;
+#X connect 25 0 23 0;
+#X connect 26 0 27 1;
+#X connect 27 0 28 0;
+#X connect 28 0 29 0;
+#X connect 28 0 29 1;
+#X connect 30 0 77 0;
+#X connect 32 0 24 0;
+#X connect 37 0 39 0;
+#X connect 40 0 75 0;
+#X connect 41 0 44 0;
+#X connect 41 0 45 0;
+#X connect 41 0 50 0;
+#X connect 42 0 43 0;
+#X connect 43 0 37 0;
+#X connect 44 0 41 1;
+#X connect 45 0 48 0;
+#X connect 45 1 46 0;
+#X connect 46 0 9 0;
+#X connect 47 0 9 0;
+#X connect 49 0 51 0;
+#X connect 51 0 47 0;
+#X connect 51 1 8 0;
+#X connect 52 0 10 0;
+#X connect 53 0 22 0;
+#X connect 54 0 5 0;
+#X connect 55 0 6 1;
+#X connect 60 0 41 0;
+#X connect 61 0 65 0;
+#X connect 62 0 65 1;
+#X connect 65 0 64 0;
+#X connect 75 0 28 0;
diff --git a/pd/doc/4.fft.examples/14.waveformgrab.pd b/pd/doc/4.fft.examples/14.waveformgrab.pd
new file mode 100644
index 00000000..a9d17bed
--- /dev/null
+++ b/pd/doc/4.fft.examples/14.waveformgrab.pd
@@ -0,0 +1,385 @@
+#N canvas 59 19 701 580 12;
+#X msg 415 337 set \$1;
+#X floatatom 596 337;
+#N canvas 81 37 483 329 fft 0;
+#X obj 102 155 *~;
+#X obj 70 155 *~;
+#X obj 70 98 *~;
+#X obj 70 52 inlet~;
+#X obj 70 127 rfft~;
+#X obj 70 182 sqrt~;
+#X obj 70 209 *~;
+#X obj 70 261 rifft~;
+#X obj 70 287 tabwrite~ grab;
+#X obj 86 74 tabreceive~ half-sine;
+#X obj 70 235 /~ 512;
+#X obj 137 191 tabreceive~ alternator;
+#X obj 162 259 r do-grab;
+#X obj 368 310 block~ 1024 2;
+#X connect 0 0 5 0;
+#X connect 1 0 5 0;
+#X connect 2 0 4 0;
+#X connect 3 0 2 0;
+#X connect 4 0 1 0;
+#X connect 4 0 1 1;
+#X connect 4 1 0 0;
+#X connect 4 1 0 1;
+#X connect 5 0 6 0;
+#X connect 6 0 10 0;
+#X connect 7 0 8 0;
+#X connect 9 0 2 1;
+#X connect 10 0 7 0;
+#X connect 11 0 6 1;
+#X connect 12 0 8 0;
+#X restore 186 422 pd fft;
+#X floatatom 511 360;
+#X floatatom 415 360;
+#X obj 186 398 hip~ 5;
+#X obj 99 544 dac~;
+#N canvas 0 0 600 273 in-sample 0;
+#X graph graph1 0 -1 440999 1 160 166 560 16;
+#X array grab 441000 float;
+#X pop;
+#X msg 186 208 \; grab resize 441000;
+#X obj 38 224 soundfiler;
+#X msg 38 191 write x.wav grab;
+#X msg 353 212 \; grab resize 4410;
+#X connect 3 0 2 0;
+#X restore 371 446 pd in-sample;
+#X obj 116 491 hip~ 5;
+#X obj 99 515 *~;
+#N canvas 59 0 781 602 regenerate 0;
+#X obj 90 400 r invblk;
+#X obj 50 463 *~;
+#X obj 67 443 clip~ 1 1000;
+#X floatatom 31 41;
+#X floatatom 221 117;
+#X obj 430 368 *~;
+#X obj 558 345 samphold~;
+#X obj 672 345 samphold~;
+#X obj 467 345 samphold~;
+#X obj 446 493 clip~ -0.5 0.5;
+#X obj 446 516 cos~;
+#X obj 674 389 +~;
+#X obj 565 443 -~;
+#X obj 550 463 *~;
+#X obj 602 491 +~;
+#X obj 585 558 *~;
+#X obj 37 373 *~;
+#X obj 169 338 samphold~;
+#X obj 311 343 samphold~;
+#X obj 116 181 sig~;
+#X obj 221 230 phasor~;
+#X obj 330 180 wrap~;
+#X obj 221 92 r pitch;
+#X obj 221 161 mtof;
+#X obj 116 157 r sample-rate;
+#X obj 314 156 line~;
+#X text 360 153 read location in blocks;
+#X obj 314 234 -~;
+#X text 374 179 fractional part;
+#X text 338 234 integer part;
+#X text 71 128 spectral stretch;
+#X obj 66 335 samphold~;
+#X obj 319 256 sig~ 0.5;
+#X text 294 357 middle of block;
+#X obj 413 34 t b f;
+#X obj 413 80 /;
+#X obj 413 56 1;
+#X obj 51 488 clip~ -0.5 0.5;
+#X obj 51 510 cos~;
+#X obj 294 396 +~;
+#X obj 173 450 -~;
+#X obj 155 471 *~;
+#X obj 162 494 +~;
+#X obj 145 550 *~;
+#X text 68 365 offset into;
+#X text 78 376 sample;
+#X text 128 243 samples;
+#X text 128 228 period in;
+#X text 175 353 weight for;
+#X text 174 364 next block;
+#X obj 640 193 wrap~;
+#X obj 413 102 s invblk;
+#X text 411 118 one over block size;
+#X obj 483 406 r invblk;
+#X obj 145 574 send~ outsig;
+#X floatatom 328 87;
+#X obj 328 112 pack 0 100;
+#X obj 413 11 r window-size;
+#X obj 454 239 r window-size;
+#X obj 31 17 r specshift;
+#X text 103 270 grain size in samples;
+#X obj 314 37 r loco;
+#X obj 444 472 *~;
+#X obj 460 450 clip~ 1 1000;
+#X text 91 347 grain;
+#X obj 221 141 - 12;
+#X obj 173 421 tabread4~ grab;
+#X obj 294 422 tabread4~ grab;
+#X obj 565 414 tabread4~ grab;
+#X obj 674 415 tabread4~ grab;
+#X obj 31 64 * 0.01;
+#X obj 31 88 + 69;
+#X obj 31 111 mtof;
+#X obj 31 134 / 440;
+#X obj 70 272 *~ 1;
+#X obj 640 168 +~ 0.5;
+#X obj 67 422 *~ 1;
+#X obj 460 429 *~ 1;
+#X obj 446 538 +~ 1;
+#X obj 51 533 +~ 1;
+#X obj 116 208 /~ 1;
+#X obj 314 282 *~ 1;
+#X obj 173 398 +~ 0;
+#X obj 565 392 +~ 0;
+#X obj 16 334 -~ 0.5;
+#X obj 413 345 -~ 0.5;
+#X msg 328 64 set \$1;
+#X connect 0 0 76 1;
+#X connect 1 0 37 0;
+#X connect 2 0 1 1;
+#X connect 3 0 70 0;
+#X connect 4 0 65 0;
+#X connect 5 0 11 0;
+#X connect 6 0 13 0;
+#X connect 7 0 11 1;
+#X connect 8 0 5 1;
+#X connect 8 0 77 0;
+#X connect 9 0 10 0;
+#X connect 10 0 78 0;
+#X connect 11 0 69 0;
+#X connect 11 0 83 0;
+#X connect 12 0 13 1;
+#X connect 13 0 14 0;
+#X connect 14 0 15 1;
+#X connect 15 0 54 0;
+#X connect 16 0 39 0;
+#X connect 17 0 41 0;
+#X connect 18 0 39 1;
+#X connect 19 0 80 0;
+#X connect 20 0 31 1;
+#X connect 20 0 18 1;
+#X connect 20 0 17 1;
+#X connect 20 0 75 0;
+#X connect 20 0 84 0;
+#X connect 21 0 27 1;
+#X connect 21 0 17 0;
+#X connect 21 0 6 0;
+#X connect 22 0 4 0;
+#X connect 23 0 20 0;
+#X connect 23 0 80 1;
+#X connect 24 0 19 0;
+#X connect 25 0 21 0;
+#X connect 25 0 27 0;
+#X connect 27 0 81 0;
+#X connect 31 0 16 1;
+#X connect 31 0 76 0;
+#X connect 32 0 81 0;
+#X connect 34 0 36 0;
+#X connect 34 1 35 1;
+#X connect 35 0 51 0;
+#X connect 36 0 35 0;
+#X connect 37 0 38 0;
+#X connect 38 0 79 0;
+#X connect 39 0 67 0;
+#X connect 39 0 82 0;
+#X connect 40 0 41 1;
+#X connect 41 0 42 0;
+#X connect 42 0 43 1;
+#X connect 43 0 54 0;
+#X connect 50 0 8 1;
+#X connect 50 0 6 1;
+#X connect 50 0 7 1;
+#X connect 50 0 85 0;
+#X connect 53 0 77 1;
+#X connect 55 0 56 0;
+#X connect 56 0 25 0;
+#X connect 57 0 34 0;
+#X connect 58 0 81 1;
+#X connect 58 0 82 1;
+#X connect 58 0 83 1;
+#X connect 59 0 3 0;
+#X connect 61 0 25 0;
+#X connect 61 0 86 0;
+#X connect 62 0 9 0;
+#X connect 63 0 62 1;
+#X connect 65 0 23 0;
+#X connect 66 0 40 0;
+#X connect 67 0 40 1;
+#X connect 67 0 42 1;
+#X connect 68 0 12 0;
+#X connect 69 0 12 1;
+#X connect 69 0 14 1;
+#X connect 70 0 71 0;
+#X connect 71 0 72 0;
+#X connect 72 0 73 0;
+#X connect 73 0 74 1;
+#X connect 74 0 31 0;
+#X connect 74 0 8 0;
+#X connect 75 0 50 0;
+#X connect 76 0 2 0;
+#X connect 77 0 63 0;
+#X connect 78 0 15 0;
+#X connect 79 0 43 0;
+#X connect 80 0 74 0;
+#X connect 81 0 18 0;
+#X connect 81 0 7 0;
+#X connect 82 0 66 0;
+#X connect 83 0 68 0;
+#X connect 84 0 16 0;
+#X connect 84 0 1 0;
+#X connect 85 0 5 0;
+#X connect 85 0 62 0;
+#X connect 86 0 55 0;
+#X restore 371 495 pd regenerate;
+#X obj 115 464 receive~ outsig;
+#X obj 43 467 line~;
+#X obj 416 383 s specshift;
+#X obj 575 386 s loco;
+#X obj 512 383 s pitch;
+#X obj 511 319 r pitch;
+#X msg 511 339 set \$1;
+#X obj 415 317 r specshift;
+#X floatatom 553 64;
+#N canvas 194 37 397 591 output 0;
+#X obj 58 180 t b;
+#X obj 58 135 f;
+#X obj 58 90 inlet;
+#X text 63 71 mute;
+#X obj 58 202 f;
+#X msg 119 217 0;
+#X msg 58 113 bang;
+#X obj 58 158 moses 1;
+#X obj 119 194 t b f;
+#X obj 86 392 outlet;
+#X msg 86 370 set \$1;
+#X obj 165 145 moses 1;
+#X obj 199 394 dbtorms;
+#X obj 199 417 pack 0 100;
+#X obj 165 122 r master-lvl;
+#X obj 86 339 r master-lvl;
+#X obj 74 254 s master-lvl;
+#X obj 199 439 s master-amp;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X restore 553 42 pd output;
+#X msg 553 19 mute;
+#X text 587 57 MASTER;
+#X text 586 69 LEVEL;
+#X obj 553 86 s master-lvl;
+#X obj 44 442 r master-amp;
+#N canvas 93 44 649 449 window 0;
+#X graph graph1 0 -1 1023 1 435 170 635 20;
+#X array half-sine 1024 float;
+#X pop;
+#X obj 108 88 r window-size;
+#X obj 108 110 t b f;
+#X obj 72 136 /;
+#X obj 25 16 r make-window;
+#X msg 25 36 bang;
+#X msg 109 136 -0.25;
+#X obj 70 181 osc~;
+#X obj 84 66 samplerate~;
+#X graph graph2 0 -1 1023 1 439 331 639 191;
+#X array alternator 1024 float;
+#X pop;
+#X obj 21 249 r make-window;
+#X msg 21 269 bang;
+#X obj 80 299 samplerate~;
+#X obj 451 372 r window-size;
+#X msg 451 397 \; half-sine resize \$1 \; alternator resize \$1;
+#X obj 80 325 / 2;
+#X obj 239 12 r make-window;
+#X obj 80 351 phasor~;
+#X obj 21 425 tabwrite~ alternator;
+#X obj 256 106 switch~;
+#X msg 239 70 1;
+#X obj 277 44 del 500;
+#X msg 277 70 0;
+#X obj 80 373 -~ 0.5;
+#X obj 25 224 tabwrite~ half-sine;
+#X obj 72 160 / 2;
+#X obj 80 396 *~ 4;
+#X msg 124 325 0.75;
+#X obj 68 203 *~;
+#X connect 1 0 2 0;
+#X connect 2 0 3 0;
+#X connect 2 1 3 1;
+#X connect 3 0 25 0;
+#X connect 4 0 5 0;
+#X connect 5 0 6 0;
+#X connect 5 0 8 0;
+#X connect 5 0 24 0;
+#X connect 6 0 7 1;
+#X connect 7 0 28 0;
+#X connect 7 0 28 1;
+#X connect 8 0 3 0;
+#X connect 10 0 11 0;
+#X connect 11 0 27 0;
+#X connect 11 0 12 0;
+#X connect 11 0 18 0;
+#X connect 12 0 15 0;
+#X connect 13 0 14 0;
+#X connect 15 0 17 0;
+#X connect 16 0 20 0;
+#X connect 16 0 21 0;
+#X connect 17 0 23 0;
+#X connect 20 0 19 0;
+#X connect 21 0 22 0;
+#X connect 22 0 19 0;
+#X connect 23 0 26 0;
+#X connect 25 0 7 0;
+#X connect 26 0 18 0;
+#X connect 27 0 17 1;
+#X connect 28 0 24 0;
+#X restore 371 471 pd window;
+#X obj 186 370 readsf~;
+#X text 38 285 CLICK HERE FIRST;
+#X msg 576 312 0 \, 250 10000;
+#X msg 28 309 \; pd dsp 1 \; sample-rate 44100 \; window-size 1024 \; make-window bang \; pitch 48 \; specshift 0 \;;
+#X obj 596 360 pack 0 100;
+#X text 196 292 analyze from soundfile;
+#X text 118 10 WAVEFORM GRABBER;
+#X text 21 27 This patch takes an incoming sound does an overlap-2 FFT analysis of it \, and bashes the phases of the spectra to known values. In this way you can use samples as waveforms and cross-fade them at will without getting phase modulation.;
+#X text 19 93 This might be useful for making synthetic instruments that mimic the spectral variation of recorded sounds. Here we analyze a soundfile \, but you can replace "readsf~" by "adc~" to analyze on the fly. The result is a massaged sample suitable for reading via the "regenerate" subpatch.;
+#X text 18 169 Since the analysis is done with an overlap of 2 \, the analyzed sample is twice the length of the original sound.;
+#X text 18 199 When you aren't busy doing analyses \, you can turn the "FFT" window off by replacing the block~ with a switch~.;
+#X msg 186 322 open ../sound/voice.wav \, 1 \; specshift -720 \; do-grab bang;
+#X text 15 231 The resynthesized sound seems to be skewed slightly north spectrally. The recorded sample should just need -530 cents of correction (because of the different sample rate) but my ears prefer -720;
+#X connect 0 0 4 0;
+#X connect 1 0 31 0;
+#X connect 3 0 15 0;
+#X connect 4 0 13 0;
+#X connect 5 0 2 0;
+#X connect 8 0 9 1;
+#X connect 9 0 6 0;
+#X connect 9 0 6 1;
+#X connect 11 0 8 0;
+#X connect 12 0 9 0;
+#X connect 16 0 17 0;
+#X connect 17 0 3 0;
+#X connect 18 0 0 0;
+#X connect 19 0 24 0;
+#X connect 20 0 19 0;
+#X connect 21 0 20 0;
+#X connect 25 0 12 0;
+#X connect 27 0 5 0;
+#X connect 29 0 14 0;
+#X connect 31 0 14 0;
+#X connect 38 0 27 0;
diff --git a/pd/doc/4.fft.examples/add-trace.pd b/pd/doc/4.fft.examples/add-trace.pd
new file mode 100644
index 00000000..c04c855a
--- /dev/null
+++ b/pd/doc/4.fft.examples/add-trace.pd
@@ -0,0 +1,152 @@
+#N canvas 222 113 821 785 10;
+#X obj 405 551 r amp;
+#X obj 466 531 element trace-template bazoo;
+#X obj 365 578 set point-template y amp;
+#X obj 382 454 r pitch;
+#X obj 366 496 f;
+#X obj 366 520 * -4;
+#X obj 442 417 pointer;
+#X obj 443 608 f 1;
+#X obj 326 252 f;
+#X obj 326 191 f;
+#X obj 326 215 sel 0;
+#X floatatom 201 220 0;
+#X obj 24 72 r add-to-trace;
+#X obj 326 143 r start-new-trace;
+#X obj 341 305 r nframe;
+#X floatatom 203 100 0;
+#X obj 22 419 r pitch;
+#X obj 75 245 r frequency;
+#X obj 68 561 r amp;
+#X text 141 200 current pitch;
+#X obj 24 262 mtof;
+#X obj 24 190 sel 1;
+#X obj 24 214 t b b;
+#X obj 24 286 -;
+#X obj 24 310 abs;
+#X obj 24 334 <;
+#X obj 79 314 r f-threshold;
+#X obj 24 358 sel 1;
+#X obj 21 461 f;
+#X obj 59 268 f;
+#X obj 24 238 f;
+#X text 34 374 if this happens \, add to the trace;
+#X obj 533 114 r done-adding-traces;
+#X obj 533 165 sel 0;
+#X obj 583 89 - 1;
+#X obj 14 45 f 2;
+#X obj 13 385 t b b;
+#X obj 203 34 r clear-traces;
+#X obj 203 58 f 0;
+#X obj 24 166 f;
+#X obj 533 141 f;
+#X obj 93 403 pointer;
+#X text 160 397 current trace;
+#X obj 115 454 getsize trace-template bazoo;
+#X obj 155 504 + 1;
+#X obj 155 528 setsize trace-template bazoo;
+#X obj 126 557 element trace-template bazoo;
+#X obj 115 478 t f f;
+#X obj 21 586 set point-template y amp;
+#X obj 93 427 t b p p;
+#X obj 21 485 * -4;
+#X obj 13 633 s added-to-trace;
+#X obj 13 609 f 1;
+#X obj 40 97 r added-to-trace;
+#X obj 24 118 f;
+#X obj 24 142 sel 0;
+#X obj 342 170 r started-new-trace;
+#X obj 326 276 sel 0;
+#X obj 443 633 s started-new-trace;
+#X text 535 482 last trace in list;
+#X text 514 411 reentrancy protection \; should go away;
+#X obj 541 464 s last-in-list;
+#X obj 615 350 r last-in-list;
+#X obj 443 474 t b b p;
+#X obj 465 507 f 0;
+#X obj 326 357 * 5;
+#X obj 326 332 f;
+#X obj 292 300 f 2;
+#X text 238 52 "state" -- 0 if free \, 1 if making a trace \, and 2 if we've added a point for the current frame;
+#X obj 546 307 f \$1;
+#X text 585 306 voice number;
+#X obj 442 391 append trace-template x voiceno;
+#X obj 516 278 t f b;
+#X connect 0 0 2 1;
+#X connect 1 0 2 2;
+#X connect 3 0 4 1;
+#X connect 4 0 5 0;
+#X connect 4 0 11 0;
+#X connect 5 0 2 0;
+#X connect 6 0 63 0;
+#X connect 6 0 41 1;
+#X connect 7 0 58 0;
+#X connect 8 0 57 0;
+#X connect 9 0 10 0;
+#X connect 10 0 8 0;
+#X connect 11 0 30 1;
+#X connect 12 0 54 0;
+#X connect 13 0 9 0;
+#X connect 14 0 66 1;
+#X connect 15 0 39 1;
+#X connect 15 0 40 1;
+#X connect 15 0 8 1;
+#X connect 16 0 28 1;
+#X connect 17 0 29 1;
+#X connect 18 0 48 1;
+#X connect 20 0 23 0;
+#X connect 21 0 22 0;
+#X connect 22 0 30 0;
+#X connect 22 1 29 0;
+#X connect 23 0 24 0;
+#X connect 24 0 25 0;
+#X connect 25 0 27 0;
+#X connect 26 0 25 1;
+#X connect 27 0 36 0;
+#X connect 28 0 50 0;
+#X connect 28 0 11 0;
+#X connect 29 0 23 1;
+#X connect 30 0 20 0;
+#X connect 32 0 40 0;
+#X connect 33 1 34 0;
+#X connect 34 0 15 0;
+#X connect 35 0 15 0;
+#X connect 36 0 35 0;
+#X connect 36 0 52 0;
+#X connect 36 1 41 0;
+#X connect 37 0 38 0;
+#X connect 38 0 15 0;
+#X connect 39 0 21 0;
+#X connect 40 0 33 0;
+#X connect 41 0 49 0;
+#X connect 43 0 47 0;
+#X connect 44 0 45 0;
+#X connect 46 0 48 2;
+#X connect 47 0 46 0;
+#X connect 47 1 44 0;
+#X connect 49 0 28 0;
+#X connect 49 1 43 0;
+#X connect 49 2 45 1;
+#X connect 49 2 46 1;
+#X connect 50 0 48 0;
+#X connect 52 0 51 0;
+#X connect 53 0 54 1;
+#X connect 54 0 55 0;
+#X connect 55 0 39 0;
+#X connect 56 0 9 1;
+#X connect 57 0 66 0;
+#X connect 57 0 67 0;
+#X connect 62 0 71 2;
+#X connect 63 0 4 0;
+#X connect 63 0 7 0;
+#X connect 63 1 64 0;
+#X connect 63 2 1 1;
+#X connect 63 2 61 0;
+#X connect 64 0 1 0;
+#X connect 65 0 72 0;
+#X connect 66 0 65 0;
+#X connect 67 0 15 0;
+#X connect 69 0 71 1;
+#X connect 71 0 6 0;
+#X connect 72 0 71 0;
+#X connect 72 1 69 0;
diff --git a/pd/doc/4.fft.examples/osc-voice.pd b/pd/doc/4.fft.examples/osc-voice.pd
new file mode 100644
index 00000000..02a8bde5
--- /dev/null
+++ b/pd/doc/4.fft.examples/osc-voice.pd
@@ -0,0 +1,54 @@
+#N canvas 230 103 972 643 10;
+#X obj 261 279 element trace-template bazoo;
+#X floatatom 320 207 0;
+#X obj 297 163 getsize trace-template bazoo;
+#X obj 429 466 line~;
+#X obj 276 49 inlet;
+#X obj 424 357 dbtorms;
+#X obj 264 396 mtof;
+#X obj 264 476 phasor~;
+#X obj 258 513 cos~;
+#X obj 265 547 *~;
+#X obj 265 587 throw~ osc-sum;
+#X obj 185 163 f;
+#X obj 245 167 + 1;
+#X obj 262 240 moses;
+#X obj 261 319 get point-template y amp;
+#X obj 426 401 pack 0 30;
+#X msg 356 432 0 30;
+#X obj 276 89 t b p;
+#X msg 225 120 0;
+#X obj 96 60 r osc-tick;
+#X obj 264 356 * -0.25;
+#X obj 81 307 print no;
+#X obj 264 436 sig~;
+#X msg 609 357 0;
+#X obj 616 326 r osc-stop;
+#X connect 0 0 14 0;
+#X connect 1 0 13 1;
+#X connect 2 0 1 0;
+#X connect 3 0 9 1;
+#X connect 4 0 17 0;
+#X connect 5 0 15 0;
+#X connect 6 0 22 0;
+#X connect 7 0 8 0;
+#X connect 8 0 9 0;
+#X connect 9 0 10 0;
+#X connect 11 0 12 0;
+#X connect 11 0 13 0;
+#X connect 12 0 11 1;
+#X connect 13 0 0 0;
+#X connect 13 1 16 0;
+#X connect 14 0 20 0;
+#X connect 14 1 5 0;
+#X connect 15 0 3 0;
+#X connect 16 0 3 0;
+#X connect 17 0 18 0;
+#X connect 17 1 0 1;
+#X connect 17 1 2 0;
+#X connect 18 0 11 1;
+#X connect 19 0 11 0;
+#X connect 20 0 6 0;
+#X connect 22 0 7 0;
+#X connect 23 0 15 0;
+#X connect 24 0 23 0;
diff --git a/pd/doc/4.fft.examples/x.wav b/pd/doc/4.fft.examples/x.wav
new file mode 100644
index 00000000..3a2fd446
--- /dev/null
+++ b/pd/doc/4.fft.examples/x.wav
Binary files differ
diff --git a/pd/doc/5.reference/0.INTRO.txt b/pd/doc/5.reference/0.INTRO.txt
new file mode 100644
index 00000000..37380162
--- /dev/null
+++ b/pd/doc/5.reference/0.INTRO.txt
@@ -0,0 +1,143 @@
+The "reference" section of the documentation should contain a patch
+demonstrating how to use each of Pd's classes. As of version 0.29, a complete
+list of "object" classes follows. Not included in this list are messages,
+atoms, graphs, etc. which aren't typed into object boxes but come
+straight off the "add" menu.
+
+---------------------------- GLUE --------------------------------
+bang - output a bang message
+float - store and recall a number
+symbol - store and recall a symbol
+int - store and recall an integer
+send - send a message to a named object
+receive - catch "sent" messages
+select - test for matching numbers or symbols
+route - route messages according to first element
+pack - make compound messages
+unpack - get elements of compound messages
+trigger - sequence and convert messagess
+spigot - interruptible message connection
+moses - part a numeric stream
+until - looping mechanism
+print - print out messages
+makefilename - format a symbol with a variable field
+change - remove repeated numbers from a stream
+swap - swap two numbers
+value - shared numeric value
+------------------------------ TIME ----------------------------------
+delay - send a message after a time delay
+metro - send a message periodically
+line - send a series of linearly stepped numbers
+timer - measure time intervals
+cputime - measure CPU time
+realtime -measure real time
+pipe - dynamically growable delay line for numbers
+------------------------------ MATH ----------------------------------
++ - * / pow arithmetic
+== != > < >= <= relational tests
+& && | || % bit twiddling
+mtof ftom powtodb rmstodb dbtopow dbtorms convert acoustical units
+mod div sin cos tan atan atan2 sqrt log exp abs higher math
+random lower math
+max min greater or lesser of 2 numbers
+------------------------------ MIDI ----------------------------------
+notein ctlin pgmin bendin touchin polytouchin midiin sysexin - MIDI input
+noteout ctlout pgmout bendout touchout polytouchout midiout - MIDI output
+makenote - schedule a delayed "note off" message corresponding to a note-on
+stripnote - strip "note off" messages
+------------------------------ TABLES----------------------------------
+tabread - read a number from a table
+tabread4 - read a number from a table, with 4 point interpolation
+tabwrite - write a number to a table
+soundfiler - read and write tables to soundfiles
+------------------------------ MISC ----------------------------------
+loadbang - bang on load
+serial - serial device control for NT only
+netsend - send messages over the internet
+netreceive - receive them
+qlist - message sequencer
+textfile - file to message converter
+openpanel - "Open" dialog
+savepanel - "Save as" dialog
+bag - set of numbers
+poly - polyphonic voice allocation
+key, keyup - numeric key values from keyboard
+keyname - symbolic key name
+-------------------------- AUDIO MATH ----------------------------------
++~ -~ *~ /~ arithmetic on audio signals
+max~ min~ - maximum or minimum of 2 inputs
+clip~ - constrict signal to lie between two bounds
+q8_rsqrt~ - cheap reciprocal square root (beware -- 8 bits!)
+q8_sqrt~ - cheap square root (beware -- 8 bits!)
+wrap~ - wraparound (fractional part, sort of)
+fft~ - complex forward discrete Fourier transform
+ifft~ - complex inverse discrete Fourier transform
+rfft~ - real forward discrete Fourier transform
+rifft~ - real inverse discrete Fourier transform
+framp~ - output a ramp for each block
+mtof~, ftom~, rmstodb~, dbtorms~, rmstopow~, powtorms~ - acoustic conversions
+-------------------------- AUDIO GLUE ----------------------------------
+dac~ - audio output
+adc~ - audio input
+sig~ - convert numbers to audio signals
+line~ - generate audio ramps
+threshold~ detect signal thresholds
+snapshot~ - sample a signal (convert it back to a number)
+bang~ - send a bang message after each DSP block
+samplerate~ get the sample rate
+send~ - nonlocal signal connection with fanout
+receive~ - get signal from send~
+throw~ - add to a summing bus
+catch~ - define and read a summing bus
+block~ - specify block size and overlap
+switch~ - switch DSP computation on and off
+readsf~ - soundfile playback from disk (UNIX only.)
+writesf~ - record sound to disk (UNIX only.)
+-------------------- AUDIO OSCILLATORS AND TABLES ------------------------
+phasor~ - sawtooth oscillator
+cos~ - cosine
+osc~ - cosine oscillator
+tabwrite~ - write to a table
+tabplay~ - play back from a table (non-transposing)
+tabread~ - non-interpolating table read
+tabread4~ - four-point interpolating table read
+tabsend~ - write one block continuously to a table
+tabreceive~ read one block continuously from a table
+-------------------- AUDIO FILTERS ------------------------
+vcf~ - voltage controlled filter
+noise~ - white noise generator
+env~ - envelope follower
+hip~ - high pass filter
+lop~ - low pass filter
+bp~ - band pass filter
+biquad~ - raw filter
+samphold~ - sample and hold unit
+print~ - print out one or more "blocks"
+-------------------- AUDIO DELAY ------------------------
+delwrite~ - write to a delay line
+delread~ - read from a delay line
+vd~ - read from a delay line at a variable delay time
+------------------------------ SUBWINDOWS ----------------------------------
+pd - define a subwindow
+inlet - add an inlet to a pd
+outlet - add an outlet to a pd
+table - array of numbers in a subwindow
+------------------------------ DATA TEMPLATES -----------------------------
+template - define the fields in a template
+drawcurve, filledcurve - draw a curve
+drawpolygon, filledpolygon - draw a polygon
+plot - plot an array field
+drawnumber - print a numeric value
+------------------------------ ACCESSING DATA ----------------------------
+pointer - point to an object belonging to a template
+get - get numeric fields
+set - change numeric fields
+element - get an array element
+getsize - get the size of an array
+setsize - change the size of an array
+append - add an element to a list
+sublist - get a pointer into a list which is an element of another scalar
+scalar - draw a scalar on parent
+------------------------------ OBSOLETE ----------------------------
+scope~ (use tabwrite~ now)
+namecanvas
diff --git a/pd/doc/5.reference/0_all_guis-INTRO.txt b/pd/doc/5.reference/0_all_guis-INTRO.txt
new file mode 100644
index 00000000..4eda9f58
--- /dev/null
+++ b/pd/doc/5.reference/0_all_guis-INTRO.txt
@@ -0,0 +1,131 @@
+HOW TO MOVE A GUI-OBJECT:
+
+Of course by mouse, and:
+select a gui-object , then navigate the object by using the
+4 direction-keys: UP , DOWN , LEFT or RIGHT.
+If you press the SHIFT-Key too , the object will move 10 times faster.
+
+
+PROPERTIES-DIALOG-WINDOW:
+
+"dimensions(pix): size:" = square-size of the gui-objects in pixels.
+"dimensions(pix)(pix): width: height:" = width & height of the rectangular
+ gui-object in pixels.
+"selectable dimensions(pix): size:" = square-size of the selectable top-left
+ corner of my_canvas in pixels.
+"flash-time(ms)(ms): hold:" = flash-hold-time in msec = duration of activity,
+ if a bang-object was activated by any message-event
+ or by a mouse-click.
+"flash-time(ms)(ms): intrrpt:" = flash-interrupt-time in msec = duration
+ of inactivity , if an already activated bang is activated
+ once more.
+"output-range: left: right:" = hslider-bounds for input- as well as
+ output-values.
+"output-range: bottom: top:" = vslider-bounds for input- as well as
+ output-values.
+"non-zero-value: value:" = toggle has 2 value-states: zero and this value.
+"visible_rectangle(pix)(pix): width: height:" = width & height of a visible,
+ deactivated rectangle in pixels.
+"init" or "no init" = if "init"-mode is selected , the object gets a loadbang-
+ behavior and puts out its in-patch-saved value.
+ if "no init"-mode is selected, nothing will happen.
+"new&old" or "new-only" = the hdial- and vdial-object changes its state in 2 ways:
+ "new&old"-mode: output sends previous state off, current state on;
+ "new-only"-mode: output sends only current state on.
+"lin" or "log" = sliders and numberboxes can have linear or logarithmical scaling.
+"number:" = number of buttons of hdials and vdials.
+"log-height:" = is the graphical height of the numberbox in logarithmical mode.
+"steady on click" or "jump on click" = the 2 slider-objects
+ react to mouse-click in 2 ways:
+ "steady on click"-mode: slider-knob stays in position,
+ mouse and knob will move parallel;
+ "jump on click"-mode: slider-knob jumps immediately to new
+ mouse-position, positions of mouse and knob will be identical.
+"send-symbol:" = an output-message can be received by a receive-object
+ with the same send-symbol-name.
+"receive-symbol:" = a send-object with the same symbol-name can send
+ an input-message to the gui-object.
+"label: name:" = visible name of a gui-object; it will be moved together with
+ the gui-object.
+"label: x_off: y_off:" = coordinates of the label in relation to top-left
+ corner of gui-object.
+"label: font: fontsize:" = font-properties of label.
+"colors:" = a click on radiobuttons "backgd:", "front:" or
+ "label:" routes the button "compose color" and/or the preset-colors
+ to background- front- and label-color.
+ the 2 fields with "testlabel" and "o=||=o" will show you the 3 colors.
+ "compose color" opens a tcl/tk color-dialog.
+"Cancel" quits the properties-dialog without sending down the last changings in dialogbox.
+"Apply" sends down the changings.
+"OK" sends down the changings and quits the dialogbox.
+
+
+THE DOLLAR-THING:
+
+If you want to send to, or to receive from gui-objects,
+you have to write into the property-entry your send- or receive-name.
+If you want an unique-name, write $0 or $0-blabla, if you want to
+communicate with this gui-object in an abstraction, write $1
+or $1-blabla or $2 or $2-blabla in your property-entry.
+(send- , receive- or label- name)
+A new feature is: you can take the same send- and receive-name.
+If there is a send-name, the object will loose its output-rectangle,
+if there is a receive-name, the object will loose its input-rectangle;
+but the connective behavior is the same.
+
+GUI-MESSAGES:
+
+all gui-objects (bng, hsl, vsl, nbx, tgl, hdl, vdl, cnv and vu)
+ understand input-messages which change their properties.
+ except cnv has no input, so you have to send messages
+ to its receive-label (edit properties).
+"size 15 128" = change width & height of sliders and vu in pixels.
+"size 15" = change square-size of rdb, bng and tgl in pixels.
+"vis_size 800 600" = change width & height of visual rectangle
+ of my_canvas in pixels.
+"range 0.1 10.0" = change slider-boundaries for
+ input- as well as output-values.
+"nonzero 127.0" = change the nonzero-value of toggle.
+"flashtime 50 600" = change flash-interrupt- and
+ flash-hold-time of bng-object.
+"pos 150 170" = change the x-y-position of the top-left
+ corner of a gui-object in pixels;
+ "pos 0 0" is the top-left corner of your window;
+ the positive directions of x- and y-axes are right and down.
+"delta 15 17" = move the gui-object in relation to its
+ current position (in pixels).
+"color 0 22 22" = change background-, front- and
+ label-color of object with one of 30 presets.
+"color 0 22" = change background- and label-color
+ of vu and my_canvas with one of 30 presets.
+"color -16777216 (-1) -1" = change background-, (front-)
+ and label-color of object with RGB-values.
+ the RGB-value will be calculated:
+ -65536*RED-value (0 .. 255)
+ - 256*GREEN-value (0 .. 255)
+ - BLUE-value (0 .. 255) - 1.
+"number 10" = change number of dial-buttons.
+"log_height 128" = graphical dimension for logarithmical behavior
+ of numberbox.
+"steady 1" change slider-knob-behaviour on mouse-click.
+"single_change" change dial-behaviour
+ to output only the new state.
+"double_change" change dial-behaviour
+ to first release the previous button,
+ then output the state of the new button.
+"send fromgui" = change send-name of gui-objects,
+ except vu and cnv.
+"receive togui" = change receive-name of object.
+"label its_me" = change label-text of object.
+"label_pos 20 8" = change offset-coordinates of label-text.
+"label_font 0 10" = change font and fontsize of label-text.
+"init 1" = change initial loadbang-mode of gui-objects
+ except vu and cnv.
+"set 64" = change only the inner state and display
+ of gui-objects, except bng and cnv;
+ no output will result.
+"lin" = change scale-mode of slider to linear.
+"log" = change scale-mode of slider to logarithmical.
+"get_pos" = if my_canvas has a receive-name and a send-name
+ and you send the message "get_pos" to it,
+ you receive the current x- and y-coordinates.
diff --git a/pd/doc/5.reference/acoustics.pd b/pd/doc/5.reference/acoustics.pd
new file mode 100644
index 00000000..2a46f589
--- /dev/null
+++ b/pd/doc/5.reference/acoustics.pd
@@ -0,0 +1,40 @@
+#N canvas 163 25 582 408 12;
+#X obj 15 269 ftom;
+#X obj 8 10 mtof;
+#X obj 15 217 mtof;
+#X floatatom 15 189 0 0 0;
+#X floatatom 15 244 0 0 0;
+#X obj 64 10 ftom;
+#X floatatom 15 293 0 0 0;
+#X obj 120 11 dbtorms;
+#X obj 196 11 rmstodb;
+#X obj 275 11 dbtopow;
+#X obj 352 11 powtodb;
+#X text 21 53 The mtof object transposes a midi value into a frequency
+in Hertz \, so that "69" goes to "440". You can specify microtonal
+pitches as in "69.5" (a quarter tone higher than 69). Ftom does the
+reverse.;
+#X floatatom 147 185 0 0 0;
+#X floatatom 147 240 0 0 0;
+#X floatatom 147 289 0 0 0;
+#X obj 147 213 dbtorms;
+#X obj 147 265 rmstodb;
+#X floatatom 261 186 0 0 0;
+#X floatatom 261 241 0 0 0;
+#X floatatom 261 290 0 0 0;
+#X obj 261 214 dbtopow;
+#X obj 261 266 powtodb;
+#X text 27 336 Overflows and underflows are clipped.;
+#X text 300 376 updated for pd version 0.33;
+#X connect 0 0 6 0;
+#X connect 2 0 4 0;
+#X connect 3 0 2 0;
+#X connect 4 0 0 0;
+#X connect 12 0 15 0;
+#X connect 13 0 16 0;
+#X connect 15 0 13 0;
+#X connect 16 0 14 0;
+#X connect 17 0 20 0;
+#X connect 18 0 21 0;
+#X connect 20 0 18 0;
+#X connect 21 0 19 0;
diff --git a/pd/doc/5.reference/acoustics~.pd b/pd/doc/5.reference/acoustics~.pd
new file mode 100644
index 00000000..f7515339
--- /dev/null
+++ b/pd/doc/5.reference/acoustics~.pd
@@ -0,0 +1,81 @@
+#N canvas 35 42 813 458 12;
+#X obj 158 118 mtof~;
+#X obj 158 174 snapshot~;
+#X obj 698 132 metro 100;
+#X floatatom 158 205 0 0 0;
+#X obj 49 174 snapshot~;
+#X floatatom 49 55 0 0 0;
+#X floatatom 49 205 0 0 0;
+#X obj 49 118 ftom~;
+#X obj 264 174 snapshot~;
+#X floatatom 264 205 0 0 0;
+#X obj 264 118 dbtorms~;
+#X obj 697 58 loadbang;
+#X msg 709 88 \; pd dsp 1;
+#X obj 49 86 sig~;
+#X floatatom 158 55 0 0 0;
+#X obj 158 86 sig~;
+#X floatatom 264 54 0 0 0;
+#X obj 264 86 sig~;
+#X obj 492 172 snapshot~;
+#X floatatom 492 203 0 0 0;
+#X obj 383 172 snapshot~;
+#X floatatom 383 53 0 0 0;
+#X floatatom 383 203 0 0 0;
+#X obj 607 172 snapshot~;
+#X floatatom 607 203 0 0 0;
+#X obj 383 84 sig~;
+#X floatatom 492 53 0 0 0;
+#X obj 492 84 sig~;
+#X floatatom 607 53 0 0 0;
+#X obj 607 84 sig~;
+#X obj 383 115 rmstodb~;
+#X obj 492 115 dbtopow~;
+#X obj 607 115 powtodb~;
+#X obj 17 10 mtof~;
+#X text 70 11 (etc) - conversions for audio signals;
+#X text 60 400 see also:;
+#X obj 145 400 mtof;
+#X text 192 400 (etc.);
+#X text 547 416 updated for Pd version 0.33;
+#X text 43 241 These objects convert MIDI pitch to frequency and back
+\, and dB to and from RMS and power. THey take audio signals as input
+and output (and work sample by sample.) Since they call library math
+functions \, they may be much more expensive than other workaday tilde
+objects such as *~ and osc~ \, depending on your hardware and math
+library.;
+#X text 41 343 Boundary conditions are handled "reasonably". 100 db
+is assigned an RMS of 1 \, and dbtorms~ and dbtopow~ output true zero
+for 0 dB and less.;
+#X connect 0 0 1 0;
+#X connect 1 0 3 0;
+#X connect 2 0 1 0;
+#X connect 2 0 8 0;
+#X connect 2 0 4 0;
+#X connect 2 0 20 0;
+#X connect 2 0 18 0;
+#X connect 2 0 23 0;
+#X connect 4 0 6 0;
+#X connect 5 0 13 0;
+#X connect 7 0 4 0;
+#X connect 8 0 9 0;
+#X connect 10 0 8 0;
+#X connect 11 0 2 0;
+#X connect 11 0 12 0;
+#X connect 13 0 7 0;
+#X connect 14 0 15 0;
+#X connect 15 0 0 0;
+#X connect 16 0 17 0;
+#X connect 17 0 10 0;
+#X connect 18 0 19 0;
+#X connect 20 0 22 0;
+#X connect 21 0 25 0;
+#X connect 23 0 24 0;
+#X connect 25 0 30 0;
+#X connect 26 0 27 0;
+#X connect 27 0 31 0;
+#X connect 28 0 29 0;
+#X connect 29 0 32 0;
+#X connect 30 0 20 0;
+#X connect 31 0 18 0;
+#X connect 32 0 23 0;
diff --git a/pd/doc/5.reference/adc~_dac~.pd b/pd/doc/5.reference/adc~_dac~.pd
new file mode 100644
index 00000000..e97429b6
--- /dev/null
+++ b/pd/doc/5.reference/adc~_dac~.pd
@@ -0,0 +1,11 @@
+#N canvas 195 155 575 293 12;
+#X obj 8 11 adc~;
+#X obj 72 11 dac~;
+#X obj 63 121 adc~ 5;
+#X text 143 121 (input from channel 5 only);
+#X obj 61 145 dac~ 1 2 5 23;
+#X text 184 145 (output to channels 1 \, 2 \, 5 \, and 23);
+#X text 16 173 The actual number of channels Pd inputs and outputs are set on Pd's command line. You can open patches that want to use more channels \, and channel numbers out of rance will be dropped (dac~) or appear as zero (adc~).;
+#X text 308 254 updated for Pd version 0.33;
+#X text 122 9 - audio I/O;
+#X text 8 46 Adc~ and dac~ rovide real-time audio input and output for Pd \, respectively \, whether analog or digital. By default they are stereo but you can specify channel numbers as in:;
diff --git a/pd/doc/5.reference/append.pd b/pd/doc/5.reference/append.pd
new file mode 100644
index 00000000..3b5076b8
--- /dev/null
+++ b/pd/doc/5.reference/append.pd
@@ -0,0 +1,629 @@
+#N struct help-append-template1 float x float y float z;
+#N canvas 330 8 595 450 12;
+#X text 15 344 see also:;
+#N canvas 164 72 425 146 help-append-template1 0;
+#X obj 60 21 template float x float y float z;
+#X obj 18 81 filledpolygon z z 0 0 0 20 0 20 30 0 30;
+#X restore 357 373 pd help-append-template1;
+#X obj 141 393 template;
+#X obj 16 368 get;
+#X obj 48 368 set;
+#X obj 148 368 getsize;
+#X obj 215 368 setsize;
+#X obj 218 393 element;
+#X obj 15 394 sublist;
+#X obj 83 393 scalar;
+#N canvas 0 0 276 163 help-append-data 1;
+#X scalar help-append-template1 129 129 129 \;;
+#X scalar help-append-template1 130 130 130 \;;
+#X scalar help-append-template1 132 132 132 \;;
+#X scalar help-append-template1 133 133 133 \;;
+#X scalar help-append-template1 135 135 135 \;;
+#X scalar help-append-template1 136 136 136 \;;
+#X scalar help-append-template1 137 137 137 \;;
+#X scalar help-append-template1 138 138 138 \;;
+#X scalar help-append-template1 139 139 139 \;;
+#X scalar help-append-template1 140 140 140 \;;
+#X scalar help-append-template1 141 141 141 \;;
+#X scalar help-append-template1 142 142 142 \;;
+#X scalar help-append-template1 141 141 141 \;;
+#X scalar help-append-template1 139 139 139 \;;
+#X scalar help-append-template1 136 136 136 \;;
+#X scalar help-append-template1 128 128 128 \;;
+#X scalar help-append-template1 125 125 125 \;;
+#X scalar help-append-template1 122 122 122 \;;
+#X scalar help-append-template1 119 119 119 \;;
+#X scalar help-append-template1 117 117 117 \;;
+#X scalar help-append-template1 114 114 114 \;;
+#X scalar help-append-template1 112 112 112 \;;
+#X scalar help-append-template1 110 110 110 \;;
+#X scalar help-append-template1 108 108 108 \;;
+#X scalar help-append-template1 107 107 107 \;;
+#X scalar help-append-template1 105 105 105 \;;
+#X scalar help-append-template1 104 104 104 \;;
+#X scalar help-append-template1 102 102 102 \;;
+#X scalar help-append-template1 101 101 101 \;;
+#X scalar help-append-template1 100 100 100 \;;
+#X scalar help-append-template1 99 99 99 \;;
+#X scalar help-append-template1 98 98 98 \;;
+#X scalar help-append-template1 97 97 97 \;;
+#X scalar help-append-template1 96 96 96 \;;
+#X scalar help-append-template1 95 95 95 \;;
+#X scalar help-append-template1 94 94 94 \;;
+#X scalar help-append-template1 93 93 93 \;;
+#X scalar help-append-template1 92 92 92 \;;
+#X scalar help-append-template1 89 89 89 \;;
+#X scalar help-append-template1 87 87 87 \;;
+#X scalar help-append-template1 85 85 85 \;;
+#X scalar help-append-template1 84 84 84 \;;
+#X scalar help-append-template1 83 83 83 \;;
+#X scalar help-append-template1 82 82 82 \;;
+#X scalar help-append-template1 81 81 81 \;;
+#X scalar help-append-template1 79 79 79 \;;
+#X scalar help-append-template1 77 77 77 \;;
+#X scalar help-append-template1 74 74 74 \;;
+#X scalar help-append-template1 72 72 72 \;;
+#X scalar help-append-template1 70 70 70 \;;
+#X scalar help-append-template1 68 68 68 \;;
+#X scalar help-append-template1 67 67 67 \;;
+#X scalar help-append-template1 66 66 66 \;;
+#X scalar help-append-template1 64 64 64 \;;
+#X scalar help-append-template1 62 62 62 \;;
+#X scalar help-append-template1 61 61 61 \;;
+#X scalar help-append-template1 59 59 59 \;;
+#X scalar help-append-template1 58 58 58 \;;
+#X scalar help-append-template1 57 57 57 \;;
+#X scalar help-append-template1 56 56 56 \;;
+#X scalar help-append-template1 55 55 55 \;;
+#X scalar help-append-template1 54 54 54 \;;
+#X scalar help-append-template1 53 53 53 \;;
+#X scalar help-append-template1 52 52 52 \;;
+#X scalar help-append-template1 50 50 50 \;;
+#X scalar help-append-template1 49 49 49 \;;
+#X scalar help-append-template1 47 47 47 \;;
+#X scalar help-append-template1 46 46 46 \;;
+#X scalar help-append-template1 45 45 45 \;;
+#X scalar help-append-template1 44 44 44 \;;
+#X scalar help-append-template1 43 43 43 \;;
+#X scalar help-append-template1 44 44 44 \;;
+#X scalar help-append-template1 45 45 45 \;;
+#X scalar help-append-template1 47 47 47 \;;
+#X scalar help-append-template1 48 48 48 \;;
+#X scalar help-append-template1 49 49 49 \;;
+#X scalar help-append-template1 51 51 51 \;;
+#X scalar help-append-template1 52 52 52 \;;
+#X scalar help-append-template1 54 54 54 \;;
+#X scalar help-append-template1 56 56 56 \;;
+#X scalar help-append-template1 58 58 58 \;;
+#X scalar help-append-template1 60 60 60 \;;
+#X scalar help-append-template1 62 62 62 \;;
+#X scalar help-append-template1 68 68 68 \;;
+#X scalar help-append-template1 70 70 70 \;;
+#X scalar help-append-template1 76 76 76 \;;
+#X scalar help-append-template1 79 79 79 \;;
+#X scalar help-append-template1 85 85 85 \;;
+#X scalar help-append-template1 88 88 88 \;;
+#X scalar help-append-template1 91 91 91 \;;
+#X scalar help-append-template1 99 99 99 \;;
+#X scalar help-append-template1 101 101 101 \;;
+#X scalar help-append-template1 103 103 103 \;;
+#X scalar help-append-template1 105 105 105 \;;
+#X scalar help-append-template1 107 107 107 \;;
+#X scalar help-append-template1 109 109 109 \;;
+#X scalar help-append-template1 111 111 111 \;;
+#X scalar help-append-template1 113 113 113 \;;
+#X scalar help-append-template1 115 115 115 \;;
+#X scalar help-append-template1 117 117 117 \;;
+#X scalar help-append-template1 119 119 119 \;;
+#X scalar help-append-template1 121 121 121 \;;
+#X scalar help-append-template1 123 123 123 \;;
+#X scalar help-append-template1 129 129 129 \;;
+#X scalar help-append-template1 132 132 132 \;;
+#X scalar help-append-template1 134 134 134 \;;
+#X scalar help-append-template1 136 136 136 \;;
+#X scalar help-append-template1 137 137 137 \;;
+#X scalar help-append-template1 139 139 139 \;;
+#X scalar help-append-template1 140 140 140 \;;
+#X scalar help-append-template1 142 142 142 \;;
+#X scalar help-append-template1 143 143 143 \;;
+#X scalar help-append-template1 144 144 144 \;;
+#X scalar help-append-template1 146 146 146 \;;
+#X scalar help-append-template1 147 147 147 \;;
+#X scalar help-append-template1 149 149 149 \;;
+#X scalar help-append-template1 150 150 150 \;;
+#X scalar help-append-template1 152 152 152 \;;
+#X scalar help-append-template1 153 153 153 \;;
+#X scalar help-append-template1 154 154 154 \;;
+#X scalar help-append-template1 155 155 155 \;;
+#X scalar help-append-template1 156 156 156 \;;
+#X scalar help-append-template1 157 157 157 \;;
+#X scalar help-append-template1 158 158 158 \;;
+#X scalar help-append-template1 160 160 160 \;;
+#X scalar help-append-template1 161 161 161 \;;
+#X scalar help-append-template1 162 162 162 \;;
+#X scalar help-append-template1 163 163 163 \;;
+#X scalar help-append-template1 164 164 164 \;;
+#X scalar help-append-template1 165 165 165 \;;
+#X scalar help-append-template1 166 166 166 \;;
+#X scalar help-append-template1 168 168 168 \;;
+#X scalar help-append-template1 169 169 169 \;;
+#X scalar help-append-template1 170 170 170 \;;
+#X scalar help-append-template1 172 172 172 \;;
+#X scalar help-append-template1 173 173 173 \;;
+#X scalar help-append-template1 175 175 175 \;;
+#X scalar help-append-template1 177 177 177 \;;
+#X scalar help-append-template1 179 179 179 \;;
+#X scalar help-append-template1 180 180 180 \;;
+#X scalar help-append-template1 181 181 181 \;;
+#X scalar help-append-template1 182 182 182 \;;
+#X scalar help-append-template1 181 181 181 \;;
+#X scalar help-append-template1 178 178 178 \;;
+#X scalar help-append-template1 170 170 170 \;;
+#X scalar help-append-template1 160 160 160 \;;
+#X scalar help-append-template1 150 150 150 \;;
+#X scalar help-append-template1 140 140 140 \;;
+#X scalar help-append-template1 137 137 137 \;;
+#X scalar help-append-template1 134 134 134 \;;
+#X scalar help-append-template1 131 131 131 \;;
+#X scalar help-append-template1 130 130 130 \;;
+#X scalar help-append-template1 128 128 128 \;;
+#X scalar help-append-template1 127 127 127 \;;
+#X scalar help-append-template1 125 125 125 \;;
+#X scalar help-append-template1 123 123 123 \;;
+#X scalar help-append-template1 120 120 120 \;;
+#X scalar help-append-template1 118 118 118 \;;
+#X scalar help-append-template1 115 115 115 \;;
+#X scalar help-append-template1 112 112 112 \;;
+#X scalar help-append-template1 110 110 110 \;;
+#X scalar help-append-template1 104 104 104 \;;
+#X scalar help-append-template1 102 102 102 \;;
+#X scalar help-append-template1 100 100 100 \;;
+#X scalar help-append-template1 99 99 99 \;;
+#X scalar help-append-template1 98 98 98 \;;
+#X scalar help-append-template1 97 97 97 \;;
+#X scalar help-append-template1 96 96 96 \;;
+#X scalar help-append-template1 95 95 95 \;;
+#X scalar help-append-template1 94 94 94 \;;
+#X scalar help-append-template1 92 92 92 \;;
+#X scalar help-append-template1 91 91 91 \;;
+#X scalar help-append-template1 90 90 90 \;;
+#X scalar help-append-template1 88 88 88 \;;
+#X scalar help-append-template1 86 86 86 \;;
+#X scalar help-append-template1 84 84 84 \;;
+#X scalar help-append-template1 82 82 82 \;;
+#X scalar help-append-template1 80 80 80 \;;
+#X scalar help-append-template1 79 79 79 \;;
+#X scalar help-append-template1 78 78 78 \;;
+#X scalar help-append-template1 77 77 77 \;;
+#X scalar help-append-template1 76 76 76 \;;
+#X scalar help-append-template1 75 75 75 \;;
+#X scalar help-append-template1 74 74 74 \;;
+#X scalar help-append-template1 75 75 75 \;;
+#X scalar help-append-template1 77 77 77 \;;
+#X scalar help-append-template1 80 80 80 \;;
+#X scalar help-append-template1 83 83 83 \;;
+#X scalar help-append-template1 85 85 85 \;;
+#X scalar help-append-template1 91 91 91 \;;
+#X scalar help-append-template1 99 99 99 \;;
+#X scalar help-append-template1 105 105 105 \;;
+#X scalar help-append-template1 111 111 111 \;;
+#X scalar help-append-template1 117 117 117 \;;
+#X scalar help-append-template1 119 119 119 \;;
+#X scalar help-append-template1 121 121 121 \;;
+#X scalar help-append-template1 123 123 123 \;;
+#X scalar help-append-template1 125 125 125 \;;
+#X scalar help-append-template1 127 127 127 \;;
+#X scalar help-append-template1 129 129 129 \;;
+#X scalar help-append-template1 131 131 131 \;;
+#X scalar help-append-template1 133 133 133 \;;
+#X scalar help-append-template1 135 135 135 \;;
+#X scalar help-append-template1 136 136 136 \;;
+#X scalar help-append-template1 138 138 138 \;;
+#X scalar help-append-template1 139 139 139 \;;
+#X scalar help-append-template1 140 140 140 \;;
+#X scalar help-append-template1 142 142 142 \;;
+#X scalar help-append-template1 144 144 144 \;;
+#X scalar help-append-template1 145 145 145 \;;
+#X scalar help-append-template1 147 147 147 \;;
+#X scalar help-append-template1 149 149 149 \;;
+#X scalar help-append-template1 150 150 150 \;;
+#X scalar help-append-template1 152 152 152 \;;
+#X scalar help-append-template1 153 153 153 \;;
+#X scalar help-append-template1 154 154 154 \;;
+#X scalar help-append-template1 155 155 155 \;;
+#X scalar help-append-template1 156 156 156 \;;
+#X scalar help-append-template1 157 157 157 \;;
+#X scalar help-append-template1 159 159 159 \;;
+#X scalar help-append-template1 160 160 160 \;;
+#X scalar help-append-template1 161 161 161 \;;
+#X scalar help-append-template1 163 163 163 \;;
+#X scalar help-append-template1 165 165 165 \;;
+#X scalar help-append-template1 166 166 166 \;;
+#X scalar help-append-template1 167 167 167 \;;
+#X scalar help-append-template1 168 168 168 \;;
+#X scalar help-append-template1 167 167 167 \;;
+#X scalar help-append-template1 164 164 164 \;;
+#X scalar help-append-template1 161 161 161 \;;
+#X scalar help-append-template1 153 153 153 \;;
+#X scalar help-append-template1 145 145 145 \;;
+#X scalar help-append-template1 142 142 142 \;;
+#X scalar help-append-template1 139 139 139 \;;
+#X scalar help-append-template1 137 137 137 \;;
+#X scalar help-append-template1 135 135 135 \;;
+#X scalar help-append-template1 133 133 133 \;;
+#X scalar help-append-template1 131 131 131 \;;
+#X scalar help-append-template1 130 130 130 \;;
+#X scalar help-append-template1 128 128 128 \;;
+#X scalar help-append-template1 126 126 126 \;;
+#X scalar help-append-template1 124 124 124 \;;
+#X scalar help-append-template1 121 121 121 \;;
+#X scalar help-append-template1 118 118 118 \;;
+#X scalar help-append-template1 116 116 116 \;;
+#X scalar help-append-template1 113 113 113 \;;
+#X scalar help-append-template1 111 111 111 \;;
+#X scalar help-append-template1 109 109 109 \;;
+#X scalar help-append-template1 106 106 106 \;;
+#X scalar help-append-template1 104 104 104 \;;
+#X scalar help-append-template1 102 102 102 \;;
+#X scalar help-append-template1 100 100 100 \;;
+#X scalar help-append-template1 98 98 98 \;;
+#X scalar help-append-template1 96 96 96 \;;
+#X scalar help-append-template1 94 94 94 \;;
+#X scalar help-append-template1 92 92 92 \;;
+#X scalar help-append-template1 90 90 90 \;;
+#X scalar help-append-template1 88 88 88 \;;
+#X scalar help-append-template1 87 87 87 \;;
+#X scalar help-append-template1 85 85 85 \;;
+#X scalar help-append-template1 84 84 84 \;;
+#X scalar help-append-template1 86 86 86 \;;
+#X scalar help-append-template1 92 92 92 \;;
+#X scalar help-append-template1 100 100 100 \;;
+#X scalar help-append-template1 108 108 108 \;;
+#X scalar help-append-template1 118 118 118 \;;
+#X scalar help-append-template1 128 128 128 \;;
+#X scalar help-append-template1 138 138 138 \;;
+#X scalar help-append-template1 150 150 150 \;;
+#X scalar help-append-template1 160 160 160 \;;
+#X scalar help-append-template1 170 170 170 \;;
+#X scalar help-append-template1 180 180 180 \;;
+#X scalar help-append-template1 188 188 188 \;;
+#X scalar help-append-template1 191 191 191 \;;
+#X scalar help-append-template1 194 194 194 \;;
+#X scalar help-append-template1 196 196 196 \;;
+#X scalar help-append-template1 197 197 197 \;;
+#X scalar help-append-template1 195 195 195 \;;
+#X scalar help-append-template1 185 185 185 \;;
+#X scalar help-append-template1 171 171 171 \;;
+#X scalar help-append-template1 157 157 157 \;;
+#X scalar help-append-template1 143 143 143 \;;
+#X scalar help-append-template1 133 133 133 \;;
+#X scalar help-append-template1 125 125 125 \;;
+#X scalar help-append-template1 123 123 123 \;;
+#X scalar help-append-template1 122 122 122 \;;
+#X scalar help-append-template1 123 123 123 \;;
+#X scalar help-append-template1 125 125 125 \;;
+#X scalar help-append-template1 135 135 135 \;;
+#X scalar help-append-template1 147 147 147 \;;
+#X scalar help-append-template1 161 161 161 \;;
+#X scalar help-append-template1 177 177 177 \;;
+#X scalar help-append-template1 191 191 191 \;;
+#X scalar help-append-template1 203 203 203 \;;
+#X scalar help-append-template1 211 211 211 \;;
+#X scalar help-append-template1 212 212 212 \;;
+#X scalar help-append-template1 213 213 213 \;;
+#X scalar help-append-template1 212 212 212 \;;
+#X scalar help-append-template1 211 211 211 \;;
+#X scalar help-append-template1 201 201 201 \;;
+#X scalar help-append-template1 187 187 187 \;;
+#X scalar help-append-template1 171 171 171 \;;
+#X scalar help-append-template1 155 155 155 \;;
+#X scalar help-append-template1 143 143 143 \;;
+#X scalar help-append-template1 140 140 140 \;;
+#X scalar help-append-template1 138 138 138 \;;
+#X scalar help-append-template1 139 139 139 \;;
+#X scalar help-append-template1 141 141 141 \;;
+#X scalar help-append-template1 153 153 153 \;;
+#X scalar help-append-template1 163 163 163 \;;
+#X scalar help-append-template1 173 173 173 \;;
+#X scalar help-append-template1 176 176 176 \;;
+#X scalar help-append-template1 178 178 178 \;;
+#X scalar help-append-template1 179 179 179 \;;
+#X scalar help-append-template1 177 177 177 \;;
+#X scalar help-append-template1 167 167 167 \;;
+#X scalar help-append-template1 149 149 149 \;;
+#X scalar help-append-template1 129 129 129 \;;
+#X scalar help-append-template1 111 111 111 \;;
+#X scalar help-append-template1 97 97 97 \;;
+#X scalar help-append-template1 87 87 87 \;;
+#X scalar help-append-template1 84 84 84 \;;
+#X scalar help-append-template1 85 85 85 \;;
+#X scalar help-append-template1 91 91 91 \;;
+#X scalar help-append-template1 99 99 99 \;;
+#X scalar help-append-template1 111 111 111 \;;
+#X scalar help-append-template1 123 123 123 \;;
+#X scalar help-append-template1 135 135 135 \;;
+#X scalar help-append-template1 147 147 147 \;;
+#X scalar help-append-template1 155 155 155 \;;
+#X scalar help-append-template1 156 156 156 \;;
+#X scalar help-append-template1 154 154 154 \;;
+#X scalar help-append-template1 144 144 144 \;;
+#X scalar help-append-template1 130 130 130 \;;
+#X scalar help-append-template1 114 114 114 \;;
+#X scalar help-append-template1 100 100 100 \;;
+#X scalar help-append-template1 90 90 90 \;;
+#X scalar help-append-template1 88 88 88 \;;
+#X scalar help-append-template1 89 89 89 \;;
+#X scalar help-append-template1 91 91 91 \;;
+#X scalar help-append-template1 99 99 99 \;;
+#X scalar help-append-template1 111 111 111 \;;
+#X scalar help-append-template1 127 127 127 \;;
+#X scalar help-append-template1 141 141 141 \;;
+#X scalar help-append-template1 149 149 149 \;;
+#X scalar help-append-template1 147 147 147 \;;
+#X scalar help-append-template1 135 135 135 \;;
+#X scalar help-append-template1 123 123 123 \;;
+#X scalar help-append-template1 107 107 107 \;;
+#X scalar help-append-template1 91 91 91 \;;
+#X scalar help-append-template1 79 79 79 \;;
+#X scalar help-append-template1 71 71 71 \;;
+#X scalar help-append-template1 70 70 70 \;;
+#X scalar help-append-template1 71 71 71 \;;
+#X scalar help-append-template1 72 72 72 \;;
+#X scalar help-append-template1 82 82 82 \;;
+#X scalar help-append-template1 92 92 92 \;;
+#X scalar help-append-template1 102 102 102 \;;
+#X scalar help-append-template1 104 104 104 \;;
+#X scalar help-append-template1 103 103 103 \;;
+#X scalar help-append-template1 102 102 102 \;;
+#X scalar help-append-template1 92 92 92 \;;
+#X scalar help-append-template1 80 80 80 \;;
+#X scalar help-append-template1 68 68 68 \;;
+#X scalar help-append-template1 56 56 56 \;;
+#X scalar help-append-template1 53 53 53 \;;
+#X scalar help-append-template1 51 51 51 \;;
+#X scalar help-append-template1 52 52 52 \;;
+#X scalar help-append-template1 53 53 53 \;;
+#X scalar help-append-template1 59 59 59 \;;
+#X scalar help-append-template1 65 65 65 \;;
+#X scalar help-append-template1 73 73 73 \;;
+#X scalar help-append-template1 76 76 76 \;;
+#X scalar help-append-template1 78 78 78 \;;
+#X scalar help-append-template1 81 81 81 \;;
+#X scalar help-append-template1 83 83 83 \;;
+#X scalar help-append-template1 85 85 85 \;;
+#X scalar help-append-template1 86 86 86 \;;
+#X scalar help-append-template1 87 87 87 \;;
+#X scalar help-append-template1 88 88 88 \;;
+#X scalar help-append-template1 90 90 90 \;;
+#X scalar help-append-template1 92 92 92 \;;
+#X scalar help-append-template1 94 94 94 \;;
+#X scalar help-append-template1 102 102 102 \;;
+#X scalar help-append-template1 110 110 110 \;;
+#X scalar help-append-template1 118 118 118 \;;
+#X scalar help-append-template1 121 121 121 \;;
+#X scalar help-append-template1 124 124 124 \;;
+#X scalar help-append-template1 127 127 127 \;;
+#X scalar help-append-template1 129 129 129 \;;
+#X scalar help-append-template1 132 132 132 \;;
+#X scalar help-append-template1 134 134 134 \;;
+#X scalar help-append-template1 136 136 136 \;;
+#X scalar help-append-template1 138 138 138 \;;
+#X scalar help-append-template1 140 140 140 \;;
+#X scalar help-append-template1 141 141 141 \;;
+#X scalar help-append-template1 143 143 143 \;;
+#X scalar help-append-template1 145 145 145 \;;
+#X scalar help-append-template1 146 146 146 \;;
+#X scalar help-append-template1 148 148 148 \;;
+#X scalar help-append-template1 149 149 149 \;;
+#X scalar help-append-template1 150 150 150 \;;
+#X scalar help-append-template1 151 151 151 \;;
+#X scalar help-append-template1 153 153 153 \;;
+#X scalar help-append-template1 155 155 155 \;;
+#X scalar help-append-template1 156 156 156 \;;
+#X scalar help-append-template1 158 158 158 \;;
+#X scalar help-append-template1 160 160 160 \;;
+#X scalar help-append-template1 161 161 161 \;;
+#X scalar help-append-template1 163 163 163 \;;
+#X scalar help-append-template1 164 164 164 \;;
+#X scalar help-append-template1 165 165 165 \;;
+#X scalar help-append-template1 166 166 166 \;;
+#X scalar help-append-template1 168 168 168 \;;
+#X scalar help-append-template1 170 170 170 \;;
+#X scalar help-append-template1 172 172 172 \;;
+#X scalar help-append-template1 174 174 174 \;;
+#X scalar help-append-template1 176 176 176 \;;
+#X scalar help-append-template1 178 178 178 \;;
+#X scalar help-append-template1 179 179 179 \;;
+#X scalar help-append-template1 180 180 180 \;;
+#X scalar help-append-template1 182 182 182 \;;
+#X scalar help-append-template1 183 183 183 \;;
+#X scalar help-append-template1 185 185 185 \;;
+#X scalar help-append-template1 187 187 187 \;;
+#X scalar help-append-template1 189 189 189 \;;
+#X scalar help-append-template1 190 190 190 \;;
+#X scalar help-append-template1 192 192 192 \;;
+#X scalar help-append-template1 194 194 194 \;;
+#X scalar help-append-template1 196 196 196 \;;
+#X scalar help-append-template1 198 198 198 \;;
+#X scalar help-append-template1 199 199 199 \;;
+#X scalar help-append-template1 200 200 200 \;;
+#X scalar help-append-template1 201 201 201 \;;
+#X scalar help-append-template1 203 203 203 \;;
+#X scalar help-append-template1 204 204 204 \;;
+#X scalar help-append-template1 206 206 206 \;;
+#X scalar help-append-template1 207 207 207 \;;
+#X scalar help-append-template1 209 209 209 \;;
+#X scalar help-append-template1 210 210 210 \;;
+#X scalar help-append-template1 211 211 211 \;;
+#X scalar help-append-template1 212 212 212 \;;
+#X scalar help-append-template1 213 213 213 \;;
+#X scalar help-append-template1 223 223 223 \;;
+#X scalar help-append-template1 226 226 226 \;;
+#X scalar help-append-template1 228 228 228 \;;
+#X scalar help-append-template1 229 229 229 \;;
+#X scalar help-append-template1 230 230 230 \;;
+#X scalar help-append-template1 231 231 231 \;;
+#X scalar help-append-template1 232 232 232 \;;
+#X scalar help-append-template1 233 233 233 \;;
+#X scalar help-append-template1 234 234 234 \;;
+#X scalar help-append-template1 233 233 233 \;;
+#X scalar help-append-template1 225 225 225 \;;
+#X scalar help-append-template1 217 217 217 \;;
+#X scalar help-append-template1 205 205 205 \;;
+#X scalar help-append-template1 195 195 195 \;;
+#X scalar help-append-template1 183 183 183 \;;
+#X scalar help-append-template1 173 173 173 \;;
+#X scalar help-append-template1 163 163 163 \;;
+#X scalar help-append-template1 155 155 155 \;;
+#X scalar help-append-template1 152 152 152 \;;
+#X scalar help-append-template1 150 150 150 \;;
+#X scalar help-append-template1 148 148 148 \;;
+#X scalar help-append-template1 147 147 147 \;;
+#X scalar help-append-template1 145 145 145 \;;
+#X scalar help-append-template1 143 143 143 \;;
+#X scalar help-append-template1 141 141 141 \;;
+#X scalar help-append-template1 139 139 139 \;;
+#X scalar help-append-template1 136 136 136 \;;
+#X scalar help-append-template1 133 133 133 \;;
+#X scalar help-append-template1 130 130 130 \;;
+#X scalar help-append-template1 122 122 122 \;;
+#X scalar help-append-template1 116 116 116 \;;
+#X scalar help-append-template1 113 113 113 \;;
+#X scalar help-append-template1 110 110 110 \;;
+#X scalar help-append-template1 108 108 108 \;;
+#X scalar help-append-template1 106 106 106 \;;
+#X scalar help-append-template1 104 104 104 \;;
+#X scalar help-append-template1 102 102 102 \;;
+#X scalar help-append-template1 100 100 100 \;;
+#X scalar help-append-template1 99 99 99 \;;
+#X scalar help-append-template1 98 98 98 \;;
+#X scalar help-append-template1 96 96 96 \;;
+#X scalar help-append-template1 95 95 95 \;;
+#X scalar help-append-template1 93 93 93 \;;
+#X scalar help-append-template1 92 92 92 \;;
+#X scalar help-append-template1 90 90 90 \;;
+#X scalar help-append-template1 88 88 88 \;;
+#X scalar help-append-template1 85 85 85 \;;
+#X scalar help-append-template1 82 82 82 \;;
+#X scalar help-append-template1 76 76 76 \;;
+#X scalar help-append-template1 74 74 74 \;;
+#X scalar help-append-template1 71 71 71 \;;
+#X scalar help-append-template1 69 69 69 \;;
+#X scalar help-append-template1 67 67 67 \;;
+#X scalar help-append-template1 65 65 65 \;;
+#X scalar help-append-template1 63 63 63 \;;
+#X scalar help-append-template1 62 62 62 \;;
+#X scalar help-append-template1 60 60 60 \;;
+#X scalar help-append-template1 58 58 58 \;;
+#X scalar help-append-template1 56 56 56 \;;
+#X scalar help-append-template1 54 54 54 \;;
+#X scalar help-append-template1 51 51 51 \;;
+#X scalar help-append-template1 49 49 49 \;;
+#X scalar help-append-template1 47 47 47 \;;
+#X scalar help-append-template1 45 45 45 \;;
+#X scalar help-append-template1 44 44 44 \;;
+#X scalar help-append-template1 42 42 42 \;;
+#X scalar help-append-template1 40 40 40 \;;
+#X scalar help-append-template1 38 38 38 \;;
+#X scalar help-append-template1 36 36 36 \;;
+#X scalar help-append-template1 34 34 34 \;;
+#X scalar help-append-template1 33 33 33 \;;
+#X scalar help-append-template1 32 32 32 \;;
+#X scalar help-append-template1 31 31 31 \;;
+#X scalar help-append-template1 30 30 30 \;;
+#X scalar help-append-template1 28 28 28 \;;
+#X scalar help-append-template1 27 27 27 \;;
+#X scalar help-append-template1 25 25 25 \;;
+#X scalar help-append-template1 24 24 24 \;;
+#X scalar help-append-template1 23 23 23 \;;
+#X scalar help-append-template1 22 22 22 \;;
+#X scalar help-append-template1 21 21 21 \;;
+#X scalar help-append-template1 20 20 20 \;;
+#X scalar help-append-template1 19 19 19 \;;
+#X scalar help-append-template1 18 18 18 \;;
+#X scalar help-append-template1 16 16 16 \;;
+#X scalar help-append-template1 15 15 15 \;;
+#X scalar help-append-template1 14 14 14 \;;
+#X scalar help-append-template1 13 13 13 \;;
+#X scalar help-append-template1 12 12 12 \;;
+#X scalar help-append-template1 11 11 11 \;;
+#X scalar help-append-template1 10 10 10 \;;
+#X scalar help-append-template1 9 9 9 \;;
+#X scalar help-append-template1 8 8 8 \;;
+#X scalar help-append-template1 7 7 7 \;;
+#X scalar help-append-template1 6 6 6 \;;
+#X scalar help-append-template1 5 5 5 \;;
+#X scalar help-append-template1 4 4 4 \;;
+#X scalar help-append-template1 3 3 3 \;;
+#X scalar help-append-template1 2 2 2 \;;
+#X scalar help-append-template1 3 3 3 \;;
+#X scalar help-append-template1 73 73 73 \;;
+#X scalar help-append-template1 75 75 75 \;;
+#X scalar help-append-template1 77 77 77 \;;
+#X scalar help-append-template1 79 79 79 \;;
+#X scalar help-append-template1 81 81 81 \;;
+#X scalar help-append-template1 82 82 82 \;;
+#X scalar help-append-template1 84 84 84 \;;
+#X scalar help-append-template1 85 85 85 \;;
+#X scalar help-append-template1 86 86 86 \;;
+#X scalar help-append-template1 87 87 87 \;;
+#X scalar help-append-template1 93 93 93 \;;
+#X scalar help-append-template1 96 96 96 \;;
+#X scalar help-append-template1 98 98 98 \;;
+#X scalar help-append-template1 100 100 100 \;;
+#X scalar help-append-template1 101 101 101 \;;
+#X scalar help-append-template1 102 102 102 \;;
+#X scalar help-append-template1 103 103 103 \;;
+#X scalar help-append-template1 104 104 104 \;;
+#X scalar help-append-template1 105 105 105 \;;
+#X scalar help-append-template1 106 106 106 \;;
+#X scalar help-append-template1 107 107 107 \;;
+#X scalar help-append-template1 108 108 108 \;;
+#X scalar help-append-template1 110 110 110 \;;
+#X scalar help-append-template1 111 111 111 \;;
+#X scalar help-append-template1 112 112 112 \;;
+#X scalar help-append-template1 113 113 113 \;;
+#X scalar help-append-template1 114 114 114 \;;
+#X scalar help-append-template1 115 115 115 \;;
+#X scalar help-append-template1 116 116 116 \;;
+#X scalar help-append-template1 117 117 117 \;;
+#X scalar help-append-template1 118 118 118 \;;
+#X scalar help-append-template1 119 119 119 \;;
+#X scalar help-append-template1 120 120 120 \;;
+#X scalar help-append-template1 121 121 121 \;;
+#X scalar help-append-template1 122 122 122 \;;
+#X scalar help-append-template1 123 123 123 \;;
+#X scalar help-append-template1 124 124 124 \;;
+#X scalar help-append-template1 125 125 125 \;;
+#X scalar help-append-template1 126 126 126 \;;
+#X scalar help-append-template1 127 127 127 \;;
+#X scalar help-append-template1 128 128 128 \;;
+#X restore 357 351 pd help-append-data;
+#X obj 212 255 pointer;
+#X obj 21 10 append;
+#X text 75 9 -- add item to a list;
+#X msg 212 231 traverse pd-help-append-data \, bang;
+#X obj 34 295 append help-append-template1 x y z;
+#X floatatom 34 246 5 0 0;
+#X obj 34 266 t f f;
+#X msg 356 311 \; pd-help-append-data clear;
+#X text 27 28 "append" maintains a pointer to a scalar \, or else an
+empty pointer to the head of a list. You may set the pointer using
+the leftmost inlet. The creation arguments specify the template of
+a new scalar to append \, and the names of the fields (there should
+be at least one) you will wish to initialize. To append an object \,
+send a number to the leftmost inlet. "Append"'s pointer is updated
+to point to the new scalar \, and the new pointer is also output.;
+#X text 28 149 To insert to the beginning of a list \, you can append
+to the "head" of the list. You may append objects of different templates
+using different "append" objects.;
+#X obj 81 368 pointer;
+#X text 341 408 updated for Pd version 0.35;
+#X text 34 226 click this first->;
+#X text 230 210 go to (and output) "head" of the list;
+#X connect 11 0 15 3;
+#X connect 14 0 11 0;
+#X connect 16 0 17 0;
+#X connect 17 0 15 0;
+#X connect 17 1 15 1;
+#X connect 17 1 15 2;
diff --git a/pd/doc/5.reference/bag.pd b/pd/doc/5.reference/bag.pd
new file mode 100644
index 00000000..cdd5bfff
--- /dev/null
+++ b/pd/doc/5.reference/bag.pd
@@ -0,0 +1,27 @@
+#N canvas 118 56 577 366 12;
+#X text 18 337 see also:;
+#X obj 148 337 makenote;
+#X msg 76 151 60 64;
+#X msg 127 151 60 0;
+#X msg 171 151 62 64;
+#X msg 218 151 62 0;
+#X obj 76 278 print;
+#X text 121 279 Output is in the printout window.;
+#X msg 218 197 clear;
+#X obj 66 15 bag;
+#X text 101 14 - COLLECTION OF NUMBERS;
+#X text 12 42 The bag object takes (value \, flag) pairs. If the flag is true (nonzero) \, the value is added to the collection \; if false \, it's removed. The collection may have many copies of the same value. You can output the collection (and empty it) with a "flush" message \, or just empty it with "clear." You can use this to mimic a sustain pedal \, for example.;
+#X msg 217 174 flush;
+#X obj 104 337 poly;
+#X obj 76 248 bag;
+#X text 267 151 <-- add or delete elements;
+#X text 271 174 <-- output them;
+#X text 273 198 <-- start over;
+#X text 328 337 updated for Pd version 0.33;
+#X connect 2 0 14 0;
+#X connect 3 0 14 0;
+#X connect 4 0 14 0;
+#X connect 5 0 14 0;
+#X connect 8 0 14 0;
+#X connect 12 0 14 0;
+#X connect 14 0 6 0;
diff --git a/pd/doc/5.reference/bang.pd b/pd/doc/5.reference/bang.pd
new file mode 100644
index 00000000..1f522268
--- /dev/null
+++ b/pd/doc/5.reference/bang.pd
@@ -0,0 +1,13 @@
+#N canvas 118 56 581 250 12;
+#X obj 49 182 print;
+#X text 107 183 Output is in the printout window.;
+#X obj 66 15 bang;
+#X text 112 14 - SEND "BANG" MSSESSAGE;
+#X msg 61 105 walk the cat;
+#X msg 49 79 45;
+#X obj 49 152 bang;
+#X text 336 233 updated for Pd version 0.27;
+#X text 23 42 Outputs a "bang" message whatever it receives.;
+#X connect 4 0 6 0;
+#X connect 5 0 6 0;
+#X connect 6 0 0 0;
diff --git a/pd/doc/5.reference/bang~.pd b/pd/doc/5.reference/bang~.pd
new file mode 100644
index 00000000..debade2f
--- /dev/null
+++ b/pd/doc/5.reference/bang~.pd
@@ -0,0 +1,18 @@
+#N canvas 0 0 529 299 12;
+#X obj 112 128 bang~;
+#X obj 112 159 print;
+#X msg 210 223 \; pd dsp 1;
+#X msg 297 216 \; pd dsp 0;
+#X msg 210 144 bang;
+#X obj 19 20 bang~;
+#X obj 306 193 loadbang;
+#X obj 297 169 delay 100;
+#X text 211 122 click to test;
+#X text 71 21 - output bang after each DSP cycle;
+#X text 5 59 Bang~ outputs a bang after each DSP cycle (at the same logical time as the DSP cycle.) This is primarily useful for sampling the outputs of analysis algorithms.;
+#X text 251 266 updated for Pd version 0.33;
+#X connect 0 0 1 0;
+#X connect 4 0 2 0;
+#X connect 4 0 7 0;
+#X connect 6 0 3 0;
+#X connect 7 0 3 0;
diff --git a/pd/doc/5.reference/biquad~.pd b/pd/doc/5.reference/biquad~.pd
new file mode 100644
index 00000000..81a31960
--- /dev/null
+++ b/pd/doc/5.reference/biquad~.pd
@@ -0,0 +1,35 @@
+#N canvas 185 22 655 349 12;
+#X obj 27 14 biquad~;
+#X msg 448 11 \; pd dsp 1;
+#X msg 448 55 \; pd dsp 0;
+#X obj 84 248 env~;
+#X floatatom 84 275;
+#X floatatom 15 110;
+#X obj 15 246 env~;
+#X floatatom 15 274;
+#X text 13 297 Compare the value of the straight signal on the left with the value of the filtered signal on the right.;
+#X obj 84 215 biquad~ 1.41407 -0.9998 1 -1.41421 1;
+#X msg 101 121 1.41407 -0.9998 1 -1.41421 1;
+#X text 76 31 calculates the following difference equation:;
+#X text 77 44 y(n) = ff1 * w(n) + ff2 * w(n-1) + ff3 * w(n-2);
+#X text 76 58 w(n) = x(n) + fb1 * w(n-1) + fb2 * w(n-2);
+#X text 18 76 Syntax: biquad~ fb1 fb2 ff1 ff2 ff3;
+#X text 341 213 this biquad~ is a notch filter for fn = Pi/4;
+#X text 390 226 (= SR/8 = 5512.5 Hz @44.1k);
+#X text 91 14 2-pole-2-zero-filter;
+#X text 113 99 list sets filter parameters;
+#X msg 119 161 set 0 0;
+#X msg 120 186 clear;
+#X obj 15 170 osc~ 5512.5;
+#X text 422 337 updated for Pd version-0.30;
+#X text 189 163 set internal state;
+#X text 187 185 ... or just clear it;
+#X connect 3 0 4 0;
+#X connect 5 0 21 0;
+#X connect 6 0 7 0;
+#X connect 9 0 3 0;
+#X connect 10 0 9 0;
+#X connect 19 0 9 0;
+#X connect 20 0 9 0;
+#X connect 21 0 6 0;
+#X connect 21 0 9 0;
diff --git a/pd/doc/5.reference/bng.pd b/pd/doc/5.reference/bng.pd
new file mode 100644
index 00000000..d48d560a
--- /dev/null
+++ b/pd/doc/5.reference/bng.pd
@@ -0,0 +1,265 @@
+#N canvas 11 201 538 357 10;
+#X obj 1 1 cnv 8 100 60 empty empty bng 20 20 1 18 -262144 -1109 0
+;
+#X text 10 288 (c) musil@iem.kug.ac.at;
+#X text 52 301 IEM KUG;
+#X text 118 61 click properties to;
+#X text 106 72 modify geometry \, colors \, etc.;
+#X obj 64 257 print;
+#N canvas 598 330 290 225 once 0;
+#X msg 38 73 1;
+#X obj 38 47 t b b;
+#X obj 68 124 sel 0;
+#X obj 68 103 f 0;
+#X obj 38 24 inlet;
+#X obj 68 154 outlet;
+#X connect 0 0 3 1;
+#X connect 1 0 0 0;
+#X connect 1 1 3 0;
+#X connect 2 0 5 0;
+#X connect 3 0 2 0;
+#X connect 4 0 1 0;
+#X restore 64 234 pd once;
+#X obj 36 258 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X obj 3 130 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X obj 36 173 bng 50 950 50 1 foo5_snd foo5_rcv big-bang 63 2 192 12
+-262131 -260818 -143491;
+#X msg 36 53 33;
+#X msg 50 75 -3.14;
+#X msg 73 117 11 22 33.33;
+#X msg 63 95 open xxx;
+#X msg 96 142 funny;
+#X text 101 11 gui-bang:;
+#X obj 202 135 s foo5_rcv;
+#X obj 202 155 r foo5_snd;
+#X obj 202 115 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144
+-1 -1;
+#X obj 202 175 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144
+-1 -1;
+#X msg 4 53 0;
+#X text 125 205 UP- \, DOWN- \, LEFT- or RIGHT-key;
+#X text 124 216 for moving selected gui-objects;
+#N canvas 425 170 699 530 edit 0;
+#X obj 39 197 f;
+#X msg 17 176 bang;
+#X floatatom 55 175 3 63 88;
+#X floatatom 90 197 3 0 37;
+#X obj 39 220 pack 0 0;
+#X text 117 197 y-label;
+#X text 83 175 x-label;
+#X floatatom 259 143 3 8 75;
+#X text 286 143 size;
+#X obj 279 236 f;
+#X msg 257 215 bang;
+#X floatatom 295 214 3 -10 10;
+#X floatatom 330 236 3 -10 10;
+#X obj 279 259 pack 0 0;
+#X obj 304 348 f;
+#X msg 282 327 bang;
+#X floatatom 320 326 3 20 90;
+#X floatatom 355 348 3 150 200;
+#X obj 304 371 pack 0 0;
+#X text 323 214 x-delta;
+#X text 357 236 y-delta;
+#X text 348 326 x-position;
+#X text 382 348 y-position;
+#X obj 59 312 f;
+#X msg 37 291 bang;
+#X floatatom 75 290 3 0 2;
+#X floatatom 110 312 3 4 36;
+#X obj 59 335 pack 0 0;
+#X text 103 290 font;
+#X text 139 312 height;
+#X msg 36 399 \; foo5_rcv label blabla;
+#X msg 59 360 \; foo5_rcv label_font \$1 \$2;
+#X msg 39 245 \; foo5_rcv label_pos \$1 \$2;
+#X msg 47 135 \; foo5_rcv color \$1 \$2 \$3;
+#X msg 259 172 \; foo5_rcv size \$1;
+#X msg 279 284 \; foo5_rcv delta \$1 \$2;
+#X msg 304 396 \; foo5_rcv pos \$1 \$2;
+#X msg 483 133 \; foo5_rcv receive foo5a_rcv;
+#X msg 482 171 \; foo5a_rcv receive foo5_rcv;
+#X msg 483 50 \; foo5_rcv send foo5a_snd;
+#X msg 483 88 \; foo5_rcv send foo5_snd;
+#X text 526 349 no init;
+#X msg 505 368 \; foo5_rcv init 0;
+#X msg 512 435 \; foo5_rcv init 1;
+#X obj 493 260 f;
+#X msg 471 239 bang;
+#X floatatom 509 238 4 10 100;
+#X floatatom 544 261 5 100 3000;
+#X obj 493 283 pack 0 0;
+#X msg 493 308 \; foo5_rcv flashtime \$1 \$2;
+#X text 548 237 interrupt-time;
+#X text 585 262 hold-time;
+#X msg 36 435 \; foo5_rcv label big-bang;
+#X text 502 417 init bang on loadbang;
+#X text 519 221 flash-time:;
+#X obj 47 114 pack 0 0 0;
+#X obj 47 86 f;
+#X msg 24 38 bang;
+#X floatatom 63 36 3 0 29;
+#X floatatom 79 56 3 0 29;
+#X floatatom 112 72 3 0 29;
+#X text 91 36 background;
+#X text 106 56 front-color;
+#X text 140 73 label-color;
+#X msg 285 35 back;
+#X msg 285 55 front;
+#X msg 285 75 label;
+#X msg 247 35 bang;
+#N canvas 15 207 606 448 RGB_____________ 0;
+#X obj 97 56 inlet;
+#X obj 262 53 inlet;
+#X obj 339 55 inlet;
+#X obj 405 56 inlet;
+#X obj 97 270 bang;
+#X msg 77 295 0;
+#X msg 104 295 1;
+#X obj 146 268 bang;
+#X msg 132 295 0;
+#X msg 160 295 1;
+#X obj 196 269 bang;
+#X msg 187 295 0;
+#X msg 214 295 1;
+#X obj 265 313 spigot;
+#X obj 312 313 spigot;
+#X obj 359 313 spigot;
+#X obj 249 385 outlet;
+#X text 93 33 select;
+#X text 267 28 red;
+#X text 337 30 green;
+#X text 409 30 blue;
+#X obj 405 102 t b f;
+#X obj 339 160 +;
+#X obj 339 185 t b f;
+#X obj 339 216 +;
+#X obj 296 385 outlet;
+#X obj 343 385 outlet;
+#X obj 28 180 loadbang;
+#X obj 97 135 route back front label bang;
+#X obj 343 362 f;
+#X obj 296 361 f;
+#X obj 249 361 f;
+#X obj 262 79 * -65536;
+#X obj 339 80 * -256;
+#X obj 405 80 * -1;
+#X obj 339 247 - 1;
+#X obj 235 168 t b b b b;
+#X connect 0 0 28 0;
+#X connect 1 0 32 0;
+#X connect 2 0 33 0;
+#X connect 3 0 34 0;
+#X connect 4 0 5 0;
+#X connect 4 0 6 0;
+#X connect 5 0 14 1;
+#X connect 5 0 15 1;
+#X connect 6 0 13 1;
+#X connect 7 0 8 0;
+#X connect 7 0 9 0;
+#X connect 8 0 13 1;
+#X connect 8 0 15 1;
+#X connect 9 0 14 1;
+#X connect 10 0 11 0;
+#X connect 10 0 12 0;
+#X connect 11 0 13 1;
+#X connect 11 0 14 1;
+#X connect 12 0 15 1;
+#X connect 13 0 31 1;
+#X connect 14 0 30 1;
+#X connect 15 0 29 1;
+#X connect 21 0 22 0;
+#X connect 21 1 22 1;
+#X connect 22 0 23 0;
+#X connect 23 0 24 0;
+#X connect 23 1 24 1;
+#X connect 24 0 35 0;
+#X connect 27 0 6 0;
+#X connect 28 0 4 0;
+#X connect 28 1 7 0;
+#X connect 28 2 10 0;
+#X connect 28 3 36 0;
+#X connect 29 0 26 0;
+#X connect 30 0 25 0;
+#X connect 31 0 16 0;
+#X connect 32 0 24 0;
+#X connect 33 0 22 0;
+#X connect 34 0 21 0;
+#X connect 35 0 15 0;
+#X connect 35 0 14 0;
+#X connect 35 0 13 0;
+#X connect 36 0 31 0;
+#X connect 36 1 30 0;
+#X connect 36 2 29 0;
+#X connect 36 3 35 0;
+#X restore 285 96 pd RGB_____________;
+#X floatatom 327 65 3 0 255;
+#X floatatom 370 65 3 0 255;
+#X floatatom 413 66 3 0 255;
+#X text 34 10 preset-colors;
+#X text 296 7 RGB-colors;
+#X text 327 47 red;
+#X text 363 46 green;
+#X text 411 46 blue;
+#X connect 0 0 4 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 1;
+#X connect 3 0 4 1;
+#X connect 4 0 32 0;
+#X connect 7 0 34 0;
+#X connect 9 0 13 0;
+#X connect 10 0 9 0;
+#X connect 11 0 9 1;
+#X connect 12 0 13 1;
+#X connect 13 0 35 0;
+#X connect 14 0 18 0;
+#X connect 15 0 14 0;
+#X connect 16 0 14 1;
+#X connect 17 0 18 1;
+#X connect 18 0 36 0;
+#X connect 23 0 27 0;
+#X connect 24 0 23 0;
+#X connect 25 0 23 1;
+#X connect 26 0 27 1;
+#X connect 27 0 31 0;
+#X connect 44 0 48 0;
+#X connect 45 0 44 0;
+#X connect 46 0 44 1;
+#X connect 47 0 48 1;
+#X connect 48 0 49 0;
+#X connect 55 0 33 0;
+#X connect 56 0 55 0;
+#X connect 57 0 56 0;
+#X connect 58 0 56 1;
+#X connect 59 0 55 1;
+#X connect 60 0 55 2;
+#X connect 64 0 68 0;
+#X connect 65 0 68 0;
+#X connect 66 0 68 0;
+#X connect 67 0 68 0;
+#X connect 68 0 55 0;
+#X connect 68 1 55 1;
+#X connect 68 2 55 2;
+#X connect 69 0 68 1;
+#X connect 70 0 68 2;
+#X connect 71 0 68 3;
+#X restore 297 144 pd edit;
+#X obj 248 34 bng 15 250 50 0 aaa aaa empty 20 8 192 8 -262144 -1 -1
+;
+#X text 185 312 updated for Pd version 0.35;
+#X text 27 313 graz \, austria 2002;
+#X obj 180 11 x_all_guis aaa bbb ccc ddd eee fff ggg hhh iii;
+#X connect 6 0 5 0;
+#X connect 8 0 9 0;
+#X connect 9 0 7 0;
+#X connect 9 0 6 0;
+#X connect 10 0 9 0;
+#X connect 11 0 9 0;
+#X connect 12 0 9 0;
+#X connect 13 0 9 0;
+#X connect 14 0 9 0;
+#X connect 17 0 19 0;
+#X connect 18 0 16 0;
+#X connect 20 0 9 0;
diff --git a/pd/doc/5.reference/bp~.pd b/pd/doc/5.reference/bp~.pd
new file mode 100644
index 00000000..8bcd86b4
--- /dev/null
+++ b/pd/doc/5.reference/bp~.pd
@@ -0,0 +1,40 @@
+#N canvas 533 67 651 393 12;
+#X obj 124 11 bp~;
+#X text 159 11 - BANDPASS FILTER;
+#X obj 70 263 env~;
+#X floatatom 70 283 0 0 0;
+#X floatatom 104 193 0 0 0;
+#X obj 12 261 env~;
+#X floatatom 12 282 0 0 0;
+#X text 119 264 env~ gives the amplitude of the signal envelop in dB.
+;
+#X floatatom 12 123 0 0 0;
+#X msg 544 67 \; pd dsp 0;
+#X obj 12 146 osc~ 100;
+#X text 57 121 <-- scroll to change input frequency;
+#X msg 72 170 clear;
+#X text 122 169 <-- reinitialize internal state;
+#X text 398 361 updated for Pd version-0.30;
+#X msg 544 28 \; pd dsp 1;
+#X text 13 73 The left inlet is the incoming audio signal \, the middle
+control input sets center frequency and the rigth input sets "Q".;
+#X text 136 194 <-- center frequency;
+#X obj 72 241 bp~ 100 10;
+#X text 169 242 Arguments initialize center frequency and Q.;
+#X text 12 301 Compare the amplitude of the original signal on the
+left with the amplitude of the filtered signal on the right.;
+#X floatatom 153 215 0 0 0;
+#X text 185 216 <-- Q;
+#X text 16 35 bp~ passes a sinusoid at the center frequency at unit
+gain (approximately). Other frequencies are attenuated.;
+#X text 21 357 see also:;
+#X obj 110 356 vcf~;
+#X connect 2 0 3 0;
+#X connect 4 0 18 1;
+#X connect 5 0 6 0;
+#X connect 8 0 10 0;
+#X connect 10 0 5 0;
+#X connect 10 0 18 0;
+#X connect 12 0 18 0;
+#X connect 18 0 2 0;
+#X connect 21 0 18 2;
diff --git a/pd/doc/5.reference/canvas.pd b/pd/doc/5.reference/canvas.pd
new file mode 100644
index 00000000..a6e33be5
--- /dev/null
+++ b/pd/doc/5.reference/canvas.pd
@@ -0,0 +1,19 @@
+#N canvas 210 82 579 437 12;
+#X obj 66 15 table;
+#X text 123 16 - Array of numbers;
+#X obj 34 199 table help-tab1 25;
+#X text 10 43 "Table" builds a subpatch with a graphical array inside.
+The creation arguments specify the name and an optional size in points.
+;
+#X msg 70 265 \; help-tab1 read table.txt;
+#X msg 70 309 \; help-tab1 write /tmp/table.txt;
+#X text 70 243 You can also send messages to the array by name:;
+#X text 216 200 <- optional creation args: name \, size;
+#X text 17 355 Unfortunately there's no way to set vertical range \,
+etc.;
+#X text 9 101 Note that the data (and other properties) of the array
+aren't saved with the patch. You can resize \, save to and/or read
+from an external file as you would with "array" objects. See "arrays"
+in the 2.control examples under the "pure documentation" help menu
+item.;
+#X text 325 391 updated for Pd version 0.35;
diff --git a/pd/doc/5.reference/change.pd b/pd/doc/5.reference/change.pd
new file mode 100644
index 00000000..74e16f6a
--- /dev/null
+++ b/pd/doc/5.reference/change.pd
@@ -0,0 +1,23 @@
+#N canvas 376 130 540 355 12;
+#X msg 67 124 bang;
+#X floatatom 67 266 0 0 0;
+#X floatatom 79 154 0 0 0;
+#X floatatom 104 182 0 0 0;
+#X text 284 309 updated for Pd version 0.27;
+#X text 173 239 creation argument initializes first value;
+#X obj 66 15 change;
+#X text 114 16 - ELIMINATE REDUNDANCY IN A NUMBER STEAM;
+#X text 12 42 The change object outputs its input only when it changes.
+You can "set" the current value \, or bang to force output.;
+#X obj 67 240 change 6.5;
+#X msg 105 211 set \$1;
+#X text 136 183 set the value;
+#X text 112 123 output current value;
+#X text 110 154 if different from current value \, output and set;
+#X obj 67 293 print;
+#X connect 0 0 9 0;
+#X connect 1 0 14 0;
+#X connect 2 0 9 0;
+#X connect 3 0 10 0;
+#X connect 9 0 1 0;
+#X connect 10 0 9 0;
diff --git a/pd/doc/5.reference/clip~.pd b/pd/doc/5.reference/clip~.pd
new file mode 100644
index 00000000..61c222fa
--- /dev/null
+++ b/pd/doc/5.reference/clip~.pd
@@ -0,0 +1,30 @@
+#N canvas 182 132 778 399 12;
+#X obj 75 164 clip~ -0.5 0.5;
+#X obj 75 104 osc~ 1000;
+#X graph graph1 0 1 100 -1 78 280 278 380;
+#X array array99 100 float 0;
+#X pop;
+#X obj 91 213 metro 500;
+#X obj 91 188 r metro;
+#X text 239 235 <-- graph the output;
+#X obj 75 237 tabwrite~ array99;
+#X msg 519 69 \; metro 0;
+#X msg 515 12 \; pd dsp 1 \; metro 1;
+#X text 604 26 <-- Click to start;
+#X text 589 73 <-- Click to stop;
+#X obj 42 19 clip~;
+#X text 88 18 - restrict a signal to lie between two limits;
+#X text 243 136 inlets to reset clip range;
+#X floatatom 135 136 4 0 0;
+#X floatatom 196 137 4 0 0;
+#X text 210 164 creation arguments initialize clip range;
+#X text 4 55 The clip~ object passes its signal input to its output
+\, clipping it to lie between two limits.;
+#X text 470 371 updated for Pd version 0.33;
+#X connect 0 0 6 0;
+#X connect 1 0 0 0;
+#X connect 3 0 6 0;
+#X connect 4 0 3 0;
+#X connect 4 0 3 0;
+#X connect 14 0 0 1;
+#X connect 15 0 0 2;
diff --git a/pd/doc/5.reference/cos~.pd b/pd/doc/5.reference/cos~.pd
new file mode 100644
index 00000000..ba6f918e
--- /dev/null
+++ b/pd/doc/5.reference/cos~.pd
@@ -0,0 +1,32 @@
+#N canvas 134 143 768 332 12;
+#X obj 112 12 cos~;
+#X obj 23 200 cos~;
+#X obj 23 249 snapshot~;
+#X obj 23 152 sig~;
+#X floatatom 23 275 0 0 0;
+#X obj 23 125 * 0.01;
+#X floatatom 23 98 0 0 0;
+#X obj 90 215 metro 500;
+#X obj 90 189 r metro;
+#X msg 540 93 \; metro 0;
+#X msg 521 39 \; pd dsp 1 \; metro 1;
+#X text 159 13 - COSINE WAVESHAPER;
+#X text 86 125 Divide by 100;
+#X text 71 153 convert to audio;
+#X text 78 100 <-- Scroll to set input value;
+#X text 64 276 <-- output of the cos~ object;
+#X text 291 195 see also:;
+#X obj 379 197 osc~;
+#X obj 423 197 tabread4~;
+#X text 494 293 updated for Pd version 0.33;
+#X text 608 54 <-Click to start;
+#X text 609 99 <-Click to stop;
+#X text 9 45 The cos~ object outputs the cosine of its signal input.;
+#X connect 1 0 2 0;
+#X connect 2 0 4 0;
+#X connect 2 0 4 0;
+#X connect 3 0 1 0;
+#X connect 5 0 3 0;
+#X connect 6 0 5 0;
+#X connect 7 0 2 0;
+#X connect 8 0 7 0;
diff --git a/pd/doc/5.reference/cputime.pd b/pd/doc/5.reference/cputime.pd
new file mode 100644
index 00000000..c0a0f43a
--- /dev/null
+++ b/pd/doc/5.reference/cputime.pd
@@ -0,0 +1,15 @@
+#N canvas 302 232 550 286 12;
+#X msg 74 144 bang;
+#X msg 30 115 bang;
+#X floatatom 30 206 0 0 0;
+#X text 71 113 Click here to reset;
+#X text 27 232 Output is in milliseconds;
+#X obj 30 175 cputime;
+#X text 124 144 Click here to get elapsed CPU time;
+#X text 6 51 The cputime object measures elapsed CPU time \, as measured by your operating system. This appears to work on NT \, IRIX \, and Linux \, but not on W98.;
+#X obj 66 15 cputime;
+#X text 123 16 - measure CPU usage;
+#X text 297 261 updated for Pd version 0.33;
+#X connect 0 0 5 1;
+#X connect 1 0 5 0;
+#X connect 5 0 2 0;
diff --git a/pd/doc/5.reference/delay.pd b/pd/doc/5.reference/delay.pd
new file mode 100644
index 00000000..5f90cd4b
--- /dev/null
+++ b/pd/doc/5.reference/delay.pd
@@ -0,0 +1,30 @@
+#N canvas 3 0 513 348 12;
+#X obj 66 15 delay;
+#X obj 13 229 50;
+#X msg 13 98 bang;
+#X floatatom 13 255;
+#X floatatom 116 182;
+#X obj 51 230 0;
+#X text 111 16 - CALLBACK AFTER TIME DELAY;
+#X text 130 205 <-- creation argument initializes delay time;
+#X text 6 41 The delay object sends a bang to its outlet after a delay in milliseconds specified by its right inlet or its creation argument.;
+#X obj 51 204 delay 1000;
+#X text 58 86 Click here to test the delay object by initializing the number box below to 50 and then clearing it after the specified delay.;
+#X text 94 132 Click here to CANCEL delay's action;
+#X msg 51 133 stop;
+#X text 43 324 see also:;
+#X obj 155 323 timer;
+#X obj 111 323 metro;
+#X msg 62 155 2000;
+#X text 102 154 Number in right inlet sets time and schedules the action.;
+#X text 316 320 updated for Pd version 0.3;
+#X text 145 183 <-- scroll to change delay time in milliseconds;
+#X text 14 280 Note: sending a bang to a delay which is already set will reschedule its output \, cancelling the old one.;
+#X connect 1 0 3 0;
+#X connect 2 0 1 0;
+#X connect 2 0 9 0;
+#X connect 4 0 9 1;
+#X connect 5 0 3 0;
+#X connect 9 0 5 0;
+#X connect 12 0 9 0;
+#X connect 16 0 9 0;
diff --git a/pd/doc/5.reference/delread~.pd b/pd/doc/5.reference/delread~.pd
new file mode 100644
index 00000000..682f6e98
--- /dev/null
+++ b/pd/doc/5.reference/delread~.pd
@@ -0,0 +1,33 @@
+#N canvas 24 20 796 472 12;
+#X text 351 276 1st argument: name of delay line;
+#X floatatom 116 253 0 0 0;
+#X text 151 255 float input (delay time in ms);
+#X text 127 310 signal output (delayed signal);
+#X text 21 52 You can use more than one delread~ objects for the same delay line.;
+#X text 20 81 If the specified delay time is longer than the size of the delay line or less than zero it is clipped to the length of the delay line.;
+#X obj 383 226 delwrite~ del_example 1000;
+#X floatatom 383 177 0 0 0;
+#X obj 116 375 snapshot~;
+#X floatatom 116 399 0 0 0;
+#X obj 24 246 loadbang;
+#X obj 24 313 metro 200;
+#X msg 32 273 \; pd dsp 1;
+#X obj 116 286 delread~ del_example 1000;
+#X obj 24 16 delread~;
+#X text 424 176 input to delay line;
+#X obj 383 201 sig~;
+#X text 433 443 updated for Pd version 0.33;
+#X text 89 16 - read a signal from a delay line;
+#X text 21 133 Note: if the delaywrite~ runs after the delread~ the minimum delay is actually one DSP period \, not zero.;
+#X text 351 292 2nd argument: (initial) delay time in ms;
+#X obj 126 444 delwrite~;
+#X obj 217 444 vd~;
+#X text 36 443 see also:;
+#X connect 1 0 13 0;
+#X connect 7 0 16 0;
+#X connect 8 0 9 0;
+#X connect 10 0 11 0;
+#X connect 10 0 12 0;
+#X connect 11 0 8 0;
+#X connect 13 0 8 0;
+#X connect 16 0 6 0;
diff --git a/pd/doc/5.reference/delwrite~.pd b/pd/doc/5.reference/delwrite~.pd
new file mode 100644
index 00000000..50ca3f63
--- /dev/null
+++ b/pd/doc/5.reference/delwrite~.pd
@@ -0,0 +1,15 @@
+#N canvas 12 24 663 281 12;
+#X obj 24 16 delwrite~;
+#X obj 24 158 delwrite~ del_line_xxx 500;
+#X text 88 123 signal input;
+#X text 116 16 writes a signal in a delay line;
+#X text 271 156 1st argument: name of delay line;
+#X text 411 243 updated for Pd version 0.33;
+#X obj 24 124 sig~ 0;
+#X text 19 50 Delwrite~ allocates memory for a delay line and writes an audio signal into it. Delread~ objects by hte same name read from the delay line.;
+#X text 294 186 (= max. delay time);
+#X text 271 172 2nd argument: length of delay line in msec;
+#X text 24 203 see also:;
+#X obj 112 205 delread~;
+#X obj 193 205 vd~;
+#X connect 6 0 1 0;
diff --git a/pd/doc/5.reference/drawnumber.pd b/pd/doc/5.reference/drawnumber.pd
new file mode 100644
index 00000000..a62a8103
--- /dev/null
+++ b/pd/doc/5.reference/drawnumber.pd
@@ -0,0 +1,35 @@
+#N struct help-drawnumber-template float x float y float cat float
+dog;
+#N canvas 369 6 538 189 12;
+#X text 15 103 see also:;
+#X obj 18 7 drawnumber;
+#X obj 204 132 plot;
+#X obj 100 131 drawpolygon;
+#X obj 22 130 template;
+#X text 114 7 -- draw numeric fields from data structures;
+#N canvas 14 10 297 129 help-drawnumber-data 1;
+#X scalar help-drawnumber-template 50 100 23 43 \;;
+#X scalar help-drawnumber-template 150 50 3.14 -1.618 \;;
+#X restore 273 71 pd help-drawnumber-data;
+#N canvas 57 283 580 439 help-drawnumber-template 1;
+#X text 24 316 This object defines the fields for this template. Their
+values are initialized in the "works" subwindow. You can see them by
+right-clicking on the object in the "data" window and selecting "properties."
+;
+#X text 44 104 - RGB color (0=black \, 999=white \, 900=red \, 90=green
+\, 9=blue \, 555=grey \, etc.);
+#X obj 11 294 template float x float y float cat float dog;
+#X text 22 166 Any of these can be numbers or field names \, like "dog"
+and "cat" here.;
+#X text 22 202 When not in "edit" mode \, you can click and drag vertically
+on the numbers to change their values. (In edit mode you can move \,
+cut \, copy \, and paste the objects.);
+#X text 24 251 Keyboard entry isn't supported yet.;
+#X obj 24 15 drawnumber cat 0 0 0 cat=;
+#X obj 259 16 drawnumber dog 0 -15 900 dog=;
+#X text 44 136 - an optional label ("cat=" for instance);
+#X text 26 44 drawnumber takes arguments specifying:;
+#X text 44 84 - an (x \, y) pair giving relative coordinates \;;
+#X text 44 64 - the number to draw;
+#X restore 273 45 pd help-drawnumber-template;
+#X text 275 159 updated for Pd version 0.35;
diff --git a/pd/doc/5.reference/drawpolygon.pd b/pd/doc/5.reference/drawpolygon.pd
new file mode 100644
index 00000000..714c404b
--- /dev/null
+++ b/pd/doc/5.reference/drawpolygon.pd
@@ -0,0 +1,42 @@
+#N struct help-drawpolygon-template float x float y float cat float
+dog float weasel;
+#N canvas 411 8 565 187 12;
+#X text 13 130 see also:;
+#X obj 111 149 drawnumber;
+#X obj 207 149 plot;
+#X obj 21 10 drawpolygon;
+#X obj 21 33 drawcurve;
+#X obj 126 11 filledpolygon;
+#X obj 127 33 filledcurve;
+#X text 225 10 -- draw shapes for data structures;
+#N canvas 30 290 587 435 help-drawpolygon-template 1;
+#X obj 14 335 template float x float y float cat float dog float weasel
+;
+#X obj 19 24 drawpolygon 0 2 0 0 0 weasel;
+#X text 26 44 drawpolygon and drawcurve take arguments specifying:
+;
+#X text 29 137 Any of these can be numbers or field names \, like "weasel"
+here. The example above draws a vertical black line of height "weasel".
+;
+#X obj 19 194 filledpolygon 900 dog 3 10 0 20 cat 30 0;
+#X text 31 216 filledpolyconn and filledcurve take the same arguments
+\, except that a new first argument is added to specify interior color.
+Here the interior color is red (900) \, the outline color is controlled
+by the "dog" field \, and the three points describe a triangle of altitude
+"cat". The fields x and y automatically govern the placement of the
+object as a whole.;
+#X text 24 357 This object defines the fields for this template. Their
+values are initialized in the "works" subwindow. You can see them by
+right-clicking on the object in the "data" window and selecting "properties."
+;
+#X text 45 62 - RGB color (0=black \, 999=white \, 900=red \, 90=green
+\, 9=blue \, 555=grey \, etc.);
+#X text 46 95 - line width;
+#X text 46 116 - two or more (x \, y) pairs giving coordinates.;
+#X restore 274 93 pd help-drawpolygon-template;
+#X obj 34 149 template;
+#N canvas 10 18 384 178 help-drawpolygon-data 1;
+#X scalar help-drawpolygon-template 50 40 30 9 80 \;;
+#X scalar help-drawpolygon-template 150 40 -20 90 50 \;;
+#X restore 274 119 pd help-drawpolygon-data;
+#X text 312 168 updated for Pd version 0.35;
diff --git a/pd/doc/5.reference/element.pd b/pd/doc/5.reference/element.pd
new file mode 100644
index 00000000..a3faeecf
--- /dev/null
+++ b/pd/doc/5.reference/element.pd
@@ -0,0 +1,51 @@
+#N struct help-element-template float x float y array array1 help-element-array1-template
+;
+#N struct help-element-array1-template float y;
+#N canvas 330 34 668 442 12;
+#X text 24 373 see also:;
+#X obj 21 393 template;
+#N canvas 393 10 491 261 help-element-template 0;
+#X obj 27 76 plot array1 500 1 10 15 20;
+#X obj 27 174 filledpolygon 509 509 0 -10 -10 10 -10 10 10 -10 10;
+#X obj 24 16 template float x float y array array1 help-element-array1-template
+;
+#X restore 414 349 pd help-element-template;
+#N canvas 0 0 288 159 help-element-data 1;
+#X scalar help-element-template 35 24 \; 0 \; 10 \; 0 \; 10 \; 20 \;
+10 \; 20 \; 70 \; 10 \; \;;
+#X restore 450 328 pd help-element-data;
+#N canvas 196 292 365 134 help-element-array1-template 0;
+#X obj 30 71 filledpolygon 0 0 0 -5 0 0 5 5 0 0 -5;
+#X obj 32 27 template float y;
+#X restore 354 371 pd help-element-array1-template;
+#X obj 22 11 element;
+#X text 91 10 -- get pointer to an element of an array;
+#X obj 98 393 pointer;
+#X obj 166 393 getsize;
+#X obj 234 393 setsize;
+#X text 24 44 "element" takes a pointer at right and a number at left.
+It looks up a field from the pointer \, which should be an array \,
+and outputs the element of the array specified by the number. There
+are no pointers to arrays themselves \, just to individual elements.
+The template and field mane are specified as creation arguments.;
+#X obj 150 228 pointer;
+#X msg 150 204 traverse pd-help-element-data \, next;
+#X floatatom 38 230 5 0 0;
+#X obj 38 256 element help-element-template array1;
+#X obj 38 303 get help-element-array1-template y;
+#X floatatom 38 330 5 0 0;
+#X text 370 256 arguments: template \, field name;
+#X text 247 230 pointer inlet;
+#X text 36 209 index;
+#X text 43 277 outlet is pointer to single element;
+#X text 89 328 here we just get the value of y.;
+#X text 24 143 Indices range from 0 to the number of elements minus
+one \; indices out of range are quietly replaced by the nearest endpoint.
+;
+#X text 152 184 click here first;
+#X text 407 416 updated for Pd version 0.35;
+#X connect 11 0 14 1;
+#X connect 12 0 11 0;
+#X connect 13 0 14 0;
+#X connect 14 0 15 0;
+#X connect 15 0 16 0;
diff --git a/pd/doc/5.reference/env~.pd b/pd/doc/5.reference/env~.pd
new file mode 100644
index 00000000..64a245ae
--- /dev/null
+++ b/pd/doc/5.reference/env~.pd
@@ -0,0 +1,28 @@
+#N canvas 40 55 711 401 12;
+#X floatatom 103 303 0 0 0;
+#X obj 74 14 env~;
+#X text 120 16 - envelope follower;
+#X obj 103 275 env~ 16384;
+#X obj 103 214 osc~ 400;
+#X obj 103 241 *~;
+#X floatatom 217 220 3 -99 300;
+#X obj 217 244 dbtorms;
+#X text 9 46 The env~ object takes a signal and outputs its RMS amplitude
+in dB (with 1 normalized to 100 dB.) Output is bounded below by zero.
+;
+#X text 8 105 The analysis is Hanning windowed.;
+#X text 9 131 The optional creation argument is the analysis window
+size in samples \, and should be a power of two. The analysis windows
+overlap by two \, so that the period of output is half the window size.
+;
+#X text 206 274 <- creation argument sets window size;
+#X text 265 221 <- set peak-to-peak amplitude here in dB.;
+#X text 185 305 <- the output is RMS amplitude which is about 3 dB
+below peak-to-peak amplitude.;
+#X text 459 375 updated for Pd version 0.35;
+#X connect 3 0 0 0;
+#X connect 3 0 0 0;
+#X connect 4 0 5 0;
+#X connect 5 0 3 0;
+#X connect 6 0 7 0;
+#X connect 7 0 5 1;
diff --git a/pd/doc/5.reference/fft~.pd b/pd/doc/5.reference/fft~.pd
new file mode 100644
index 00000000..149a6634
--- /dev/null
+++ b/pd/doc/5.reference/fft~.pd
@@ -0,0 +1,64 @@
+#N canvas 22 7 886 436 12;
+#X text 85 158 frequency;
+#X floatatom 16 173 0 0 0;
+#X obj 16 120 * 44100;
+#X floatatom 16 94 0 0 0;
+#X text 88 92 frequency;
+#X text 91 111 in bins;
+#X text 85 175 in Hz.;
+#X obj 16 229 osc~;
+#X obj 36 16 fft~;
+#X obj 86 17 ifft~;
+#X text 146 15 - forward and inverse complex FFT;
+#X obj 36 42 rfft~;
+#X obj 86 43 rifft~;
+#X text 146 41 - forward and inverse real FFT;
+#X obj 16 254 rfft~;
+#X obj 16 148 / 64;
+#X obj 574 21 loadbang;
+#X msg 574 47 \; pd dsp 1;
+#X text 636 403 updated for Pd version 0.33;
+#X obj 16 322 rifft~;
+#X obj 102 310 print~ real;
+#X obj 115 285 print~ imaginary;
+#X obj 16 352 /~ 64;
+#X obj 16 407 print~ resynthesized;
+#X msg 30 380 bang;
+#X msg 101 248 bang;
+#X msg 100 199 0.25;
+#X msg 152 199 0;
+#X text 195 200 <-- bash phase;
+#X text 152 249 <-- print analysis;
+#X text 79 380 <-- print resynthesis;
+#X text 76 352 <-- renormalize;
+#X text 347 294 There is no normalization \, so that an FFT followed
+by an IFFT has a gain of N.;
+#X text 346 343 See the FFT examples to see how to use these in practice.
+;
+#X text 346 112 The FFT objects do Fourier analyses and resyntheses
+of incoming real or complex signals. Complex signals are handled as
+pairs of signals (real and imaginary part.) The analysis size is one
+block (you can use the block~ or switch~ obejcts to control block size).
+;
+#X text 347 205 The real FFT outputs N/2+1 real parts and N/2-1 imaginary
+parts. The other outputs are zero. At DC and at the Nyquist there is
+no imaginary part \, but the second through Nth output is as a real
+and imaginary pair \, which can be thought of as the cosine and sin
+component strengths.;
+#X connect 1 0 7 0;
+#X connect 2 0 15 0;
+#X connect 3 0 2 0;
+#X connect 7 0 14 0;
+#X connect 14 0 20 0;
+#X connect 14 0 19 0;
+#X connect 14 1 21 0;
+#X connect 14 1 19 1;
+#X connect 15 0 1 0;
+#X connect 16 0 17 0;
+#X connect 19 0 22 0;
+#X connect 22 0 23 0;
+#X connect 24 0 23 0;
+#X connect 25 0 20 0;
+#X connect 25 0 21 0;
+#X connect 26 0 7 1;
+#X connect 27 0 7 1;
diff --git a/pd/doc/5.reference/float.pd b/pd/doc/5.reference/float.pd
new file mode 100644
index 00000000..efc5235a
--- /dev/null
+++ b/pd/doc/5.reference/float.pd
@@ -0,0 +1,18 @@
+#N canvas 23 20 429 272 12;
+#X msg 30 107 bang;
+#X obj 30 183 float 6.5;
+#X floatatom 30 209;
+#X floatatom 42 129;
+#X floatatom 88 153;
+#X obj 55 12 float;
+#X text 97 11 - STORE A (FLOATING POINT) NUMBER;
+#X text 12 36 The float object stores a number \, initialized by its creation argument \, which may be reset using its inlet and output by sending it the "bang" message. Sending a number sets a new value and outputs it.;
+#X text 77 103 outputs the value;
+#X text 74 130 sets and outputs the value;
+#X text 123 155 sets the value;
+#X text 108 184 creation argument initializes the value;
+#X text 223 242 updated for Pd version 0.3;
+#X connect 0 0 1 0;
+#X connect 1 0 2 0;
+#X connect 3 0 1 0;
+#X connect 4 0 1 1;
diff --git a/pd/doc/5.reference/framp~.pd b/pd/doc/5.reference/framp~.pd
new file mode 100644
index 00000000..cbf3f047
--- /dev/null
+++ b/pd/doc/5.reference/framp~.pd
@@ -0,0 +1,40 @@
+#N canvas 22 7 857 377 12;
+#X text 85 158 frequency;
+#X floatatom 16 173 0 0 0;
+#X obj 16 120 * 44100;
+#X floatatom 16 94 0 0 0;
+#X text 88 92 frequency;
+#X text 91 111 in bins;
+#X text 85 175 in Hz.;
+#X obj 17 238 rfft~;
+#X obj 16 148 / 64;
+#X obj 653 14 loadbang;
+#X msg 653 40 \; pd dsp 1;
+#X text 599 339 updated for Pd version 0.33;
+#X msg 103 260 bang;
+#X text 154 261 <-- print analysis;
+#X obj 36 16 framp~;
+#X text 119 15 - estimate frequency and amplitude of FFT components
+;
+#X obj 16 270 framp~;
+#X obj 103 322 print~ frequency;
+#X obj 118 297 print~ amplitude;
+#X obj 16 204 osc~;
+#X text 324 98 Framp~ takes as input a rectangular-windowed FFT and
+outputs \, for each FFT channel \, the estimated amplitude and frequency
+of any component feedinf that channel. A sinusoidal component should
+appear in four components (or three in the special case of a sinusoid
+exactly tuned to a bin.) Frequency output is in bins \, i.e. \, units
+of SR/N.;
+#X connect 1 0 19 0;
+#X connect 2 0 8 0;
+#X connect 3 0 2 0;
+#X connect 7 0 16 0;
+#X connect 7 1 16 1;
+#X connect 8 0 1 0;
+#X connect 9 0 10 0;
+#X connect 12 0 17 0;
+#X connect 12 0 18 0;
+#X connect 16 0 17 0;
+#X connect 16 1 18 0;
+#X connect 19 0 7 0;
diff --git a/pd/doc/5.reference/gatom.pd b/pd/doc/5.reference/gatom.pd
new file mode 100644
index 00000000..2dd7ce61
--- /dev/null
+++ b/pd/doc/5.reference/gatom.pd
@@ -0,0 +1,32 @@
+#N canvas 138 65 675 508 12;
+#X floatatom 107 9 0 0 0;
+#X text 155 10 atoms (number boxes);
+#X floatatom 38 85 0 0 0;
+#X floatatom 38 125 0 0 0;
+#X msg 51 260 set 45;
+#X floatatom 51 288 0 0 0;
+#X floatatom 51 317 0 0 0;
+#X text 84 40 Number boxes allow you to display numbers or to enter
+numbers using the mouse and keyboard. When a number arrives at the
+number box's inlet \, it is displayed and sent to the outlet. You can
+click on a number box and drag upward or downward to change the value
+continuously.;
+#X text 88 130 You can shift-click and drag to change the number by
+hundredths instead of units. Alt clicking toggles the value between
+0 and the last nonzero value.;
+#X text 83 184 You can also type in values by clicking and typing a
+number followed by "enter.";
+#X text 30 220 the "set" message sets the number box's value but does
+not send it to the outlet.;
+#X text 423 482 updated for Pd version 0.34;
+#X text 39 339 You can set the width of the box by right-clicking and
+choosing "properties." By default the width is 5 characters. If you
+select a width of 0 \, the number box will grow as needed to hold the
+number--BUT BEWARE \, THIS IS EXPENSIVE IN CPU TIME. In a production
+patch \, you'll want to set a specific width.;
+#X floatatom 547 439 1 0 0;
+#X text 41 438 A width of one gives a clickable toggle switch ala Max:
+;
+#X connect 2 0 3 0;
+#X connect 4 0 5 0;
+#X connect 5 0 6 0;
diff --git a/pd/doc/5.reference/get.pd b/pd/doc/5.reference/get.pd
new file mode 100644
index 00000000..6784a6ce
--- /dev/null
+++ b/pd/doc/5.reference/get.pd
@@ -0,0 +1,46 @@
+#N struct help-get-template1 float x float y;
+#N canvas 293 5 629 429 12;
+#X text 13 325 see also:;
+#X obj 143 370 template;
+#X obj 84 345 set;
+#X obj 116 345 append;
+#X obj 175 345 getsize;
+#X obj 243 345 setsize;
+#X obj 311 345 element;
+#X obj 16 370 sublist;
+#X obj 84 370 scalar;
+#X msg 60 130 next;
+#N canvas 164 72 425 146 help-get-template1 0;
+#X obj 41 87 filledpolygon 9 0 1 0 0 20 0 20 30 0 30;
+#X obj 60 21 template float x float y;
+#X restore 376 234 pd help-get-template1;
+#N canvas 0 0 276 156 help-get-data 1;
+#X scalar help-get-template1 46 23 \;;
+#X scalar help-get-template1 106 73 \;;
+#X restore 376 212 pd help-get-data;
+#X obj 21 10 get;
+#X text 86 10 -- get values from a scalar;
+#X msg 45 102 traverse pd-help-get-data \, next;
+#X floatatom 45 214 5 0 0;
+#X floatatom 222 210 5 0 0;
+#X obj 45 157 pointer;
+#X text 337 101 output first scalar in list;
+#X text 103 129 output next item;
+#X text 21 277 If you have data whose template varies (from a heterogeneous
+list \, for example) you can use "pointer" to select according to template
+before sending to "get".;
+#X obj 45 185 get help-get-template1 x y;
+#X text 31 37 "Get" \, when sent a pointer to a scalar \, retrieves
+fields from it by name. The fields can be float or symbol. In the future
+this will also allow access to sublists of scalars.;
+#X text 293 167 First argument selects template.;
+#X text 294 182 Remaining args are names of fields.;
+#X text 41 233 x output;
+#X text 220 232 y output;
+#X obj 16 345 pointer;
+#X text 373 399 updated for Pd version 0.35;
+#X connect 9 0 17 0;
+#X connect 14 0 17 0;
+#X connect 17 0 21 0;
+#X connect 21 0 15 0;
+#X connect 21 1 16 0;
diff --git a/pd/doc/5.reference/getsize.pd b/pd/doc/5.reference/getsize.pd
new file mode 100644
index 00000000..f2b17e2f
--- /dev/null
+++ b/pd/doc/5.reference/getsize.pd
@@ -0,0 +1,41 @@
+#N struct help-getsize-template float x float y array array1 help-getsize-array1-template
+;
+#N struct help-getsize-array1-template float y;
+#N canvas 340 20 655 398 12;
+#X text 28 319 see also:;
+#X obj 25 339 template;
+#N canvas 393 10 491 261 help-getsize-template 0;
+#X obj 27 76 plot array1 500 1 10 15 20;
+#X obj 27 174 filledpolygon 509 509 0 -10 -10 10 -10 10 10 -10 10;
+#X obj 24 16 template float x float y array array1 help-getsize-array1-template
+;
+#X restore 338 311 pd help-getsize-template;
+#N canvas 0 0 301 193 help-getsize-data 1;
+#X scalar help-getsize-template 43 37 \; 0 \; 10 \; 0 \; 10 \; 20 \;
+10 \; 20 \; 70 \; 10 \; \;;
+#X restore 337 290 pd help-getsize-data;
+#N canvas 196 292 365 134 help-getsize-array1-template 0;
+#X obj 30 71 filledpolygon 0 0 0 -5 0 0 5 5 0 0 -5;
+#X obj 32 27 template float y;
+#X restore 337 334 pd help-getsize-array1-template;
+#X obj 102 339 pointer;
+#X obj 236 338 setsize;
+#X obj 25 166 pointer;
+#X msg 25 142 traverse pd-help-getsize-data \, next;
+#X floatatom 25 242 5 0 0;
+#X text 360 214 arguments: template \, field name;
+#X text 76 240 here we just get the value of y.;
+#X obj 25 213 getsize help-getsize-template array1;
+#X text 34 192 inlet for pointer;
+#X obj 35 21 getsize;
+#X text 98 22 -- get number of elements of an array;
+#X text 24 44 When sent a pointer \, "element" looks up a field \,
+which should be an array \, and outputs the number of elements of the
+array. The template and field name are specified as creation arguments.
+;
+#X text 23 110 The smallest possible size is one.;
+#X obj 169 339 element;
+#X text 397 374 updated for Pd version 0.35;
+#X connect 7 0 12 0;
+#X connect 8 0 7 0;
+#X connect 12 0 9 0;
diff --git a/pd/doc/5.reference/graph.pd b/pd/doc/5.reference/graph.pd
new file mode 100644
index 00000000..1badaee3
--- /dev/null
+++ b/pd/doc/5.reference/graph.pd
@@ -0,0 +1,13 @@
+#N canvas 28 3 693 300 12;
+#X graph graph1 0 -1 99 1 188 290 338 190;
+#X array array99 100 float;
+#X pop;
+#X text 174 19 GRAPHS;
+#X text 20 42 A graph in Pd is a rectangular subregion of the window in
+which you can store numeric arrays.;
+#X text 19 140 You can change the array values by redrawing it in the graph.
+See also "11.arrays" and passim in the "control examples".;
+#X text 406 266 last updated for release 0.33;
+#X text 18 85 If you create a new array Pd will usually make a new graph
+to put it in (you can change this using the "array" dialog that pops up.)
+;
diff --git a/pd/doc/5.reference/hdial.pd b/pd/doc/5.reference/hdial.pd
new file mode 100644
index 00000000..006c0f8b
--- /dev/null
+++ b/pd/doc/5.reference/hdial.pd
@@ -0,0 +1,282 @@
+#N canvas 106 314 612 281 10;
+#X obj 1 1 cnv 8 100 60 empty empty hdial=hdl 20 20 1 18 -262144 -1109
+0;
+#X text 16 213 (c) musil@iem.kug.ac.at;
+#X text 58 226 IEM KUG;
+#X text 289 52 click properties to;
+#X text 277 63 modify geometry \, colors \, etc.;
+#X obj 356 172 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144
+-1 -1;
+#X obj 21 54 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X obj 355 124 s foo8_rcv;
+#X obj 356 150 r foo8_snd;
+#X obj 44 100 hdl 25 1 1 10 foo8_snd foo8_rcv hdial_0_9 156 -8 192
+10 -99865 -262144 -260818 2;
+#X msg 44 142 \$1;
+#X floatatom 44 164 4 0 0;
+#X obj 44 186 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X obj 89 161 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 89 140 route 0 1 2 3 4 5 6 7 8 9;
+#X msg 176 64 set \$1;
+#X floatatom 176 43 4 0 9;
+#X floatatom 44 54 4 0 9;
+#X msg 91 41 7 0 -5.44;
+#X msg 95 63 3 3 4.55;
+#X obj 106 161 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 123 161 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 1
+1;
+#X obj 140 161 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 157 161 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 174 161 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 191 161 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 208 161 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 225 161 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 242 161 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 82 178 print;
+#X floatatom 380 198 4 0 0;
+#X msg 380 172 \$1;
+#X msg 355 103 set \$1;
+#X floatatom 355 82 4 0 9;
+#X text 128 178 UP- \, DOWN- \, LEFT- or RIGHT-key;
+#X text 127 189 for moving selected gui-objects;
+#N canvas 226 227 699 530 edit 0;
+#X obj 42 198 f;
+#X msg 20 177 bang;
+#X floatatom 58 176 3 63 156;
+#X floatatom 93 198 3 -20 37;
+#X obj 42 221 pack 0 0;
+#X text 120 198 y-label;
+#X text 86 176 x-label;
+#X floatatom 270 187 3 8 50;
+#X text 297 187 size;
+#X obj 286 293 f;
+#X msg 264 272 bang;
+#X floatatom 302 271 3 -10 10;
+#X floatatom 337 293 3 -10 10;
+#X obj 286 316 pack 0 0;
+#X obj 300 412 f;
+#X msg 278 391 bang;
+#X floatatom 316 390 3 20 60;
+#X floatatom 351 412 3 100 200;
+#X obj 300 435 pack 0 0;
+#X text 330 271 x-delta;
+#X text 364 293 y-delta;
+#X text 344 390 x-position;
+#X text 378 412 y-position;
+#X obj 62 313 f;
+#X msg 40 292 bang;
+#X floatatom 78 291 3 0 2;
+#X floatatom 113 313 3 4 36;
+#X obj 62 336 pack 0 0;
+#X text 106 291 font;
+#X text 142 313 height;
+#X text 504 293 no init;
+#X text 475 348 init value on loadbang;
+#X floatatom 482 228 5 2 20;
+#X msg 47 125 \; foo8_rcv color \$1 \$2 \$3;
+#X msg 42 246 \; foo8_rcv label_pos \$1 \$2;
+#X msg 62 361 \; foo8_rcv label_font \$1 \$2;
+#X msg 34 423 \; foo8_rcv label blabla;
+#X msg 300 460 \; foo8_rcv pos \$1 \$2;
+#X msg 286 341 \; foo8_rcv delta \$1 \$2;
+#X msg 270 216 \; foo8_rcv size \$1;
+#X msg 482 171 \; foo8a_rcv receive foo8_rcv;
+#X msg 483 133 \; foo8_rcv receive foo8a_rcv;
+#X msg 483 88 \; foo8_rcv send foo8_snd;
+#X msg 483 50 \; foo8_rcv send foo8a_snd;
+#X msg 483 312 \; foo8_rcv init 0;
+#X msg 485 366 \; foo8_rcv init 1;
+#X msg 490 436 \; foo8_rcv single_change;
+#X msg 490 470 \; foo8_rcv double_change;
+#X text 491 417 changing-behavior;
+#X msg 482 254 \; foo8_rcv number \$1;
+#X text 526 228 number of buttons;
+#X obj 47 104 pack 0 0 0;
+#X obj 47 76 f;
+#X msg 24 28 bang;
+#X floatatom 63 26 3 0 29;
+#X floatatom 79 46 3 0 29;
+#X floatatom 112 62 3 0 29;
+#X text 91 26 background;
+#X text 106 46 front-color;
+#X text 140 63 label-color;
+#X msg 285 25 back;
+#X msg 285 45 front;
+#X msg 285 65 label;
+#X msg 247 25 bang;
+#N canvas 15 207 606 448 RGB_____________ 0;
+#X obj 97 56 inlet;
+#X obj 262 53 inlet;
+#X obj 339 55 inlet;
+#X obj 405 56 inlet;
+#X obj 97 270 bang;
+#X msg 77 295 0;
+#X msg 104 295 1;
+#X obj 146 268 bang;
+#X msg 132 295 0;
+#X msg 160 295 1;
+#X obj 196 269 bang;
+#X msg 187 295 0;
+#X msg 214 295 1;
+#X obj 265 313 spigot;
+#X obj 312 313 spigot;
+#X obj 359 313 spigot;
+#X obj 249 385 outlet;
+#X text 93 33 select;
+#X text 267 28 red;
+#X text 337 30 green;
+#X text 409 30 blue;
+#X obj 405 102 t b f;
+#X obj 339 160 +;
+#X obj 339 185 t b f;
+#X obj 339 216 +;
+#X obj 296 385 outlet;
+#X obj 343 385 outlet;
+#X obj 28 180 loadbang;
+#X obj 97 135 route back front label bang;
+#X obj 343 362 f;
+#X obj 296 361 f;
+#X obj 249 361 f;
+#X obj 262 79 * -65536;
+#X obj 339 80 * -256;
+#X obj 405 80 * -1;
+#X obj 339 247 - 1;
+#X obj 235 168 t b b b b;
+#X connect 0 0 28 0;
+#X connect 1 0 32 0;
+#X connect 2 0 33 0;
+#X connect 3 0 34 0;
+#X connect 4 0 5 0;
+#X connect 4 0 6 0;
+#X connect 5 0 14 1;
+#X connect 5 0 15 1;
+#X connect 6 0 13 1;
+#X connect 7 0 8 0;
+#X connect 7 0 9 0;
+#X connect 8 0 13 1;
+#X connect 8 0 15 1;
+#X connect 9 0 14 1;
+#X connect 10 0 11 0;
+#X connect 10 0 12 0;
+#X connect 11 0 13 1;
+#X connect 11 0 14 1;
+#X connect 12 0 15 1;
+#X connect 13 0 31 1;
+#X connect 14 0 30 1;
+#X connect 15 0 29 1;
+#X connect 21 0 22 0;
+#X connect 21 1 22 1;
+#X connect 22 0 23 0;
+#X connect 23 0 24 0;
+#X connect 23 1 24 1;
+#X connect 24 0 35 0;
+#X connect 27 0 6 0;
+#X connect 28 0 4 0;
+#X connect 28 1 7 0;
+#X connect 28 2 10 0;
+#X connect 28 3 36 0;
+#X connect 29 0 26 0;
+#X connect 30 0 25 0;
+#X connect 31 0 16 0;
+#X connect 32 0 24 0;
+#X connect 33 0 22 0;
+#X connect 34 0 21 0;
+#X connect 35 0 15 0;
+#X connect 35 0 14 0;
+#X connect 35 0 13 0;
+#X connect 36 0 31 0;
+#X connect 36 1 30 0;
+#X connect 36 2 29 0;
+#X connect 36 3 35 0;
+#X restore 285 86 pd RGB_____________;
+#X floatatom 327 55 3 0 255;
+#X floatatom 370 55 3 0 255;
+#X floatatom 413 56 3 0 255;
+#X text 34 0 preset-colors;
+#X text 296 -3 RGB-colors;
+#X text 327 37 red;
+#X text 363 36 green;
+#X text 411 36 blue;
+#X msg 34 459 \; foo8_rcv label hdial_0_9;
+#X connect 0 0 4 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 1;
+#X connect 3 0 4 1;
+#X connect 4 0 34 0;
+#X connect 7 0 39 0;
+#X connect 9 0 13 0;
+#X connect 10 0 9 0;
+#X connect 11 0 9 1;
+#X connect 12 0 13 1;
+#X connect 13 0 38 0;
+#X connect 14 0 18 0;
+#X connect 15 0 14 0;
+#X connect 16 0 14 1;
+#X connect 17 0 18 1;
+#X connect 18 0 37 0;
+#X connect 23 0 27 0;
+#X connect 24 0 23 0;
+#X connect 25 0 23 1;
+#X connect 26 0 27 1;
+#X connect 27 0 35 0;
+#X connect 32 0 49 0;
+#X connect 51 0 33 0;
+#X connect 52 0 51 0;
+#X connect 53 0 52 0;
+#X connect 54 0 52 1;
+#X connect 55 0 51 1;
+#X connect 56 0 51 2;
+#X connect 60 0 64 0;
+#X connect 61 0 64 0;
+#X connect 62 0 64 0;
+#X connect 63 0 64 0;
+#X connect 64 0 51 0;
+#X connect 64 1 51 1;
+#X connect 64 2 51 2;
+#X connect 65 0 64 1;
+#X connect 66 0 64 2;
+#X connect 67 0 64 3;
+#X restore 469 108 pd edit;
+#X obj 346 35 hdl 15 1 0 8 eee eee empty 20 8 192 8 -262144 -1 -1 0
+;
+#X obj 260 11 x_all_guis aaa bbb ccc ddd eee fff ggg hhh iii;
+#X text 183 11 gui-hdial:;
+#X text 33 238 graz \, austria 2002;
+#X text 251 232 updated for Pd version 0.35;
+#X connect 6 0 9 0;
+#X connect 8 0 5 0;
+#X connect 8 0 31 0;
+#X connect 9 0 10 0;
+#X connect 9 0 14 0;
+#X connect 9 0 29 0;
+#X connect 10 0 11 0;
+#X connect 11 0 12 0;
+#X connect 14 0 13 0;
+#X connect 14 1 20 0;
+#X connect 14 2 21 0;
+#X connect 14 3 22 0;
+#X connect 14 4 23 0;
+#X connect 14 5 24 0;
+#X connect 14 6 25 0;
+#X connect 14 7 26 0;
+#X connect 14 8 27 0;
+#X connect 14 9 28 0;
+#X connect 15 0 9 0;
+#X connect 16 0 15 0;
+#X connect 17 0 9 0;
+#X connect 18 0 9 0;
+#X connect 19 0 9 0;
+#X connect 31 0 30 0;
+#X connect 32 0 7 0;
+#X connect 33 0 32 0;
diff --git a/pd/doc/5.reference/hip~.pd b/pd/doc/5.reference/hip~.pd
new file mode 100644
index 00000000..bc8c408d
--- /dev/null
+++ b/pd/doc/5.reference/hip~.pd
@@ -0,0 +1,31 @@
+#N canvas 21 5 556 324 12;
+#X obj 70 228 env~;
+#X floatatom 70 248;
+#X floatatom 107 178;
+#X obj 70 206 hip~ 5;
+#X text 119 201 The high pass filter is initialized to cutoff frequencies below 5 Hz.;
+#X obj 12 226 env~;
+#X floatatom 12 245;
+#X text 108 227 env~ gives the amplitude of the signal envelop in dB.;
+#X floatatom 12 107;
+#X msg 452 24 \; pd dsp 1;
+#X msg 452 58 \; pd dsp 0;
+#X obj 83 6 lop~;
+#X text 9 68 The left inlet is the incoming audio signal. The right inlet is the cutoff frequency in Hz.;
+#X obj 12 130 osc~ 100;
+#X text 57 105 <-- scroll to change input frequency;
+#X text 12 266 Compare the value of the original signal on the left with the value of the filtered signal on the right.;
+#X text 8 35 lop~ is a one-pole low pass filter with a specified rolloff frequency.;
+#X text 114 7 - one-pole low pass filter;
+#X msg 70 154 clear;
+#X text 114 153 <-- reinitialize internal state;
+#X text 139 179 <-- set cutoff frequency;
+#X text 361 306 updated for Pd version-0.30;
+#X connect 0 0 1 0;
+#X connect 2 0 3 1;
+#X connect 3 0 0 0;
+#X connect 5 0 6 0;
+#X connect 8 0 13 0;
+#X connect 13 0 5 0;
+#X connect 13 0 3 0;
+#X connect 18 0 3 0;
diff --git a/pd/doc/5.reference/hslider.pd b/pd/doc/5.reference/hslider.pd
new file mode 100644
index 00000000..80bd83a5
--- /dev/null
+++ b/pd/doc/5.reference/hslider.pd
@@ -0,0 +1,303 @@
+#N canvas 243 228 551 413 10;
+#X obj 1 1 cnv 8 100 60 empty empty hslider=hsl 20 20 1 18 -262144
+-1109 0;
+#X floatatom 38 127 9 0 0;
+#X msg 47 84 set \$1;
+#X floatatom 38 41 7 0 0;
+#X text 13 355 (c) musil@iem.kug.ac.at;
+#X text 55 368 IEM KUG;
+#X obj 38 149 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X obj 18 41 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X obj 41 107 hsl 101 15 25 75 0 1 foo1_snd foo1_rcv empty 8 -8 192
+10 -225280 -1109 -1 6200 1;
+#X text 174 11 gui-horicontal-slider:;
+#X floatatom 47 62 7 0 0;
+#X floatatom 116 150 9 0 0;
+#X obj 110 308 r goo2_snd;
+#X obj 145 248 s goo2_rcv;
+#X floatatom 105 40 7 0 0;
+#X floatatom 145 206 7 0 0;
+#X obj 60 170 print;
+#N canvas 276 200 290 224 once 0;
+#X obj 38 47 t b b f;
+#X msg 56 85 1;
+#X obj 31 108 f 0;
+#X obj 31 131 pack 0 0;
+#X obj 31 156 route 0;
+#X obj 38 24 inlet;
+#X obj 31 180 outlet;
+#X connect 0 0 1 0;
+#X connect 0 1 2 0;
+#X connect 0 2 3 1;
+#X connect 1 0 2 1;
+#X connect 2 0 3 0;
+#X connect 3 0 4 0;
+#X connect 4 0 6 0;
+#X connect 5 0 0 0;
+#X restore 60 147 pd once;
+#X obj 40 265 hsl 73 15 55 3520 1 1 goo2_snd goo2_rcv log.freq. 8 -8
+192 10 -42246 -260818 -90133 1600 1;
+#X obj 37 308 ftom;
+#X floatatom 37 330 9 0 0;
+#X floatatom 64 287 9 0 0;
+#X floatatom 110 329 9 0 0;
+#X text 175 176 click properties to;
+#X floatatom 37 203 8 0 0;
+#X obj 37 226 mtof;
+#X text 12 184 --------------------;
+#X text 163 187 modify geometry \, colors \, etc.;
+#X obj 105 82 s foo1_rcv;
+#X obj 116 130 r foo1_snd;
+#X msg 105 61 set \$1;
+#X msg 145 227 set \$1;
+#X text 197 120 (0.01 pixels);
+#X text 183 99 shift-click & drag;
+#X text 189 109 for fine-tuning;
+#X text 148 270 UP- \, DOWN- \, LEFT- or RIGHT-key;
+#X text 147 281 for moving selected gui-objects;
+#N canvas 207 113 716 530 edit 0;
+#X obj 32 220 f;
+#X msg 10 199 bang;
+#X floatatom 48 198 3 63 88;
+#X floatatom 83 220 3 0 37;
+#X obj 32 243 pack 0 0;
+#X text 110 220 y-label;
+#X text 76 198 x-label;
+#X obj 279 246 f;
+#X msg 257 225 bang;
+#X floatatom 295 224 3 -10 10;
+#X floatatom 330 246 3 -10 10;
+#X obj 279 269 pack 0 0;
+#X obj 292 358 f;
+#X msg 270 337 bang;
+#X floatatom 308 336 3 20 60;
+#X floatatom 343 358 3 150 200;
+#X obj 292 381 pack 0 0;
+#X text 323 224 x-delta;
+#X text 357 246 y-delta;
+#X text 336 336 x-position;
+#X text 370 358 y-position;
+#X obj 52 335 f;
+#X msg 30 314 bang;
+#X floatatom 68 313 3 0 2;
+#X floatatom 103 335 3 4 36;
+#X obj 52 358 pack 0 0;
+#X text 96 313 font;
+#X text 132 335 height;
+#X floatatom 476 188 1 0 1;
+#X text 523 401 no init;
+#X text 493 453 init value on loadbang;
+#X msg 47 154 \; goo2_rcv color \$1 \$2 \$3;
+#X msg 32 268 \; goo2_rcv label_pos \$1 \$2;
+#X msg 52 383 \; goo2_rcv label_font \$1 \$2;
+#X msg 34 427 \; goo2_rcv label blabla;
+#X msg 292 406 \; goo2_rcv pos \$1 \$2;
+#X msg 279 294 \; goo2_rcv delta \$1 \$2;
+#X msg 475 21 \; goo2_rcv send goo2a_snd;
+#X msg 475 59 \; goo2_rcv send goo2_snd;
+#X msg 476 105 \; goo2_rcv receive goo2a_rcv;
+#X msg 476 143 \; goo2a_rcv receive goo2_rcv;
+#X msg 502 420 \; goo2_rcv init 0;
+#X msg 503 471 \; goo2_rcv init 1;
+#X text 520 188 steady;
+#X obj 486 291 f;
+#X msg 464 270 bang;
+#X floatatom 502 269 3 55 440;
+#X floatatom 537 291 6 440 3520;
+#X obj 486 314 pack 0 0;
+#X text 530 269 left-range-bound;
+#X text 586 291 right-range-bound;
+#X msg 486 339 \; goo2_rcv range \$1 \$2;
+#X msg 363 465 \; goo2_rcv log;
+#X msg 269 466 \; goo2_rcv lin;
+#X text 269 448 linear / logarithmical;
+#X obj 275 133 f;
+#X msg 250 112 bang;
+#X floatatom 291 111 3 15 73;
+#X floatatom 326 133 3 8 50;
+#X obj 275 156 pack 0 0;
+#X text 319 111 width;
+#X text 357 134 height;
+#X msg 275 181 \; goo2_rcv size \$1 \$2;
+#X msg 34 463 \; goo2_rcv label log.freq.;
+#X msg 476 212 \; goo2_rcv steady \$1;
+#X obj 47 100 pack 0 0 0;
+#X obj 47 72 f;
+#X msg 24 24 bang;
+#X floatatom 63 22 3 0 29;
+#X floatatom 79 42 3 0 29;
+#X floatatom 112 58 3 0 29;
+#X text 91 22 background;
+#X text 106 42 front-color;
+#X text 140 59 label-color;
+#X msg 277 22 back;
+#X msg 277 42 front;
+#X msg 277 62 label;
+#X msg 239 22 bang;
+#N canvas 15 207 606 448 RGB_____________ 0;
+#X obj 97 56 inlet;
+#X obj 262 53 inlet;
+#X obj 339 55 inlet;
+#X obj 405 56 inlet;
+#X obj 97 270 bang;
+#X msg 77 295 0;
+#X msg 104 295 1;
+#X obj 146 268 bang;
+#X msg 132 295 0;
+#X msg 160 295 1;
+#X obj 196 269 bang;
+#X msg 187 295 0;
+#X msg 214 295 1;
+#X obj 265 313 spigot;
+#X obj 312 313 spigot;
+#X obj 359 313 spigot;
+#X obj 249 385 outlet;
+#X text 93 33 select;
+#X text 267 28 red;
+#X text 337 30 green;
+#X text 409 30 blue;
+#X obj 405 102 t b f;
+#X obj 339 160 +;
+#X obj 339 185 t b f;
+#X obj 339 216 +;
+#X obj 296 385 outlet;
+#X obj 343 385 outlet;
+#X obj 28 180 loadbang;
+#X obj 97 135 route back front label bang;
+#X obj 343 362 f;
+#X obj 296 361 f;
+#X obj 249 361 f;
+#X obj 262 79 * -65536;
+#X obj 339 80 * -256;
+#X obj 405 80 * -1;
+#X obj 339 247 - 1;
+#X obj 235 168 t b b b b;
+#X connect 0 0 28 0;
+#X connect 1 0 32 0;
+#X connect 2 0 33 0;
+#X connect 3 0 34 0;
+#X connect 4 0 5 0;
+#X connect 4 0 6 0;
+#X connect 5 0 14 1;
+#X connect 5 0 15 1;
+#X connect 6 0 13 1;
+#X connect 7 0 8 0;
+#X connect 7 0 9 0;
+#X connect 8 0 13 1;
+#X connect 8 0 15 1;
+#X connect 9 0 14 1;
+#X connect 10 0 11 0;
+#X connect 10 0 12 0;
+#X connect 11 0 13 1;
+#X connect 11 0 14 1;
+#X connect 12 0 15 1;
+#X connect 13 0 31 1;
+#X connect 14 0 30 1;
+#X connect 15 0 29 1;
+#X connect 21 0 22 0;
+#X connect 21 1 22 1;
+#X connect 22 0 23 0;
+#X connect 23 0 24 0;
+#X connect 23 1 24 1;
+#X connect 24 0 35 0;
+#X connect 27 0 6 0;
+#X connect 28 0 4 0;
+#X connect 28 1 7 0;
+#X connect 28 2 10 0;
+#X connect 28 3 36 0;
+#X connect 29 0 26 0;
+#X connect 30 0 25 0;
+#X connect 31 0 16 0;
+#X connect 32 0 24 0;
+#X connect 33 0 22 0;
+#X connect 34 0 21 0;
+#X connect 35 0 15 0;
+#X connect 35 0 14 0;
+#X connect 35 0 13 0;
+#X connect 36 0 31 0;
+#X connect 36 1 30 0;
+#X connect 36 2 29 0;
+#X connect 36 3 35 0;
+#X restore 277 82 pd RGB_____________;
+#X floatatom 319 52 3 0 255;
+#X floatatom 362 52 3 0 255;
+#X floatatom 405 53 3 0 255;
+#X text 34 -1 preset-colors;
+#X text 290 1 RGB-colors;
+#X text 319 34 red;
+#X text 355 33 green;
+#X text 403 33 blue;
+#X connect 0 0 4 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 1;
+#X connect 3 0 4 1;
+#X connect 4 0 32 0;
+#X connect 7 0 11 0;
+#X connect 8 0 7 0;
+#X connect 9 0 7 1;
+#X connect 10 0 11 1;
+#X connect 11 0 36 0;
+#X connect 12 0 16 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 1;
+#X connect 15 0 16 1;
+#X connect 16 0 35 0;
+#X connect 21 0 25 0;
+#X connect 22 0 21 0;
+#X connect 23 0 21 1;
+#X connect 24 0 25 1;
+#X connect 25 0 33 0;
+#X connect 28 0 64 0;
+#X connect 44 0 48 0;
+#X connect 45 0 44 0;
+#X connect 46 0 44 1;
+#X connect 47 0 48 1;
+#X connect 48 0 51 0;
+#X connect 55 0 59 0;
+#X connect 56 0 55 0;
+#X connect 57 0 55 1;
+#X connect 58 0 59 1;
+#X connect 59 0 62 0;
+#X connect 65 0 31 0;
+#X connect 66 0 65 0;
+#X connect 67 0 66 0;
+#X connect 68 0 66 1;
+#X connect 69 0 65 1;
+#X connect 70 0 65 2;
+#X connect 74 0 78 0;
+#X connect 75 0 78 0;
+#X connect 76 0 78 0;
+#X connect 77 0 78 0;
+#X connect 78 0 65 0;
+#X connect 78 1 65 1;
+#X connect 78 2 65 2;
+#X connect 79 0 78 1;
+#X connect 80 0 78 2;
+#X connect 81 0 78 3;
+#X restore 314 245 pd edit;
+#X obj 221 61 hsl 128 15 0 127 0 0 ddd ddd empty 20 8 192 8 -262144
+-1 -1 10600 1;
+#X text 187 379 updated for Pd version 0.35;
+#X text 30 380 graz \, austria 2002;
+#X obj 168 34 x_all_guis aaa bbb ccc ddd eee fff ggg hhh iii;
+#X connect 1 0 6 0;
+#X connect 2 0 8 0;
+#X connect 3 0 8 0;
+#X connect 7 0 8 0;
+#X connect 8 0 1 0;
+#X connect 8 0 17 0;
+#X connect 10 0 2 0;
+#X connect 12 0 22 0;
+#X connect 14 0 30 0;
+#X connect 15 0 31 0;
+#X connect 17 0 16 0;
+#X connect 18 0 21 0;
+#X connect 18 0 19 0;
+#X connect 19 0 20 0;
+#X connect 24 0 25 0;
+#X connect 25 0 18 0;
+#X connect 29 0 11 0;
+#X connect 30 0 28 0;
+#X connect 31 0 13 0;
diff --git a/pd/doc/5.reference/int.pd b/pd/doc/5.reference/int.pd
new file mode 100644
index 00000000..6b6de2f6
--- /dev/null
+++ b/pd/doc/5.reference/int.pd
@@ -0,0 +1,24 @@
+#N canvas 4 30 605 319 12;
+#X msg 61 101 bang;
+#X floatatom 61 248 0 0 0;
+#X floatatom 73 125 0 0 0;
+#X floatatom 102 198 0 0 0;
+#X text 108 97 outputs the value;
+#X text 110 126 sets and outputs the value;
+#X text 113 220 creation argument initializes the value;
+#X obj 61 222 int 6;
+#X msg 73 148 9.6;
+#X text 106 148 non-integers get truncated;
+#X msg 71 173 -9.6;
+#X text 113 173 toward zero;
+#X text 136 200 set the value but no output;
+#X obj 60 11 int;
+#X text 108 12 - STORE AN INTEGER;
+#X text 37 33 The int object stores a number \, initialized by its creation argument \, which may be reset using its inlet and output by sending it the "bang" message. The output is truncated to an integer ala Max.;
+#X text 315 270 updated for Pd version 0.33;
+#X connect 0 0 7 0;
+#X connect 2 0 7 0;
+#X connect 3 0 7 1;
+#X connect 7 0 1 0;
+#X connect 8 0 7 0;
+#X connect 10 0 7 0;
diff --git a/pd/doc/5.reference/key.pd b/pd/doc/5.reference/key.pd
new file mode 100644
index 00000000..ceab4440
--- /dev/null
+++ b/pd/doc/5.reference/key.pd
@@ -0,0 +1,19 @@
+#N canvas 243 41 464 286 12;
+#X obj 21 10 key;
+#X obj 48 10 keyup;
+#X obj 89 9 keyname;
+#X text 157 8 -- grab keyboard;
+#X obj 38 67 key;
+#X floatatom 38 95 3 0 0;
+#X floatatom 75 95 3 0 0;
+#X obj 75 69 keyup;
+#X floatatom 122 97 3 0 0;
+#X obj 122 71 keyname;
+#X symbolatom 166 98 10 0 0;
+#X text 254 256 updated for Pd version 0.32.;
+#X text 32 143 Key and keyup report the (system dependent) numbers of "printing" keys of the keyboard. Keyname gives the symbolic name of the key \, with a 1 or 0 if it's up or down \, and works with non-printing keys like shift or "F1".;
+#X text 33 203 Caveat -- this only works if Pd actually gets the key events which can depend on the stacking order of windows and/or the pointer location \, depending on the system.;
+#X connect 4 0 5 0;
+#X connect 7 0 6 0;
+#X connect 9 0 8 0;
+#X connect 9 1 10 0;
diff --git a/pd/doc/5.reference/line.pd b/pd/doc/5.reference/line.pd
new file mode 100644
index 00000000..31392b86
--- /dev/null
+++ b/pd/doc/5.reference/line.pd
@@ -0,0 +1,22 @@
+#N canvas 31 15 486 368 12;
+#X floatatom 22 323;
+#X msg 31 216 0 1000;
+#X msg 46 238 39;
+#X obj 66 15 line;
+#X text 106 14 - ramp generator;
+#X obj 22 297 line 100;
+#X text 88 292 creation argument sets time increment. In this implementation you can't set this dtnamically.;
+#X msg 22 193 1 1000;
+#X text 18 36 The line object takes (target \, time) pairs and slews to the specified target over the time given \, updating its output at a "grain rate" given by the creation argument. If you dont' specify a time \, line jumps immediately to the target. Note that the inlet does not remember old values (unlike every other inlet in Pd) -- sending a float causes a jump in the output regardless of whatever time value was specified in some previous message. If the line object receives a message specifying some new target before reaching the previous one \, it takes off from its current value.;
+#X text 93 205 send a pair to ramp to a new value;
+#X text 105 235 send a single number to jump;
+#X text 46 347 see also:;
+#X obj 120 347 line~;
+#X msg 57 263 stop;
+#X text 98 262 "stop" message to stop output;
+#X text 273 348 updated for Pd version 0.27;
+#X connect 1 0 5 0;
+#X connect 2 0 5 0;
+#X connect 5 0 0 0;
+#X connect 7 0 5 0;
+#X connect 13 0 5 0;
diff --git a/pd/doc/5.reference/line~.pd b/pd/doc/5.reference/line~.pd
new file mode 100644
index 00000000..7b470c2d
--- /dev/null
+++ b/pd/doc/5.reference/line~.pd
@@ -0,0 +1,33 @@
+#N canvas 121 54 813 370 12;
+#X obj 33 301 snapshot~;
+#X obj 21 8 line~;
+#X obj 33 226 line~;
+#X floatatom 33 324 0 0 0;
+#X obj 43 274 metro 100;
+#X obj 43 249 r start;
+#X msg 550 21 \; pd dsp 1 \; start bang;
+#X msg 34 106 1 1000;
+#X text 89 105 a pair of numbers starts a ramp;
+#X msg 60 176 2;
+#X text 91 150 a single number jumps to value;
+#X msg 61 200 stop;
+#X text 104 199 "stop" message freezes line~ at its current value;
+#X msg 60 153 0;
+#X msg 43 128 0 5000;
+#X text 10 28 The line~ object generates linear ramps whose levels and timing are determined by messages you send it. The messages may be a single target value (causing the output to jump to the target) or a target and a time in milliseconds (to start a new ramp.);
+#X text 644 36 Click to start;
+#X text 639 94 Click to stop;
+#X text 185 300 see also:;
+#X obj 271 302 line;
+#X msg 550 75 \; pd dsp 0 \; start 0;
+#X text 75 7 - audio ramp generator;
+#X text 576 335 updated for version 0.33;
+#X connect 0 0 3 0;
+#X connect 2 0 0 0;
+#X connect 4 0 0 0;
+#X connect 5 0 4 0;
+#X connect 7 0 2 0;
+#X connect 9 0 2 0;
+#X connect 11 0 2 0;
+#X connect 13 0 2 0;
+#X connect 14 0 2 0;
diff --git a/pd/doc/5.reference/lop~.pd b/pd/doc/5.reference/lop~.pd
new file mode 100644
index 00000000..9f3e85f8
--- /dev/null
+++ b/pd/doc/5.reference/lop~.pd
@@ -0,0 +1,31 @@
+#N canvas 402 99 566 329 12;
+#X obj 70 228 env~;
+#X floatatom 70 248;
+#X floatatom 107 178;
+#X obj 70 206 hip~ 5;
+#X text 119 201 The high pass filter is initialized to cutoff frequencies below 5 Hz.;
+#X obj 12 226 env~;
+#X floatatom 12 245;
+#X text 108 227 env~ gives the amplitude of the signal envelop in dB.;
+#X floatatom 12 107;
+#X msg 452 24 \; pd dsp 1;
+#X msg 452 58 \; pd dsp 0;
+#X obj 83 6 lop~;
+#X text 9 68 The left inlet is the incoming audio signal. The right inlet is the cutoff frequency in Hz.;
+#X obj 12 130 osc~ 100;
+#X text 57 105 <-- scroll to change input frequency;
+#X text 12 266 Compare the value of the original signal on the left with the value of the filtered signal on the right.;
+#X text 8 35 lop~ is a one-pole low pass filter with a specified rolloff frequency.;
+#X text 114 7 - one-pole low pass filter;
+#X msg 70 154 clear;
+#X text 114 153 <-- reinitialize internal state;
+#X text 139 179 <-- set cutoff frequency;
+#X text 364 306 updated for Pd version-0.30;
+#X connect 0 0 1 0;
+#X connect 2 0 3 1;
+#X connect 3 0 0 0;
+#X connect 5 0 6 0;
+#X connect 8 0 13 0;
+#X connect 13 0 5 0;
+#X connect 13 0 3 0;
+#X connect 18 0 3 0;
diff --git a/pd/doc/5.reference/makefilename.pd b/pd/doc/5.reference/makefilename.pd
new file mode 100644
index 00000000..6850699b
--- /dev/null
+++ b/pd/doc/5.reference/makefilename.pd
@@ -0,0 +1,17 @@
+#N canvas 18 58 583 301 12;
+#X floatatom 30 169 0 0 0;
+#X obj 30 223 print;
+#X obj 30 196 makefilename dog%d.aif;
+#X msg 245 166 symbol meat;
+#X msg 355 167 symbol hair;
+#X obj 245 223 print;
+#X obj 245 196 makefilename dog%s.aif;
+#X text 28 49 The Makefilename object generates symbols according to a format string \, for use as a series of filenames \, table names \, or whatnot. You can plug in a variable number or symbol by putting "%d" or "%s" in the string. If you put "%s" in the string be sure to send it a symbol and vice versa... there's no checking.;
+#X text 319 257 updated for Pd version 0.33;
+#X obj 49 17 makefilename;
+#X text 170 18 - format a "name" with a variable field;
+#X connect 0 0 2 0;
+#X connect 2 0 1 0;
+#X connect 3 0 6 0;
+#X connect 4 0 6 0;
+#X connect 6 0 5 0;
diff --git a/pd/doc/5.reference/makenote.pd b/pd/doc/5.reference/makenote.pd
new file mode 100644
index 00000000..e3d003fc
--- /dev/null
+++ b/pd/doc/5.reference/makenote.pd
@@ -0,0 +1,26 @@
+#N canvas 39 28 663 385 12;
+#X floatatom 180 207 0 0 0;
+#X floatatom 110 176 0 0 0;
+#X msg 48 127 60;
+#X obj 41 262 print x1;
+#X obj 180 262 print x2;
+#X floatatom 41 104 0 0 0;
+#X obj 29 14 makenote;
+#X text 113 14 - send note-on messages and schedule note-off for later;
+#X text 19 41 Makenote makes MIDI-style note-on/note-off pairs \, which you can use for MIDI output or to drive note-like processes within Pd.;
+#X msg 48 153 60.5;
+#X text 80 105 numbers at left are "pitches" which may be integers or not.;
+#X text 146 178 "velocity";
+#X text 215 210 duration in milliseconds;
+#X obj 41 235 makenote 3.2 500;
+#X text 193 235 creation arguments initialize velocity and duration;
+#X text 38 316 see also;
+#X obj 117 316 stripnote;
+#X text 389 325 updated for Pd version 0.33;
+#X connect 0 0 13 2;
+#X connect 1 0 13 1;
+#X connect 2 0 13 0;
+#X connect 5 0 13 0;
+#X connect 9 0 13 0;
+#X connect 13 0 3 0;
+#X connect 13 1 4 0;
diff --git a/pd/doc/5.reference/math.pd b/pd/doc/5.reference/math.pd
new file mode 100644
index 00000000..5464b8aa
--- /dev/null
+++ b/pd/doc/5.reference/math.pd
@@ -0,0 +1,60 @@
+#N canvas 0 0 554 555 12;
+#X floatatom 283 263 0 0 0;
+#X floatatom 226 349 0 0 0;
+#X floatatom 226 262 0 0 0;
+#X floatatom 281 486 0 0 0;
+#X floatatom 281 425 0 0 0;
+#X floatatom 185 486 0 0 0;
+#X floatatom 185 425 0 0 0;
+#X floatatom 117 486 0 0 0;
+#X floatatom 117 425 0 0 0;
+#X floatatom 117 326 0 0 0;
+#X floatatom 117 265 0 0 0;
+#X floatatom 30 486 0 0 0;
+#X floatatom 30 425 0 0 0;
+#X floatatom 218 186 0 0 0;
+#X floatatom 135 182 0 0 0;
+#X obj 66 146 sin;
+#X floatatom 66 53 0 0 0;
+#X floatatom 66 180 0 0 0;
+#X obj 66 113 * 6.28319;
+#X obj 66 83 / 360;
+#X obj 135 148 cos;
+#X obj 218 152 tan;
+#X obj 30 456 sqrt;
+#X obj 117 296 atan;
+#X obj 117 456 log;
+#X obj 185 456 exp;
+#X obj 281 456 abs;
+#X obj 226 290 float;
+#X obj 283 290 t b f;
+#X obj 226 319 atan2;
+#X text 87 17 Higher math in Pd;
+#X text 171 58 trig functions take inputs in radians;
+#X text 24 213 The arc tangent takes two forms. The atan2 version takes an (x \, y) pair and gives you an output between -pi and pi.;
+#X text 23 380 also \, square root \, natural logarithm and exponential \, and absolute value:;
+#X text 292 529 updated for Pd version 0.33;
+#X connect 0 0 28 0;
+#X connect 2 0 27 0;
+#X connect 4 0 26 0;
+#X connect 6 0 25 0;
+#X connect 8 0 24 0;
+#X connect 10 0 23 0;
+#X connect 12 0 22 0;
+#X connect 15 0 17 0;
+#X connect 16 0 19 0;
+#X connect 18 0 15 0;
+#X connect 18 0 20 0;
+#X connect 18 0 21 0;
+#X connect 19 0 18 0;
+#X connect 20 0 14 0;
+#X connect 21 0 13 0;
+#X connect 22 0 11 0;
+#X connect 23 0 9 0;
+#X connect 24 0 7 0;
+#X connect 25 0 5 0;
+#X connect 26 0 3 0;
+#X connect 27 0 29 0;
+#X connect 28 0 27 0;
+#X connect 28 1 29 1;
+#X connect 29 0 1 0;
diff --git a/pd/doc/5.reference/message.pd b/pd/doc/5.reference/message.pd
new file mode 100644
index 00000000..13a74f55
--- /dev/null
+++ b/pd/doc/5.reference/message.pd
@@ -0,0 +1,46 @@
+#N canvas 0 365 726 512 12;
+#X msg 88 13 message boxes;
+#X text 55 36 Message boxes hold one or more message. Anytime the message
+box receives any message at all \, the messages in the box are all
+sent to their destinations.;
+#X obj 178 301 print;
+#X msg 178 241 60 64;
+#X msg 178 271 pitch \$1 \, velocity \$2;
+#X msg 49 378 123 \; my-receiver-name 858 \; another-receiver -45;
+#X text 55 84 Clicking on a message also sends it \, so you can use
+messsage boxes for push buttins. For instance \, click here while watching
+the printout window:;
+#X msg 164 141 walk the dog;
+#X obj 164 170 print;
+#X text 281 141 <--- message;
+#X text 265 167 <--- object (different border);
+#X text 35 200 You can separate multiple messages by commas. Also \,
+you can use "$1" \, "$2" \, etc. to make variable messages:;
+#X text 14 323 Finally \, if you separate messages by a semicolon instead
+of a comma \, the following message(s) are re-routed to named objects
+such as "receives":;
+#X obj 49 433 print;
+#X obj 253 378 receive my-receiver-name;
+#X floatatom 253 402 0 0 0;
+#X floatatom 252 449 0 0 0;
+#X obj 252 425 receive another-receiver;
+#X msg 559 349 dog bird monkey \; monkey \;;
+#X msg 559 229 set dog;
+#X msg 567 252 set cat;
+#X text 593 189 this is;
+#X text 592 206 arcane:;
+#X msg 576 303 add monkey;
+#X msg 581 327 add2 bird;
+#X msg 573 277 set;
+#X text 415 479 updated for Pd version 0.34;
+#X connect 3 0 4 0;
+#X connect 4 0 2 0;
+#X connect 5 0 13 0;
+#X connect 7 0 8 0;
+#X connect 14 0 15 0;
+#X connect 17 0 16 0;
+#X connect 19 0 18 0;
+#X connect 20 0 18 0;
+#X connect 23 0 18 0;
+#X connect 24 0 18 0;
+#X connect 25 0 18 0;
diff --git a/pd/doc/5.reference/metro.pd b/pd/doc/5.reference/metro.pd
new file mode 100644
index 00000000..f848e582
--- /dev/null
+++ b/pd/doc/5.reference/metro.pd
@@ -0,0 +1,29 @@
+#N canvas 39 7 634 372 12;
+#X text 19 36 The metro object sends a series of bangs at a constant rate. The right inlet takes the value in milliseconds between each bang. The left inlet takes a 1 or 0 \, turning the metronome on or off.;
+#X obj 67 285 + 1;
+#X obj 32 284 int;
+#X floatatom 32 317 4 0 0;
+#X obj 32 243 metro 500;
+#X obj 5 6 metro;
+#X floatatom 81 220 4 0 0;
+#X text 104 282 These objects work together as a counter. For each bang sent by metro \, the output adds 1;
+#X obj 32 103 loadbang;
+#X msg 32 125 1;
+#X text 92 135 nonzero number or "bang" to start;
+#X msg 49 172 0;
+#X msg 38 148 bang;
+#X msg 49 194 stop;
+#X text 99 181 zero or "stop" to stop.;
+#X text 351 332 Updated for Pd version 0.33;
+#X text 130 220 right inlet sets the rate in msec per tick.;
+#X text 127 243 creation argument initializes rate in msec;
+#X connect 1 0 2 1;
+#X connect 2 0 3 0;
+#X connect 2 0 1 0;
+#X connect 4 0 2 0;
+#X connect 6 0 4 1;
+#X connect 8 0 9 0;
+#X connect 9 0 4 0;
+#X connect 11 0 4 0;
+#X connect 12 0 4 0;
+#X connect 13 0 4 0;
diff --git a/pd/doc/5.reference/midi.pd b/pd/doc/5.reference/midi.pd
new file mode 100644
index 00000000..4b731688
--- /dev/null
+++ b/pd/doc/5.reference/midi.pd
@@ -0,0 +1,129 @@
+#N canvas 68 50 876 553 12;
+#X floatatom 318 379 0 0 0;
+#X floatatom 282 468 0 0 0;
+#X floatatom 200 469 0 0 0;
+#X text 96 330 off;
+#X floatatom 52 383 0 0 0;
+#X floatatom 70 134 0 0 0;
+#X obj 34 108 notein;
+#X floatatom 34 134 0 0 0;
+#X obj 52 488 noteout;
+#X obj 52 462 makenote 64 250;
+#X obj 52 409 metro 500;
+#X msg 52 356 1;
+#X msg 84 356 0;
+#X text 52 333 on;
+#X msg 52 436 60;
+#X obj 200 496 pgmout;
+#X obj 282 494 bendout;
+#X floatatom 416 379 0 0 0;
+#X floatatom 197 136 0 0 0;
+#X floatatom 145 136 0 0 0;
+#X text 41 79 omni;
+#X floatatom 106 134 0 0 0;
+#X obj 145 109 notein 1;
+#X text 145 85 channel 1;
+#X text 194 17 MIDI I/O objects;
+#X text 85 54 notes;
+#X text 334 47 control change;
+#X text 264 69 everything;
+#X floatatom 309 137 0 0 0;
+#X floatatom 271 137 0 0 0;
+#X floatatom 347 136 0 0 0;
+#X obj 271 110 ctlin;
+#X floatatom 440 138 0 0 0;
+#X floatatom 396 138 0 0 0;
+#X obj 396 111 ctlin 7;
+#X text 364 71 specific controller number;
+#X text 410 88 omni;
+#X text 496 89 channel 1;
+#X floatatom 493 140 0 0 0;
+#X obj 493 114 ctlin 7 1;
+#X obj 61 221 pgmin;
+#X floatatom 97 248 0 0 0;
+#X floatatom 61 248 0 0 0;
+#X floatatom 197 250 0 0 0;
+#X floatatom 161 250 0 0 0;
+#X floatatom 307 253 0 0 0;
+#X floatatom 272 253 0 0 0;
+#X floatatom 382 255 0 0 0;
+#X floatatom 343 253 0 0 0;
+#X floatatom 420 256 0 0 0;
+#X obj 161 222 bendin;
+#X obj 272 226 touchin;
+#X obj 343 227 polytouchin;
+#X text 49 167 these can also take an optional channel number as argument but by default are omni:;
+#X text 32 197 program change;
+#X text 155 198 pitch bend;
+#X text 271 203 channel and poly aftertouch;
+#X floatatom 191 380 0 0 0;
+#X floatatom 224 380 0 0 0;
+#X floatatom 260 382 0 0 0;
+#X obj 191 407 ctlout;
+#X obj 318 406 ctlout 7;
+#X text 192 349 control out;
+#X text 314 353 control 7;
+#X text 409 354 control 7 \, channel 4;
+#X obj 416 406 ctlout 7 4;
+#X text 101 277 outputs work similarly. They all take an optional channel as creation argument \, and ctlin takes a control number and a channel. You get inlets to change them in any case. IF you specify no channel \, it's channel 1;
+#X floatatom 355 467 0 0 0;
+#X floatatom 440 466 0 0 0;
+#X obj 355 493 touchout;
+#X obj 440 492 polytouchout;
+#X floatatom 479 467 0 0 0;
+#X floatatom 520 467 0 0 0;
+#X obj 625 218 midiin;
+#X floatatom 625 249 0 0 0;
+#X floatatom 656 249 0 0 0;
+#X floatatom 695 249 0 0 0;
+#X floatatom 726 250 0 0 0;
+#X text 590 155 These two are always omni and;
+#X text 590 174 output the port number instead;
+#X text 594 192 of the channel:;
+#X obj 697 218 sysexin;
+#X obj 623 472 midiout;
+#X text 571 413 use this to output raw MIDI;
+#X text 566 433 (the second inlet is the port;
+#X text 569 451 number.);
+#X text 625 514 updated for Pd release 0.33;
+#X connect 0 0 61 0;
+#X connect 1 0 16 0;
+#X connect 2 0 15 0;
+#X connect 4 0 10 0;
+#X connect 6 0 7 0;
+#X connect 6 1 5 0;
+#X connect 6 2 21 0;
+#X connect 9 0 8 0;
+#X connect 9 1 8 1;
+#X connect 10 0 14 0;
+#X connect 11 0 4 0;
+#X connect 12 0 4 0;
+#X connect 14 0 9 0;
+#X connect 17 0 65 0;
+#X connect 22 0 19 0;
+#X connect 22 1 18 0;
+#X connect 31 0 29 0;
+#X connect 31 1 28 0;
+#X connect 31 2 30 0;
+#X connect 34 0 33 0;
+#X connect 34 1 32 0;
+#X connect 39 0 38 0;
+#X connect 40 0 42 0;
+#X connect 40 1 41 0;
+#X connect 50 0 44 0;
+#X connect 50 1 43 0;
+#X connect 51 0 46 0;
+#X connect 51 1 45 0;
+#X connect 52 0 48 0;
+#X connect 52 1 47 0;
+#X connect 52 2 49 0;
+#X connect 57 0 60 0;
+#X connect 58 0 60 1;
+#X connect 59 0 60 2;
+#X connect 67 0 69 0;
+#X connect 68 0 70 0;
+#X connect 71 0 70 1;
+#X connect 72 0 70 2;
+#X connect 73 0 74 0;
+#X connect 73 1 75 0;
+#X connect 81 0 76 0;
diff --git a/pd/doc/5.reference/moses.pd b/pd/doc/5.reference/moses.pd
new file mode 100644
index 00000000..c1f23c90
--- /dev/null
+++ b/pd/doc/5.reference/moses.pd
@@ -0,0 +1,17 @@
+#N canvas 0 0 624 300 12;
+#X obj 72 196 moses 10;
+#X floatatom 72 164 4 0 0;
+#X floatatom 139 167 4 0 0;
+#X floatatom 72 229 4 0 0;
+#X floatatom 139 230 4 0 0;
+#X obj 63 24 moses;
+#X text 118 23 - part a stream of numbers;
+#X text 303 235 updated for Pd version 0.33;
+#X text 24 64 Moses takes numbers and outputs them at left if they're
+less than a control value \, and at right if they're greater or equal
+to it. The creation argument initializes the control value (10 in this
+example) and the right inlet changes it.;
+#X connect 0 0 3 0;
+#X connect 0 1 4 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 1;
diff --git a/pd/doc/5.reference/my_canvas.pd b/pd/doc/5.reference/my_canvas.pd
new file mode 100644
index 00000000..decda628
--- /dev/null
+++ b/pd/doc/5.reference/my_canvas.pd
@@ -0,0 +1,243 @@
+#N canvas 482 81 568 339 10;
+#X obj 1 1 cnv 15 300 60 foo10_snd foo10_rcv my_canvas=cnv 63 37 192
+17 -257472 -355 0;
+#X text 4 232 (c) musil@iem.kug.ac.at;
+#X text 46 245 IEM KUG;
+#N canvas 219 100 699 530 edit 0;
+#X obj 39 226 f;
+#X msg 17 205 bang;
+#X floatatom 55 204 3 63 88;
+#X floatatom 90 226 3 0 37;
+#X obj 39 249 pack 0 0;
+#X text 117 226 y-label;
+#X text 83 204 x-label;
+#X obj 297 281 f;
+#X msg 275 260 bang;
+#X floatatom 313 259 3 -10 10;
+#X floatatom 348 281 3 -10 10;
+#X obj 297 304 pack 0 0;
+#X obj 309 396 f;
+#X msg 287 375 bang;
+#X floatatom 325 374 3 20 60;
+#X floatatom 360 396 3 150 200;
+#X obj 309 419 pack 0 0;
+#X text 341 259 x-delta;
+#X text 375 281 y-delta;
+#X text 353 374 x-position;
+#X text 387 396 y-position;
+#X obj 59 341 f;
+#X msg 37 320 bang;
+#X floatatom 75 319 3 0 2;
+#X floatatom 110 341 3 4 36;
+#X obj 59 364 pack 0 0;
+#X text 103 319 font;
+#X text 139 341 height;
+#X floatatom 275 183 3 2 20;
+#X msg 52 137 \; foo10_rcv color \$1 \$2;
+#X msg 39 274 \; foo10_rcv label_pos \$1 \$2;
+#X msg 59 390 \; foo10_rcv label_font \$1 \$2;
+#X msg 36 430 \; foo10_rcv label blabla;
+#X msg 36 466 \; foo10_rcv label my_canvas;
+#X msg 309 444 \; foo10_rcv pos \$1 \$2;
+#X msg 297 329 \; foo10_rcv delta \$1 \$2;
+#X obj 505 234 f;
+#X msg 483 213 bang;
+#X floatatom 521 212 5 100 1000;
+#X floatatom 556 234 4 50 500;
+#X obj 505 257 pack 0 0;
+#X text 566 212 width;
+#X text 594 236 height;
+#X msg 505 282 \; foo10_rcv vis_size \$1 \$2;
+#X msg 275 211 \; foo10_rcv size \$1;
+#X text 305 183 selectable size;
+#X msg 483 156 \; foo10a_rcv receive foo10_rcv;
+#X msg 483 119 \; foo10_rcv receive foo10a_rcv;
+#X msg 482 29 \; foo10_rcv send foo10a_snd;
+#X msg 482 67 \; foo10_rcv send foo10_snd;
+#X msg 509 372 \; foo10_rcv get_pos;
+#X obj 510 407 r foo10_snd;
+#X obj 510 428 unpack 0 0;
+#X floatatom 510 453 4 0 0;
+#X floatatom 575 452 4 0 0;
+#X text 490 452 x=;
+#X text 557 452 y=;
+#X obj 52 79 f;
+#X msg 29 31 bang;
+#X floatatom 68 29 3 0 29;
+#X floatatom 103 47 3 0 29;
+#X text 96 29 background;
+#X text 131 48 label-color;
+#X msg 290 25 back;
+#X msg 290 49 label;
+#X msg 252 25 bang;
+#N canvas 15 207 606 448 RGB_____________ 0;
+#X obj 97 56 inlet;
+#X obj 262 53 inlet;
+#X obj 339 55 inlet;
+#X obj 405 56 inlet;
+#X obj 97 270 bang;
+#X msg 77 295 0;
+#X msg 104 295 1;
+#X obj 146 268 bang;
+#X msg 132 295 0;
+#X msg 160 295 1;
+#X obj 265 313 spigot;
+#X obj 312 313 spigot;
+#X obj 249 385 outlet;
+#X text 93 33 select;
+#X text 267 28 red;
+#X text 337 30 green;
+#X text 409 30 blue;
+#X obj 405 102 t b f;
+#X obj 339 160 +;
+#X obj 339 185 t b f;
+#X obj 339 216 +;
+#X obj 296 385 outlet;
+#X obj 28 180 loadbang;
+#X obj 296 361 f;
+#X obj 249 361 f;
+#X obj 262 79 * -65536;
+#X obj 339 80 * -256;
+#X obj 405 80 * -1;
+#X obj 339 247 - 1;
+#X obj 97 135 route back label bang;
+#X obj 235 168 t b b b;
+#X connect 0 0 29 0;
+#X connect 1 0 25 0;
+#X connect 2 0 26 0;
+#X connect 3 0 27 0;
+#X connect 4 0 5 0;
+#X connect 4 0 6 0;
+#X connect 5 0 11 1;
+#X connect 6 0 10 1;
+#X connect 7 0 8 0;
+#X connect 7 0 9 0;
+#X connect 8 0 10 1;
+#X connect 9 0 11 1;
+#X connect 10 0 24 1;
+#X connect 11 0 23 1;
+#X connect 17 0 18 0;
+#X connect 17 1 18 1;
+#X connect 18 0 19 0;
+#X connect 19 0 20 0;
+#X connect 19 1 20 1;
+#X connect 20 0 28 0;
+#X connect 22 0 6 0;
+#X connect 23 0 21 0;
+#X connect 24 0 12 0;
+#X connect 25 0 20 0;
+#X connect 26 0 18 0;
+#X connect 27 0 17 0;
+#X connect 28 0 11 0;
+#X connect 28 0 10 0;
+#X connect 29 0 4 0;
+#X connect 29 1 7 0;
+#X connect 29 2 30 0;
+#X connect 30 0 24 0;
+#X connect 30 1 23 0;
+#X connect 30 2 28 0;
+#X restore 290 86 pd RGB_____________;
+#X floatatom 332 55 3 0 255;
+#X floatatom 375 55 3 0 255;
+#X floatatom 418 56 3 0 255;
+#X text 39 3 preset-colors;
+#X text 301 0 RGB-colors;
+#X text 332 37 red;
+#X text 368 36 green;
+#X text 416 36 blue;
+#X obj 52 104 pack 0 0;
+#X connect 0 0 4 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 1;
+#X connect 3 0 4 1;
+#X connect 4 0 30 0;
+#X connect 7 0 11 0;
+#X connect 8 0 7 0;
+#X connect 9 0 7 1;
+#X connect 10 0 11 1;
+#X connect 11 0 35 0;
+#X connect 12 0 16 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 1;
+#X connect 15 0 16 1;
+#X connect 16 0 34 0;
+#X connect 21 0 25 0;
+#X connect 22 0 21 0;
+#X connect 23 0 21 1;
+#X connect 24 0 25 1;
+#X connect 25 0 31 0;
+#X connect 28 0 44 0;
+#X connect 36 0 40 0;
+#X connect 37 0 36 0;
+#X connect 38 0 36 1;
+#X connect 39 0 40 1;
+#X connect 40 0 43 0;
+#X connect 51 0 52 0;
+#X connect 52 0 53 0;
+#X connect 52 1 54 0;
+#X connect 57 0 75 0;
+#X connect 58 0 57 0;
+#X connect 59 0 57 1;
+#X connect 60 0 75 1;
+#X connect 63 0 66 0;
+#X connect 64 0 66 0;
+#X connect 65 0 66 0;
+#X connect 66 0 75 0;
+#X connect 66 1 75 1;
+#X connect 67 0 66 1;
+#X connect 68 0 66 2;
+#X connect 69 0 66 3;
+#X connect 75 0 29 0;
+#X restore 305 20 pd edit;
+#X floatatom 110 193 4 0 0;
+#X floatatom 147 193 4 0 0;
+#X text 121 209 x;
+#X text 158 209 y;
+#X obj 7 161 metro 100;
+#X obj 33 141 tgl 15 1 empty empty empty 20 8 0 10 -262144 -1 -1 1
+1;
+#X obj 110 145 r from_K1;
+#X floatatom 188 194 4 0 0;
+#X floatatom 225 194 4 0 0;
+#X text 198 210 x;
+#X text 236 210 y;
+#X obj 188 146 r from_K2;
+#X msg 7 185 \; to_K get_pos;
+#N canvas 0 296 395 395 room 1;
+#X obj 1 1 cnv 1 400 400 empty empty type...ctrl+e 150 140 2 17 -33289
+-24198 0;
+#X obj 15 16 cnv 1 1 360 empty empty move_K1_and_K2 115 160 2 17 -166441
+-24198 0;
+#X obj 374 15 cnv 1 1 360 empty empty empty 20 12 2 20 -99865 -66577
+0;
+#X obj 15 15 cnv 1 360 1 empty empty empty 20 12 2 20 -166441 -66577
+0;
+#X obj 17 375 cnv 1 358 1 empty empty empty 20 12 2 20 -99865 -66577
+0;
+#X obj 23 22 cnv 25 25 25 from_K1 to_K K1 1 13 194 14 -261681 -123526
+0;
+#X obj 342 342 cnv 25 25 25 from_K2 to_K K2 1 13 194 14 -225280 -1109
+0;
+#X restore 307 147 pd room;
+#X obj 110 169 unpack;
+#X obj 188 170 unpack;
+#X text 51 92 to modify geometry \, colors \, etc.;
+#X obj 2 115 cnv 1 470 1 empty empty empty 20 12 2 20 -261681 -66577
+0;
+#X text 40 78 of the light-blue;
+#X text 166 78 my_canvas-object \,;
+#X text 5 64 click the properties-dialog on the top-left corner;
+#X obj 361 195 r foo10_rcv;
+#X obj 403 215 s ggg;
+#X text 172 257 updated for Pd version 0.35;
+#X text 21 257 graz \, austria 2002;
+#X obj 187 236 x_all_guis aaa bbb ccc ddd eee fff ggg hhh iii;
+#X connect 8 0 16 0;
+#X connect 9 0 8 0;
+#X connect 10 0 18 0;
+#X connect 15 0 19 0;
+#X connect 18 0 4 0;
+#X connect 18 1 5 0;
+#X connect 19 0 11 0;
+#X connect 19 1 12 0;
+#X connect 25 0 26 0;
diff --git a/pd/doc/5.reference/namecanvas.pd b/pd/doc/5.reference/namecanvas.pd
new file mode 100644
index 00000000..4c408bcb
--- /dev/null
+++ b/pd/doc/5.reference/namecanvas.pd
@@ -0,0 +1,8 @@
+#N canvas 38 53 532 261 12;
+#X obj 66 15 namecanvas;
+#X text 169 15 - ATTACH THIS CANVAS TO A NAME;
+#X obj 204 107 namecanvas bonzo;
+#X msg 205 64 \; bonzo msg 50 50 hi there;
+#X text 252 224 updated for Pd version 0.33;
+#X msg 79 180 \; pd-namecanvas.pd msg 50 70 this is better;
+#X text 44 153 This is obsolete. Instead \, you can just say:;
diff --git a/pd/doc/5.reference/netreceive.pd b/pd/doc/5.reference/netreceive.pd
new file mode 100644
index 00000000..b4bd3f9c
--- /dev/null
+++ b/pd/doc/5.reference/netreceive.pd
@@ -0,0 +1,23 @@
+#N canvas 50 24 682 520 12;
+#X obj 100 323 netreceive 3000;
+#X floatatom 202 353 0 0 0;
+#X obj 100 414 netreceive 3001 1;
+#X text 33 36 The Netreceive object opens a socket for TCP ("stream") or UDP ("datagram") network reception on a specified port. If using TCP \, an outlet gives you the number of Netsend objects (or other compatible clients) have opened connections here.;
+#X text 31 117 Incoming network messages appear on "receive" objects \; it's up to the sender to select which one. Here \, a "receive foo" fields messages sent from the Netsend help window \, q.v.;
+#X text 108 270 first argument: portnumber = 3000;
+#X text 105 291 second argument: 0 or none for TCP \, nonzero for UDP;
+#X text 238 322 <-- TCP \, port 3000;
+#X text 262 413 <-- UDP \, port 3001;
+#X text 236 354 <--- number of open connections;
+#X text 85 12 Netreceive -- listen for incoming messages from network;
+#X text 26 383 incoming messages;
+#X text 203 488 see also:;
+#X obj 289 490 netsend;
+#X obj 100 353 print tcp;
+#X obj 100 442 print udp;
+#X text 425 484 updated for Pd version 0.33;
+#X text 30 207 SECURITY ALERT: don't publish the port number of your netreceive unless you wouldn't mind other people being able to send you messages.;
+#X text 32 168 There are some possibilities for intercommunication with other programs... see the help for "netsend.";
+#X connect 0 0 14 0;
+#X connect 0 1 1 0;
+#X connect 2 0 15 0;
diff --git a/pd/doc/5.reference/netsend.pd b/pd/doc/5.reference/netsend.pd
new file mode 100644
index 00000000..f2eb9bad
--- /dev/null
+++ b/pd/doc/5.reference/netsend.pd
@@ -0,0 +1,55 @@
+#N canvas 84 44 866 530 12;
+#X obj 15 425 netsend;
+#X msg 15 263 connect localhost 3000;
+#X msg 24 403 send foo \$1;
+#X floatatom 24 376 0 0 0;
+#X msg 15 344 disconnect;
+#X msg 285 397 send foo \$1;
+#X floatatom 285 370 0 0 0;
+#X msg 268 344 disconnect;
+#X obj 268 422 netsend 1;
+#X msg 268 263 connect localhost 3001;
+#X floatatom 15 452 0 0 0;
+#X floatatom 268 449 0 0 0;
+#X text 359 422 creation argument: 0 or none for TCP \, nonzero for
+UDP;
+#X text 66 242 TCP;
+#X text 343 239 UDP;
+#X text 197 9 Netsend -- send Pd messages over a network;
+#X text 475 261 Connect to "localhost" port 3000/3001;
+#X text 373 345 Close the connection;
+#X text 325 372 Send messages to "foo" on remote machine;
+#X text 10 473 Outlet is nonzero if connection is open \, zero otherwise.
+;
+#X text 87 38 The Netsend object connects to another machine over the
+network for sending TCP ("stream") or UDP ("datagram") messages. An
+outlet reports whether the connection is open or not. A connection
+request should specify the name or IP address of the other host and
+the port number. There should be a "Netreceive" on the remote host
+with a matching port number.;
+#X obj 409 497 netreceive;
+#X text 318 497 see also:;
+#X text 607 498 updated for Pd version 0.33;
+#X text 87 150 Opt@web.fm has made compatible objects for Max so that
+Pd and Max can intercommunicate: see ftp://fals.ch/pub/pdnets/.;
+#X text 87 186 The Linux version of Pd comes with "pdsend" and "pdreceive"
+standalone programs. These haven't been tested in Windows yet (but
+the source is included in the Pd distribution.);
+#X msg 15 290 connect molloy 3000;
+#X msg 268 290 connect molloy 3001;
+#X msg 15 317 connect bug 3000;
+#X msg 268 317 connect bug 3000;
+#X connect 0 0 10 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 0;
+#X connect 3 0 2 0;
+#X connect 4 0 0 0;
+#X connect 5 0 8 0;
+#X connect 6 0 5 0;
+#X connect 7 0 8 0;
+#X connect 8 0 11 0;
+#X connect 9 0 8 0;
+#X connect 26 0 0 0;
+#X connect 27 0 8 0;
+#X connect 28 0 0 0;
+#X connect 29 0 8 0;
diff --git a/pd/doc/5.reference/noise~.pd b/pd/doc/5.reference/noise~.pd
new file mode 100644
index 00000000..cafc15c3
--- /dev/null
+++ b/pd/doc/5.reference/noise~.pd
@@ -0,0 +1,18 @@
+#N canvas 174 90 458 270 12;
+#X floatatom 77 178 4 0 0;
+#X obj 77 111 noise~;
+#X obj 167 149 print~;
+#X msg 167 123 bang;
+#X obj 282 89 loadbang;
+#X msg 282 114 \; pd dsp 1;
+#X obj 77 150 env~ 4096;
+#X text 67 204 RMS in dB;
+#X text 171 242 updated for Pd version 0.33;
+#X obj 20 11 noise~;
+#X text 84 11 - uniformly distributed white noise;
+#X text 38 49 the output range is -1 to 1...;
+#X connect 1 0 2 0;
+#X connect 1 0 6 0;
+#X connect 3 0 2 0;
+#X connect 4 0 5 0;
+#X connect 6 0 0 0;
diff --git a/pd/doc/5.reference/numbox2.pd b/pd/doc/5.reference/numbox2.pd
new file mode 100644
index 00000000..a26db250
--- /dev/null
+++ b/pd/doc/5.reference/numbox2.pd
@@ -0,0 +1,302 @@
+#N canvas 290 235 617 416 10;
+#X obj 1 1 cnv 8 100 60 empty empty numbox=nbx 20 20 1 18 -262144 -1109
+0;
+#X floatatom 38 300 9 0 0;
+#X msg 47 84 set \$1;
+#X floatatom 38 43 7 0 0;
+#X text 25 363 (c) musil@iem.kug.ac.at;
+#X text 67 376 IEM KUG;
+#X obj 38 324 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X obj 18 47 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X floatatom 47 63 7 0 0;
+#X floatatom 116 324 9 0 0;
+#X floatatom 106 42 7 0 0;
+#X floatatom 183 113 7 0 0;
+#X obj 111 249 ftom;
+#X floatatom 111 271 9 0 0;
+#X floatatom 147 244 9 0 0;
+#X floatatom 221 266 9 0 0;
+#X text 217 151 click properties to;
+#X floatatom 111 112 9 0 0;
+#X obj 111 134 mtof;
+#X text 202 65 (0.01 pixels);
+#X text 57 99 ------------------------------------------;
+#X text 57 286 --------------------------------------------;
+#X text 205 162 modify geometry \, colors \, etc.;
+#X msg 106 63 set \$1;
+#X text 188 44 shift-click & drag;
+#X text 194 54 for fine-tuning;
+#X text 195 203 UP- \, DOWN- \, LEFT- or RIGHT-key;
+#X text 193 214 for moving selected gui-objects;
+#N canvas 239 379 699 530 edit 0;
+#X obj 37 233 f;
+#X msg 15 212 bang;
+#X floatatom 53 211 3 6 88;
+#X floatatom 88 233 3 -20 37;
+#X obj 37 256 pack 0 0;
+#X text 115 233 y-label;
+#X text 81 211 x-label;
+#X obj 287 271 f;
+#X msg 265 250 bang;
+#X floatatom 303 249 3 -10 10;
+#X floatatom 338 271 3 -10 10;
+#X obj 287 294 pack 0 0;
+#X obj 299 381 f;
+#X msg 277 360 bang;
+#X floatatom 315 359 3 20 90;
+#X floatatom 350 381 3 150 200;
+#X obj 299 404 pack 0 0;
+#X text 331 249 x-delta;
+#X text 365 271 y-delta;
+#X text 343 359 x-position;
+#X text 377 381 y-position;
+#X obj 57 348 f;
+#X msg 35 327 bang;
+#X floatatom 73 326 3 0 2;
+#X floatatom 108 348 3 4 36;
+#X obj 57 371 pack 0 0;
+#X text 101 326 font;
+#X text 137 348 height;
+#X floatatom 476 188 1 0 1;
+#X text 523 401 no init;
+#X text 493 453 init value on loadbang;
+#X text 520 188 steady;
+#X obj 486 291 f;
+#X msg 464 270 bang;
+#X floatatom 502 269 4 55 440;
+#X floatatom 537 291 6 440 3520;
+#X obj 486 314 pack 0 0;
+#X text 269 469 linear / logarithmical;
+#X msg 47 158 \; goo4_rcv color \$1 \$2 \$3;
+#X msg 37 281 \; goo4_rcv label_pos \$1 \$2;
+#X msg 57 396 \; goo4_rcv label_font \$1 \$2;
+#X msg 40 442 \; goo4_rcv label blabla;
+#X msg 269 487 \; goo4_rcv lin;
+#X msg 363 486 \; goo4_rcv log;
+#X msg 299 429 \; goo4_rcv pos \$1 \$2;
+#X msg 287 319 \; goo4_rcv delta \$1 \$2;
+#X msg 475 21 \; goo4_rcv send goo4a_snd;
+#X msg 475 59 \; goo4_rcv send goo4_snd;
+#X msg 476 105 \; goo4_rcv receive goo4a_rcv;
+#X msg 476 143 \; goo4a_rcv receive goo4_rcv;
+#X msg 486 339 \; goo4_rcv range \$1 \$2;
+#X msg 502 420 \; goo4_rcv init 0;
+#X msg 503 471 \; goo4_rcv init 1;
+#X text 539 270 bottom-range-bound;
+#X text 586 292 top-range-bound;
+#X obj 286 160 f;
+#X msg 264 139 bang;
+#X floatatom 302 138 3 4 55;
+#X floatatom 337 160 3 15 73;
+#X obj 286 183 pack 0 0;
+#X msg 286 208 \; goo4_rcv size \$1 \$2;
+#X text 330 138 width;
+#X text 368 161 height;
+#X msg 41 478 \; goo4_rcv label log.freq.;
+#X msg 476 212 \; goo4_rcv steady \$1;
+#X obj 47 116 pack 0 0 0;
+#X obj 47 88 f;
+#X msg 24 40 bang;
+#X floatatom 63 38 3 0 29;
+#X floatatom 79 58 3 0 29;
+#X floatatom 112 74 3 0 29;
+#X text 91 38 background;
+#X text 106 58 front-color;
+#X text 140 75 label-color;
+#X msg 285 37 back;
+#X msg 285 57 front;
+#X msg 285 77 label;
+#X msg 247 37 bang;
+#N canvas 15 207 606 448 RGB_____________ 0;
+#X obj 97 56 inlet;
+#X obj 262 53 inlet;
+#X obj 339 55 inlet;
+#X obj 405 56 inlet;
+#X obj 97 270 bang;
+#X msg 77 295 0;
+#X msg 104 295 1;
+#X obj 146 268 bang;
+#X msg 132 295 0;
+#X msg 160 295 1;
+#X obj 196 269 bang;
+#X msg 187 295 0;
+#X msg 214 295 1;
+#X obj 265 313 spigot;
+#X obj 312 313 spigot;
+#X obj 359 313 spigot;
+#X obj 249 385 outlet;
+#X text 93 33 select;
+#X text 267 28 red;
+#X text 337 30 green;
+#X text 409 30 blue;
+#X obj 405 102 t b f;
+#X obj 339 160 +;
+#X obj 339 185 t b f;
+#X obj 339 216 +;
+#X obj 296 385 outlet;
+#X obj 343 385 outlet;
+#X obj 28 180 loadbang;
+#X obj 97 135 route back front label bang;
+#X obj 343 362 f;
+#X obj 296 361 f;
+#X obj 249 361 f;
+#X obj 262 79 * -65536;
+#X obj 339 80 * -256;
+#X obj 405 80 * -1;
+#X obj 339 247 - 1;
+#X obj 235 168 t b b b b;
+#X connect 0 0 28 0;
+#X connect 1 0 32 0;
+#X connect 2 0 33 0;
+#X connect 3 0 34 0;
+#X connect 4 0 5 0;
+#X connect 4 0 6 0;
+#X connect 5 0 14 1;
+#X connect 5 0 15 1;
+#X connect 6 0 13 1;
+#X connect 7 0 8 0;
+#X connect 7 0 9 0;
+#X connect 8 0 13 1;
+#X connect 8 0 15 1;
+#X connect 9 0 14 1;
+#X connect 10 0 11 0;
+#X connect 10 0 12 0;
+#X connect 11 0 13 1;
+#X connect 11 0 14 1;
+#X connect 12 0 15 1;
+#X connect 13 0 31 1;
+#X connect 14 0 30 1;
+#X connect 15 0 29 1;
+#X connect 21 0 22 0;
+#X connect 21 1 22 1;
+#X connect 22 0 23 0;
+#X connect 23 0 24 0;
+#X connect 23 1 24 1;
+#X connect 24 0 35 0;
+#X connect 27 0 6 0;
+#X connect 28 0 4 0;
+#X connect 28 1 7 0;
+#X connect 28 2 10 0;
+#X connect 28 3 36 0;
+#X connect 29 0 26 0;
+#X connect 30 0 25 0;
+#X connect 31 0 16 0;
+#X connect 32 0 24 0;
+#X connect 33 0 22 0;
+#X connect 34 0 21 0;
+#X connect 35 0 15 0;
+#X connect 35 0 14 0;
+#X connect 35 0 13 0;
+#X connect 36 0 31 0;
+#X connect 36 1 30 0;
+#X connect 36 2 29 0;
+#X connect 36 3 35 0;
+#X restore 285 98 pd RGB_____________;
+#X floatatom 327 67 3 0 255;
+#X floatatom 370 67 3 0 255;
+#X floatatom 413 68 3 0 255;
+#X text 34 12 preset-colors;
+#X text 296 9 RGB-colors;
+#X text 327 49 red;
+#X text 363 48 green;
+#X text 411 48 blue;
+#X connect 0 0 4 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 1;
+#X connect 3 0 4 1;
+#X connect 4 0 39 0;
+#X connect 7 0 11 0;
+#X connect 8 0 7 0;
+#X connect 9 0 7 1;
+#X connect 10 0 11 1;
+#X connect 11 0 45 0;
+#X connect 12 0 16 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 1;
+#X connect 15 0 16 1;
+#X connect 16 0 44 0;
+#X connect 21 0 25 0;
+#X connect 22 0 21 0;
+#X connect 23 0 21 1;
+#X connect 24 0 25 1;
+#X connect 25 0 40 0;
+#X connect 28 0 64 0;
+#X connect 32 0 36 0;
+#X connect 33 0 32 0;
+#X connect 34 0 32 1;
+#X connect 35 0 36 1;
+#X connect 36 0 50 0;
+#X connect 55 0 59 0;
+#X connect 56 0 55 0;
+#X connect 57 0 55 1;
+#X connect 58 0 59 1;
+#X connect 59 0 60 0;
+#X connect 65 0 38 0;
+#X connect 66 0 65 0;
+#X connect 67 0 66 0;
+#X connect 68 0 66 1;
+#X connect 69 0 65 1;
+#X connect 70 0 65 2;
+#X connect 74 0 78 0;
+#X connect 75 0 78 0;
+#X connect 76 0 78 0;
+#X connect 77 0 78 0;
+#X connect 78 0 65 0;
+#X connect 78 1 65 1;
+#X connect 78 2 65 2;
+#X connect 79 0 78 1;
+#X connect 80 0 78 2;
+#X connect 81 0 78 3;
+#X restore 327 48 pd edit;
+#X obj 61 345 print;
+#N canvas 276 200 290 224 once 0;
+#X obj 38 47 t b b f;
+#X msg 56 85 1;
+#X obj 31 108 f 0;
+#X obj 31 131 pack 0 0;
+#X obj 31 156 route 0;
+#X obj 38 24 inlet;
+#X obj 31 180 outlet;
+#X connect 0 0 1 0;
+#X connect 0 1 2 0;
+#X connect 0 2 3 1;
+#X connect 1 0 2 1;
+#X connect 2 0 3 0;
+#X connect 3 0 4 0;
+#X connect 4 0 6 0;
+#X connect 5 0 0 0;
+#X restore 61 322 pd once;
+#X obj 249 87 x_all_guis aaa bbb ccc ddd eee fff ggg hhh iii;
+#X text 218 387 updated for Pd version 0.35;
+#X text 42 388 graz \, austria 2002;
+#X text 192 13 gui-number-box:;
+#X obj 106 84 s foo13_rcv;
+#X obj 183 133 s goo14_rcv;
+#X obj 221 244 r goo14_snd;
+#X obj 116 302 r foo13_snd;
+#X obj 47 172 nbx 4 15 100 300 0 0 foo13_snd foo13_rcv empty 45 7 192
+10 -225280 -1109 -1 100 256;
+#X obj 111 200 nbx 5 18 55 3520 1 0 goo14_snd goo14_rcv log.freq. 45
+-10 192 14 -261681 -260818 -90881 55 72;
+#X obj 464 114 nbx 5 14 -1e+37 1e+37 0 0 hhh hhh empty 45 7 192 10
+-262144 -1 -1 0 256;
+#X connect 1 0 6 0;
+#X connect 2 0 39 0;
+#X connect 3 0 39 0;
+#X connect 7 0 39 0;
+#X connect 8 0 2 0;
+#X connect 10 0 23 0;
+#X connect 11 0 36 0;
+#X connect 12 0 13 0;
+#X connect 17 0 18 0;
+#X connect 18 0 40 0;
+#X connect 23 0 35 0;
+#X connect 30 0 29 0;
+#X connect 37 0 15 0;
+#X connect 38 0 9 0;
+#X connect 39 0 30 0;
+#X connect 39 0 1 0;
+#X connect 40 0 12 0;
+#X connect 40 0 14 0;
diff --git a/pd/doc/5.reference/openpanel.pd b/pd/doc/5.reference/openpanel.pd
new file mode 100644
index 00000000..15e5d244
--- /dev/null
+++ b/pd/doc/5.reference/openpanel.pd
@@ -0,0 +1,11 @@
+#N canvas 35 31 585 245 12;
+#X obj 128 136 openpanel;
+#X msg 128 108 bang;
+#X obj 128 161 print;
+#X text 31 11 openpanel -- query you for a filename;
+#X text 48 218 see also:;
+#X obj 136 219 savepanel;
+#X text 272 223 updated for Pd version 0.33;
+#X text 33 59 When Openpanel gets a "bang" an "Open file" browser appears on the screen. If you select a file \, its name appears on the outlet.;
+#X connect 0 0 2 0;
+#X connect 1 0 0 0;
diff --git a/pd/doc/5.reference/operators.pd b/pd/doc/5.reference/operators.pd
new file mode 100644
index 00000000..64b47e4d
--- /dev/null
+++ b/pd/doc/5.reference/operators.pd
@@ -0,0 +1,31 @@
+#N canvas 52 109 635 355 12;
+#X obj 29 172 +;
+#X floatatom 41 113 0 0 0;
+#X floatatom 29 197 0 0 0;
+#X floatatom 51 143 0 0 0;
+#X msg 29 82 bang;
+#X obj 44 6 +;
+#X text 27 307 see also:;
+#X obj 186 314 +~;
+#X text 79 5 (etc.) -- ARITHMETIC;
+#X text 72 88 Bang outputs sum;
+#X text 79 112 Numbers in left inlet add and output sum;
+#X text 93 142 Numbers in right inlet only change the inlet's value;
+#X obj 113 314 trigger;
+#X text 348 325 last updated for version 0.33;
+#X text 93 189 You can supply a creation argument to initialize the right inlet:;
+#X text 29 29 The floating point binary operators are + \, - \, * \, / \, pow \, max \, and min. Note that pow only works for nonnegative mantissas.;
+#X floatatom 101 225 0 0 0;
+#X floatatom 101 275 0 0 0;
+#X obj 101 250 pow -1;
+#X floatatom 179 225 0 0 0;
+#X floatatom 179 275 0 0 0;
+#X obj 179 250 min 20;
+#X connect 0 0 2 0;
+#X connect 1 0 0 0;
+#X connect 3 0 0 1;
+#X connect 4 0 0 0;
+#X connect 16 0 18 0;
+#X connect 18 0 17 0;
+#X connect 19 0 21 0;
+#X connect 21 0 20 0;
diff --git a/pd/doc/5.reference/osc~.pd b/pd/doc/5.reference/osc~.pd
new file mode 100644
index 00000000..2bd5f0df
--- /dev/null
+++ b/pd/doc/5.reference/osc~.pd
@@ -0,0 +1,58 @@
+#N canvas 85 32 811 508 12;
+#X obj 252 320 dac~ 1;
+#X obj 252 292 *~;
+#X floatatom 156 115 0 0 0;
+#X obj 276 264 line~;
+#X msg 276 208 0.1 100;
+#X msg 295 233 0 100;
+#X text 347 203 on;
+#X text 344 232 off;
+#X text 333 261 envelope;
+#X text 333 274 generator;
+#X text 260 183 amplitude controls:;
+#X text 250 344 audio output;
+#X text 424 426 see also:;
+#X obj 580 428 cos~;
+#X obj 629 428 tabread4~;
+#X obj 68 14 osc~;
+#X text 142 16 - cosine wave oscillator;
+#X obj 126 294 metro 500;
+#X obj 126 269 r metro;
+#X text 88 344 graph the output;
+#X obj 510 427 phasor~;
+#X msg 571 79 \; metro 0;
+#X msg 570 20 \; pd dsp 1 \; metro 1;
+#X floatatom 637 245 0 0 0;
+#X obj 637 275 sig~;
+#X text 522 266 convert to;
+#X text 512 282 audio signal;
+#X text 518 305 oscillator;
+#X text 479 243 frequency control;
+#X obj 637 306 osc~;
+#X text 3 120 change frequency;
+#X text 244 145 <-- creation argument sets initial frequency;
+#X text 231 123 v-- inlet resets phase;
+#X graph graph1 0 -1 100 1 94 388 294 488;
+#X array array99 100 float;
+#X pop;
+#X text 16 39 The osc~ object outputs a cosine wave. If no argument is supplied \, the input is taken to be an audio signal. With a floating-point argument \, osc~ takes floating-point messages to change frequency.;
+#X text 510 336 invoked without argument to;
+#X text 512 360 specify audio signal input;
+#X text 2 105 incoming numbers;
+#X obj 89 322 tabwrite~ array99;
+#X obj 156 144 osc~ 1000;
+#X text 546 480 updated for Pd version 0.33;
+#X text 655 39 <-Click to start;
+#X text 648 88 <-Click to stop;
+#X connect 1 0 0 0;
+#X connect 2 0 39 0;
+#X connect 3 0 1 1;
+#X connect 4 0 3 0;
+#X connect 5 0 3 0;
+#X connect 17 0 38 0;
+#X connect 18 0 17 0;
+#X connect 18 0 17 0;
+#X connect 23 0 24 0;
+#X connect 24 0 29 0;
+#X connect 39 0 1 0;
+#X connect 39 0 38 0;
diff --git a/pd/doc/5.reference/otherbinops.pd b/pd/doc/5.reference/otherbinops.pd
new file mode 100644
index 00000000..3f310818
--- /dev/null
+++ b/pd/doc/5.reference/otherbinops.pd
@@ -0,0 +1,90 @@
+#N canvas 20 43 698 447 12;
+#X floatatom 524 338 0 0 0;
+#X floatatom 353 338 0 0 0;
+#X floatatom 298 338 0 0 0;
+#X floatatom 413 338 0 0 0;
+#X floatatom 467 338 0 0 0;
+#X obj 524 311 <;
+#X obj 298 262 r left;
+#X obj 540 267 r right;
+#X floatatom 71 335 0 0 0;
+#X floatatom 19 334 0 0 0;
+#X floatatom 119 335 0 0 0;
+#X floatatom 163 334 0 0 0;
+#X obj 16 45 &;
+#X obj 66 45 |;
+#X obj 118 45 &&;
+#X obj 169 45 ||;
+#X obj 19 307 &;
+#X obj 71 308 |;
+#X obj 119 308 &&;
+#X obj 163 307 ||;
+#X text 13 73 The Logical Operators;
+#X obj 19 266 r left;
+#X obj 218 266 r right;
+#X obj 12 118 >;
+#X obj 61 118 >=;
+#X obj 114 118 ==;
+#X obj 215 119 <=;
+#X obj 262 119 <;
+#X text 11 153 The Relational Operators;
+#X obj 298 312 >;
+#X obj 353 312 >=;
+#X obj 413 312 ==;
+#X obj 467 312 <=;
+#X text 16 190 relational output is logical- and negative numbers BAD
+in bitwise logical operators. These operators as defined by C programming
+language. (inputs and outputs are converted to and from floating point).
+;
+#X floatatom 410 65 0 0 0;
+#X obj 410 92 s left;
+#X floatatom 481 66 0 0 0;
+#X obj 481 94 t b f;
+#X obj 481 122 s left;
+#X obj 541 122 s right;
+#X obj 166 118 !=;
+#X text 377 42 set left and right inputs here;
+#X floatatom 202 335 0 0 0;
+#X floatatom 246 334 0 0 0;
+#X obj 208 46 <<;
+#X obj 259 46 >>;
+#X obj 202 308 <<;
+#X obj 246 307 >>;
+#X text 421 383 last updated for version 0.32;
+#X connect 5 0 0 0;
+#X connect 6 0 29 0;
+#X connect 6 0 30 0;
+#X connect 6 0 31 0;
+#X connect 6 0 32 0;
+#X connect 6 0 5 0;
+#X connect 7 0 29 1;
+#X connect 7 0 30 1;
+#X connect 7 0 31 1;
+#X connect 7 0 32 1;
+#X connect 7 0 5 1;
+#X connect 16 0 9 0;
+#X connect 17 0 8 0;
+#X connect 18 0 10 0;
+#X connect 19 0 11 0;
+#X connect 21 0 16 0;
+#X connect 21 0 17 0;
+#X connect 21 0 18 0;
+#X connect 21 0 19 0;
+#X connect 21 0 46 0;
+#X connect 21 0 47 0;
+#X connect 22 0 19 1;
+#X connect 22 0 18 1;
+#X connect 22 0 17 1;
+#X connect 22 0 16 1;
+#X connect 22 0 47 1;
+#X connect 22 0 46 1;
+#X connect 29 0 2 0;
+#X connect 30 0 1 0;
+#X connect 31 0 3 0;
+#X connect 32 0 4 0;
+#X connect 34 0 35 0;
+#X connect 36 0 37 0;
+#X connect 37 0 38 0;
+#X connect 37 1 39 0;
+#X connect 46 0 42 0;
+#X connect 47 0 43 0;
diff --git a/pd/doc/5.reference/pack.pd b/pd/doc/5.reference/pack.pd
new file mode 100644
index 00000000..c979d480
--- /dev/null
+++ b/pd/doc/5.reference/pack.pd
@@ -0,0 +1,37 @@
+#N canvas 14 8 809 354 12;
+#X floatatom 19 86 0 0 0;
+#X msg 29 115 bang;
+#X floatatom 49 138 0 0 0;
+#X floatatom 188 138 0 0 0;
+#X obj 19 254 print;
+#X msg 86 138 symbol cat;
+#X obj 82 9 pack;
+#X text 28 319 See also;
+#X obj 106 321 unpack;
+#X text 14 34 The pack object takes a series of inputs and outputs
+a concatenated list. The number of creation arguments determines the
+number of inlets.;
+#X text 60 85 <-- number in first inlet generates output;
+#X text 70 114 <-- bang generates output without resetting first value
+;
+#X text 226 135 <-- numbers and symbols in the corresponding inlets
+change the values without causing output (see "trigger" for a way to
+change this behavior.);
+#X text 250 187 <-- as with any Pd object \, you can send a list whose
+atoms are automatically distributed to the corresponding inlets.;
+#X msg 175 190 1 2 dog;
+#X obj 167 321 trigger;
+#X obj 19 227 pack 100 0 s 0;
+#X text 121 9 - combine several atoms into one message;
+#X text 155 226 <-- creation arguments specify the number of inlets
+and their types: a number make a numeric outlet (and initializes the
+value). A symbol argument can start with "s" \, "f" \, or "p" to specify
+a "symbol" \, "float" (number) \, or pointer outlet.;
+#X text 538 331 updated for Pd version 0.34;
+#X connect 0 0 16 0;
+#X connect 1 0 16 0;
+#X connect 2 0 16 1;
+#X connect 3 0 16 3;
+#X connect 5 0 16 2;
+#X connect 14 0 16 0;
+#X connect 16 0 4 0;
diff --git a/pd/doc/5.reference/pd.pd b/pd/doc/5.reference/pd.pd
new file mode 100644
index 00000000..f7db8f66
--- /dev/null
+++ b/pd/doc/5.reference/pd.pd
@@ -0,0 +1,52 @@
+#N canvas 32 130 677 385 12;
+#N canvas 0 0 600 400 /SUBPATCH/ 0;
+#X restore 59 10 pd;
+#X text 88 12 - subpatch;
+#X obj 218 10 inlet;
+#X text 263 10 - control inlet;
+#X obj 442 11 inlet~;
+#X text 494 12 - audio inlet;
+#X obj 215 39 outlet;
+#X text 265 39 - control outlet;
+#X obj 435 40 outlet~;
+#X text 494 40 - audio outlet;
+#X text 37 74 Type "pd" into an object box to make a subpatch. When
+in run mode you can click on the object to open the subpatch. You can
+name the subpatch with an argument:;
+#N canvas 0 0 600 396 my-subpatch 0;
+#X restore 133 131 pd my-subpatch;
+#N canvas 0 0 600 392 my-subpatch-with-inlets-and-outlets 0;
+#X obj 68 126 inlet;
+#X text 20 96 control inlet for receiving messages;
+#X floatatom 68 154 0 0 0;
+#X floatatom 71 255 0 0 0;
+#X obj 71 287 outlet;
+#X text 35 225 control outlet for sending message;
+#X obj 403 121 inlet~;
+#X obj 403 172 print~;
+#X msg 418 146 bang;
+#X obj 402 314 outlet~;
+#X obj 402 288 sig~ 34;
+#X connect 0 0 2 0;
+#X connect 3 0 4 0;
+#X connect 6 0 7 0;
+#X connect 8 0 7 0;
+#X connect 10 0 9 0;
+#X restore 86 272 pd my-subpatch-with-inlets-and-outlets;
+#X text 55 174 and you can put inlets and outlets by making "inlet"
+objects \, etc \, in the subpatch (open the patch below to see them.)
+;
+#X obj 423 322 print~;
+#X msg 362 294 bang;
+#X obj 422 243 sig~ 12;
+#X floatatom 86 246 0 0 0;
+#X floatatom 86 298 0 0 0;
+#X text 441 272 (check that audio is on);
+#X text 52 221 messages in and out;
+#X text 392 220 audio in and out;
+#X text 391 351 updated for Pd version 0.26;
+#X connect 12 0 18 0;
+#X connect 12 1 14 0;
+#X connect 15 0 14 0;
+#X connect 16 0 12 1;
+#X connect 17 0 12 0;
diff --git a/pd/doc/5.reference/phasor~.pd b/pd/doc/5.reference/phasor~.pd
new file mode 100644
index 00000000..2da01cf9
--- /dev/null
+++ b/pd/doc/5.reference/phasor~.pd
@@ -0,0 +1,36 @@
+#N canvas 5 31 889 373 12;
+#X graph graph1 0 1 100 -1 67 250 267 350;
+#X array array99 100 float;
+#X pop;
+#X obj 29 181 metro 500;
+#X obj 13 126 phasor~;
+#X floatatom 13 76 0 0 0;
+#X obj 57 12 phasor~;
+#X obj 29 156 r metro;
+#X obj 13 100 sig~ 890;
+#X text 78 75 <-- specify frequency;
+#X text 92 98 <-- convert it to audio signal;
+#X msg 409 75 \; metro 0;
+#X msg 405 18 \; pd dsp 1 \; metro 1;
+#X text 494 32 <-- Click to start;
+#X text 479 79 <-- Click to stop;
+#X text 129 14 - sawtooth generator;
+#X text 170 207 <-- graph the output;
+#X text 82 128 <-- right inlet resets phase;
+#X obj 425 227 phasor~ 440;
+#X floatatom 425 203 0 0 0;
+#X text 348 118 The phasor~ object outputs a sawtooth signal \, traditionally used for table lookup via cos~ or tabread4~. If no argument is supplied \, the input is taken to be an audio signal \; with a floating-point argument \, phasor~ takes floating-point messages to change frequency.;
+#X text 294 246 Invoked above with argument for non-signal input. Incoming messages override the initial value.;
+#X text 311 301 see also:;
+#X obj 396 301 osc~;
+#X obj 439 301 cos~;
+#X obj 481 301 tabread4~;
+#X text 627 345 updated for Pd version 0.33;
+#X obj 13 205 tabwrite~ array99;
+#X connect 1 0 25 0;
+#X connect 2 0 25 0;
+#X connect 3 0 6 0;
+#X connect 5 0 1 0;
+#X connect 5 0 1 0;
+#X connect 6 0 2 0;
+#X connect 17 0 16 0;
diff --git a/pd/doc/5.reference/pipe.pd b/pd/doc/5.reference/pipe.pd
new file mode 100644
index 00000000..272057ed
--- /dev/null
+++ b/pd/doc/5.reference/pipe.pd
@@ -0,0 +1,41 @@
+#N canvas 99 89 737 480 12;
+#X floatatom 52 127 0 0 0;
+#X floatatom 127 227 0 0 0;
+#X floatatom 52 284 0 0 0;
+#X floatatom 544 281 0 0 0;
+#X floatatom 535 392 0 0 0;
+#X obj 534 364 pipe 5 6 7 1000;
+#X obj 543 307 t f f f;
+#X obj 563 338 + 1;
+#X obj 597 337 + 2;
+#X floatatom 590 390 0 0 0;
+#X floatatom 658 391 0 0 0;
+#X text 32 433 see also:;
+#X text 129 13 pipe -- message "delay line";
+#X obj 52 253 pipe 2000;
+#X text 91 125 numbers to store and output later;
+#X text 117 148 output all stored messages immediately;
+#X msg 63 152 flush;
+#X msg 65 180 clear;
+#X text 113 180 forget all stored messages;
+#X text 91 286 delayed output;
+#X obj 116 435 delay;
+#X obj 167 435 timer;
+#X text 51 42 The Pipe object stores a sequence of messages and outputs them after a specified delay time in miliseconds. You can change the delay time as you wish. The outputs are sorted automatically.;
+#X text 140 254 creation argument initializes delay time;
+#X text 163 228 set delay time;
+#X text 487 449 updated for Pd version 0.33;
+#X text 21 330 You can specify compound messages (lists) by adding arguments which set their type and initial value as in "pack." In this case the delay time comes last and is changed by the last inlet. You can also pack symbols and pointers but this feature is UNTESTED.;
+#X connect 0 0 13 0;
+#X connect 1 0 13 1;
+#X connect 3 0 6 0;
+#X connect 5 0 4 0;
+#X connect 5 1 9 0;
+#X connect 5 2 10 0;
+#X connect 6 0 5 0;
+#X connect 6 1 7 0;
+#X connect 6 2 8 0;
+#X connect 7 0 5 1;
+#X connect 8 0 5 2;
+#X connect 13 0 2 0;
+#X connect 16 0 13 0;
diff --git a/pd/doc/5.reference/plot.pd b/pd/doc/5.reference/plot.pd
new file mode 100644
index 00000000..d25eaa15
--- /dev/null
+++ b/pd/doc/5.reference/plot.pd
@@ -0,0 +1,58 @@
+#N struct help-plot-template float x float y array array1 help-plot-array1-template
+array array2 help-plot-array2-template array array3 help-plot-array3-template
+;
+#N struct help-plot-array1-template float y;
+#N struct help-plot-array2-template float x float y;
+#N struct help-plot-array3-template float y float w;
+#N canvas 398 0 516 229 12;
+#N canvas 89 309 626 539 help-plot-template 1;
+#X text 29 34 creation arguments:;
+#X text 48 71 - RGB color (0=black \, 999=white \, 900=red \, 90=green
+\, 9=blue \, 555=grey \, etc.);
+#X obj 24 387 template float x float y array array1 help-plot-array1-template
+array array2 help-plot-array2-template array array3 help-plot-array3-template
+;
+#X text 47 52 - OPTIONAL word "curve" to specify bezier;
+#X text 46 98 - line width;
+#X text 46 114 - relative x and y location;
+#X text 47 130 - x spacing;
+#X obj 39 217 plot curve array2 70 3 100 0;
+#X obj 30 308 plot curve array3 9 1 120 50 20;
+#X obj 45 12 plot array1 500 1 10 15 20;
+#X text 29 147 This first example plots the red trace (500) \, width
+1 \, at point (10 \, 15) \, with horizontal spacing 20 The black diamonds
+come from the template of the array1 element itself.;
+#X text 62 239 This is the green spiral (color 70 \, line width 3 \,
+location (100 \, 0). Since the template for array2 contains an "x"
+cariable \, play ignores x spacing requests and takes x from the data
+itself.;
+#X text 50 328 If a "w" variable is present in the template as for
+array3 \, it is added to the line width.;
+#X text 33 366 here's the template for all this:;
+#X obj 27 501 filledpolygon 509 509 0 -10 -10 10 -10 10 10 -10 10;
+#X text 27 454 To see the data itself \, select "properties" for the
+scalar by right clicking on the purple square.;
+#X restore 243 78 pd help-plot-template;
+#N canvas 196 292 273 120 help-plot-array1-template 0;
+#X obj 30 71 filledpolygon 0 0 0 -5 0 0 5 5 0 0 -5;
+#X obj 32 27 template float y;
+#X restore 242 101 pd help-plot-array1-template;
+#N canvas 161 163 273 120 help-plot-array2-template 0;
+#X obj 32 26 template float x float y;
+#X restore 243 123 pd help-plot-array2-template;
+#N canvas 0 0 411 207 help-plot-data 1;
+#X scalar help-plot-template 39 73 \; 0 \; 20 \; 0 \; 30 \; 0 \; \;
+0 0 \; 0 10 \; 20 0 \; 0 -30 \; -40 0 \; 0 50 \; 60 0 \; \; 0 0 \;
+10 10 \; 0 10 \; 0 1 \; 20 1 \; 20 10 \; 20 1 \; \;;
+#X restore 242 57 pd help-plot-data;
+#X text 23 139 see also:;
+#X obj 30 184 drawnumber;
+#X obj 29 163 template;
+#X obj 35 22 plot;
+#X text 87 21 -- draw array elements of scalars;
+#X obj 29 206 drawpolygon;
+#N canvas 161 163 273 120 help-plot-array3-template 0;
+#X obj 43 32 template float y float w;
+#X restore 242 144 pd help-plot-array3-template;
+#X text 8 79 explanation is in here-->;
+#X text 264 203 updated for Pd version 0.35;
diff --git a/pd/doc/5.reference/pointer.pd b/pd/doc/5.reference/pointer.pd
new file mode 100644
index 00000000..96a22ff1
--- /dev/null
+++ b/pd/doc/5.reference/pointer.pd
@@ -0,0 +1,79 @@
+#N struct template2 float x float y;
+#N struct template1 float x float y float z;
+#N canvas 223 0 715 654 12;
+#X text 20 572 see also:;
+#X obj 21 10 pointer;
+#X text 95 10 -- remember the location of a scalar in a list;
+#N canvas 164 72 425 146 help-pointer-template1 0;
+#X obj 18 81 filledpolygon z 0 1 0 0 20 0 20 30 0 30;
+#X obj 60 21 struct template1 float x float y float z;
+#X restore 327 386 pd help-pointer-template1;
+#N canvas 26 456 510 145 help-pointer-template2 0;
+#X obj 52 78 filledcurve 909 0 0 0 0 30 30 60 0 30 -30 0 0;
+#X obj 60 21 struct template2 float x float y;
+#X restore 327 409 pd help-pointer-template2;
+#X obj 23 592 get;
+#X obj 56 592 set;
+#X obj 91 592 append;
+#X obj 152 592 getsize;
+#X obj 220 593 setsize;
+#X obj 290 593 element;
+#X obj 23 617 sublist;
+#N canvas 0 0 312 185 help-pointer-data 1;
+#X scalar template2 20 97 \;;
+#X scalar template1 80 17 90 \;;
+#X scalar template1 120 117 9 \;;
+#X restore 327 364 pd help-pointer-data;
+#X obj 54 360 pointer;
+#X msg 54 231 traverse pd-help-pointer-data;
+#X msg 67 255 bang;
+#X text 109 256 outputs current value;
+#X msg 69 281 next;
+#X obj 54 385 print out1;
+#X obj 167 371 print out2;
+#X text 119 274 moves forward one item and outputs pointer \; if we
+reach the end \, a "bang" goes to out2.;
+#X text 16 426 Optional arguments to pointer allow you to select according
+to the class of the scalar being output:;
+#X msg 74 487 next;
+#X msg 60 464 traverse pd-help-pointer-data;
+#X obj 60 515 pointer help-pointer-template1 help-pointer-template2
+;
+#X obj 60 541 print template1;
+#X obj 198 541 print template2;
+#X obj 338 541 print other;
+#X obj 441 541 print bangout;
+#X text 333 232 sets to the "head" of the list;
+#X text 29 34 "Pointer" is a storage object like "float" \, except
+that the thing stored is the location of a scalar somewhere. You can
+send a pointer a value (perhaps from another "pointer" object). The
+right inlet takes pointers and simply stores them. A bang in the left
+outputs the pointer \, and a pointer in the left both sets and outputs
+the value.;
+#X text 29 132 The value of a pointer can either indicate a real scalar
+\, or else the "head" (before the first element) of the list. This
+allows you to point to an empty list \, and also \, to "append" a scalar
+to the beginning of the list.;
+#X text 29 191 Pointers are "safe": if you delete a scalar pointers
+to it are marked invalid.;
+#X text 166 391 bang at end;
+#X text 167 407 of list;
+#X text 53 405 output;
+#X text 445 617 updated for Pd version 0.35;
+#X obj 92 616 struct;
+#X msg 71 307 vnext 1;
+#X text 149 308 "vnext" gets the next object (if arg is 0) or the next
+selected object (if arg is 1 -- but the window must be visible for
+the "selection" to make sense).;
+#X connect 13 0 18 0;
+#X connect 13 1 19 0;
+#X connect 14 0 13 0;
+#X connect 15 0 13 0;
+#X connect 17 0 13 0;
+#X connect 22 0 24 0;
+#X connect 23 0 24 0;
+#X connect 24 0 25 0;
+#X connect 24 1 26 0;
+#X connect 24 2 27 0;
+#X connect 24 3 28 0;
+#X connect 38 0 13 0;
diff --git a/pd/doc/5.reference/poly.pd b/pd/doc/5.reference/poly.pd
new file mode 100644
index 00000000..0b34f99e
--- /dev/null
+++ b/pd/doc/5.reference/poly.pd
@@ -0,0 +1,30 @@
+#N canvas 0 0 600 496 12;
+#X text 155 228 <-- scroll to change the value of delay in milliseconds.;
+#X text 406 383 updated for Pd version 0.25;
+#X text 42 383 see also:;
+#X obj 66 15 poly;
+#X text 101 14 - MIDI-STYLE POLYPHONIC VOICE ALLOCATOR;
+#X text 12 42 The poly object takes a stream of pitch/velocity pairs and outputs triples containing voice number \, pitch and velocity. You can pack the output and use the route object to route messages among a bank of voices depending on the first outlet. Poly can be configured to do voice stealing or not (the default.);
+#X obj 110 384 route;
+#X obj 154 384 makenote;
+#X obj 52 254 poly 4 1;
+#X text 134 253 <-- first argument \, number of voices \; second argument selects voice stealing;
+#X msg 52 168 60 64;
+#X msg 103 168 60 0;
+#X msg 147 168 62 64;
+#X msg 194 168 62 0;
+#X obj 52 280 pack 0 0 0;
+#X obj 52 306 print;
+#X text 97 305 Output is in the printout window.;
+#X msg 254 177 stop;
+#X msg 296 177 clear;
+#X connect 8 0 14 0;
+#X connect 8 1 14 1;
+#X connect 8 2 14 2;
+#X connect 10 0 8 0;
+#X connect 11 0 8 0;
+#X connect 12 0 8 0;
+#X connect 13 0 8 0;
+#X connect 14 0 15 0;
+#X connect 17 0 8 0;
+#X connect 18 0 8 0;
diff --git a/pd/doc/5.reference/print.pd b/pd/doc/5.reference/print.pd
new file mode 100644
index 00000000..50af069a
--- /dev/null
+++ b/pd/doc/5.reference/print.pd
@@ -0,0 +1,13 @@
+#N canvas 349 65 615 247 12;
+#X msg 102 52 walk the dog;
+#X msg 29 51 bang;
+#X msg 70 51 234;
+#X obj 29 96 print x1;
+#X obj 21 10 print;
+#X text 37 134 Print prints out the messages it receives on the "terminal
+window" that Pd is run from.;
+#X text 249 200 updated for Pd version 0.31.;
+#X text 73 10 -- print messages to terminal window;
+#X connect 0 0 3 0;
+#X connect 1 0 3 0;
+#X connect 2 0 3 0;
diff --git a/pd/doc/5.reference/print~.pd b/pd/doc/5.reference/print~.pd
new file mode 100644
index 00000000..b3a9c429
--- /dev/null
+++ b/pd/doc/5.reference/print~.pd
@@ -0,0 +1,18 @@
+#N canvas 118 333 531 212 10;
+#X msg 74 143 2;
+#X msg 455 77 \; pd dsp 0;
+#X msg 454 40 \; pd dsp 1;
+#X obj 62 177 print~;
+#X msg 74 118 bang;
+#X obj 62 92 phasor~ 1000;
+#X text 122 119 bang prints one vector;
+#X obj 454 18 loadbang;
+#X text 109 142 print two or more successive vectors;
+#X obj 32 12 print~;
+#X text 85 12 - print out raw values of a signal;
+#X text 301 171 Updated for Pd version 0.33;
+#X text 19 44 The print~ object takes a signal input and prints one or more vectors out when you send it a bang or a number. By default a vector is 64 samples.;
+#X connect 0 0 3 0;
+#X connect 4 0 3 0;
+#X connect 5 0 3 0;
+#X connect 7 0 2 0;
diff --git a/pd/doc/5.reference/qlist.pd b/pd/doc/5.reference/qlist.pd
new file mode 100644
index 00000000..a5b2a574
--- /dev/null
+++ b/pd/doc/5.reference/qlist.pd
@@ -0,0 +1,76 @@
+#N canvas 7 31 1178 587 12;
+#X obj 546 328 qlist;
+#X msg 592 110 rewind;
+#X msg 591 135 next;
+#X floatatom 546 382 0 0 0;
+#X msg 593 54 bang;
+#X obj 441 515 r this;
+#X obj 544 515 r that;
+#X obj 441 544 print this;
+#X obj 544 544 print that;
+#X obj 560 356 print done;
+#X msg 593 80 tempo 1;
+#X text 18 51 The qlist object reads text files containing time-tagged
+Pd messages. You can have them sequenced automatically (by sending
+a "bang" message \, possibly changing speed via "tempo" messages) or
+manually via the "rewind" and "next" messages.;
+#X text 15 136 To run the qlist automatically \, send it a "read" message
+(the filename is relative to the directory the patch is in) and later
+a "bang." Messages in the file are separated by semicolons. Optional
+leading numbers are delay times in milliseconds. If the tempo is diffrerent
+from 1 the messages are sent faster or slower accordingly. Messages
+should start with a symbol giving the destination object. In the file
+"qlist.q" used here \, the messages go to objects "this" and "that"
+which are receives below.;
+#X text 17 281 To run it manually \, send "rewind" followed by "next".
+All messages not preceeded by numbers are sent. As soon as a message
+starting with one or more numbers is encountered \, the numbers are
+output as a list. There are many ways you could design a sequencer
+around this.;
+#X text 668 48 sequence automatically;
+#X text 670 79 set relative tempo;
+#X text 668 105 go to beginning (and stop);
+#X text 668 132 single-step forward;
+#X text 713 273 read a file;
+#X text 777 300 write one;
+#X text 552 404 This outlet gets a list of leading numbers for the
+next message \, for you to use in designing your own sequencer.;
+#X msg 586 274 read qlist.txt;
+#X msg 586 300 write /tmp/qlist.txt;
+#X text 21 493 see also:;
+#X obj 97 493 textfile;
+#X text 22 362 You can also record textual messages and save them to
+a file. Send "clear" to empty the qlist and "add" to add messages (terminated
+with semicolons.) The message \, "add2" adds a list of atoms without
+finishing with a semicolon in case you want to make variable-length
+messages.;
+#X msg 589 190 clear;
+#X msg 589 216 add 500 this is another message;
+#X msg 590 242 add2 that;
+#X text 666 187 empty the qlist;
+#X text 882 217 add a message to a qlist;
+#X text 683 240 add a message to a qlist but don't terminate it;
+#X text 653 341 This outlet gets a bang when you hit the end of the
+sequence. In the file "qlist.txt" the end is delayed 1000 milliseconds
+after the last message.;
+#X text 379 470 These receives are invoked in the file "qlist.txt"
+in this directory.;
+#X obj 71 13 qlist;
+#X text 132 15 - text-based sequencer;
+#X text 668 158 single-step forward SUPRESSING MESSAGE-SENDING;
+#X msg 591 161 next 1;
+#X text 921 558 updated for Pd version 0.35;
+#X connect 0 0 3 0;
+#X connect 0 1 9 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 0;
+#X connect 4 0 0 0;
+#X connect 5 0 7 0;
+#X connect 6 0 8 0;
+#X connect 10 0 0 0;
+#X connect 21 0 0 0;
+#X connect 22 0 0 0;
+#X connect 26 0 0 0;
+#X connect 27 0 0 0;
+#X connect 28 0 0 0;
+#X connect 37 0 0 0;
diff --git a/pd/doc/5.reference/qlist.txt b/pd/doc/5.reference/qlist.txt
new file mode 100644
index 00000000..790a8945
--- /dev/null
+++ b/pd/doc/5.reference/qlist.txt
@@ -0,0 +1,3 @@
+this text file is read by qlist.pd;
+1000 that should explain everything;
+1000;
diff --git a/pd/doc/5.reference/random.pd b/pd/doc/5.reference/random.pd
new file mode 100644
index 00000000..b792325c
--- /dev/null
+++ b/pd/doc/5.reference/random.pd
@@ -0,0 +1,19 @@
+#N canvas 0 0 630 421 12;
+#X msg 40 212 bang;
+#X obj 40 287 random 5;
+#X floatatom 83 261 0 0 0;
+#X floatatom 40 312 0 0 0;
+#X msg 50 236 seed 123;
+#X text 92 210 bang for output;
+#X text 132 236 message to set the seed;
+#X text 116 259 inlet to reset the range;
+#X text 119 286 argument to initialize the range;
+#X text 378 337 updated for Pd version 0.33;
+#X text 11 46 Random outputs pseudorandom integers from 0 to N-1 where N is the creation argument (5 in the example below.) You can specify a seed if you wish. Seeds are kept locally so that if two Randoms are seeded the same they will have the same output (or indeed you can seed the same one twice to repeat the output.);
+#X text 12 139 On the other hand \, if you don't supply a seed each instance of random gets its own seed. WARNING: nothing is known about the quality of teh pseudorandom number generator. It isn't any standard one!;
+#X obj 20 11 random;
+#X text 84 11 - pseudorandom integers;
+#X connect 0 0 1 0;
+#X connect 1 0 3 0;
+#X connect 2 0 1 1;
+#X connect 4 0 1 0;
diff --git a/pd/doc/5.reference/readsf~.pd b/pd/doc/5.reference/readsf~.pd
new file mode 100644
index 00000000..115f9db6
--- /dev/null
+++ b/pd/doc/5.reference/readsf~.pd
@@ -0,0 +1,48 @@
+#N canvas 38 26 630 390 10;
+#X msg 458 10 \; pd dsp 1;
+#X msg 47 186 1;
+#X msg 47 205 0;
+#X obj 403 305 print didit;
+#X obj 126 305 env~ 16384;
+#X floatatom 126 324;
+#X msg 48 227 print;
+#X obj 44 330 dac~;
+#X obj 191 305 env~ 16384;
+#X floatatom 191 324;
+#X obj 38 252 readsf~ 4 1e+06;
+#X obj 256 305 env~ 16384;
+#X floatatom 256 324;
+#X obj 323 306 env~ 16384;
+#X floatatom 323 325;
+#X msg 45 167 open ../sound/bell.aiff 0 200 4 2 b;
+#X obj 82 306 *~ 0.1;
+#X obj 40 306 *~ 0.1;
+#X text 40 7 READSF~ - read a soundfile;
+#X msg 45 146 open ../sound/bell.aiff;
+#X text 146 240 optional arguments: number of channels \; buffer size per channnel in bytes.;
+#X text 404 286 when the soundfile is done.;
+#X text 403 272 last outlet gives a "bang";
+#X text 471 369 Updated for version 0.29;
+#X text 271 146 Open takes a filename \, an onset in sample frames \, and \, as an override \, you may also supply a header size to skip \, a number of channels \, bytes per channel \, and endianness.;
+#X text 36 93 The wave \, aiff \, and nextstep formats are parsed automatically \, although only 2- 3- and 4- byte samples are accepted (4 bytes implies floating point and is only available in the nextstep format.);
+#X text 37 27 The readsf~ object reads a soundfile into its signal outputs. You must open the soundfile in advance (a couple of seconds before you'll need it) using the "open" message. The object immediately starts reading from the file \, but output will only appear after you send a "1" to start playback. A "0" stops it.;
+#X connect 1 0 10 0;
+#X connect 2 0 10 0;
+#X connect 4 0 5 0;
+#X connect 6 0 10 0;
+#X connect 8 0 9 0;
+#X connect 10 0 4 0;
+#X connect 10 0 17 0;
+#X connect 10 1 8 0;
+#X connect 10 1 16 0;
+#X connect 10 2 11 0;
+#X connect 10 2 16 0;
+#X connect 10 3 13 0;
+#X connect 10 3 17 0;
+#X connect 10 4 3 0;
+#X connect 11 0 12 0;
+#X connect 13 0 14 0;
+#X connect 15 0 10 0;
+#X connect 16 0 7 1;
+#X connect 17 0 7 0;
+#X connect 19 0 10 0;
diff --git a/pd/doc/5.reference/realtime.pd b/pd/doc/5.reference/realtime.pd
new file mode 100644
index 00000000..60fcffaa
--- /dev/null
+++ b/pd/doc/5.reference/realtime.pd
@@ -0,0 +1,15 @@
+#N canvas 156 202 565 269 12;
+#X msg 73 146 bang;
+#X msg 30 115 bang;
+#X floatatom 30 206 0 0 0;
+#X text 71 113 Click here to reset;
+#X text 27 232 Output is in milliseconds;
+#X text 114 147 Click here to get elapsed CPU time;
+#X obj 66 15 realtime;
+#X text 12 47 The realtime object measures elapsed real time \, as measured by your operating system.;
+#X obj 30 176 realtime;
+#X text 134 15 - ask OS for elapsed real time;
+#X text 302 244 updated for Pd version 0.33;
+#X connect 0 0 8 1;
+#X connect 1 0 8 0;
+#X connect 8 0 2 0;
diff --git a/pd/doc/5.reference/receive.pd b/pd/doc/5.reference/receive.pd
new file mode 100644
index 00000000..17bb08cb
--- /dev/null
+++ b/pd/doc/5.reference/receive.pd
@@ -0,0 +1,26 @@
+#N canvas 257 45 511 351 12;
+#X text 278 321 updated for Pd version 0.32;
+#X floatatom 36 55 5 0 0;
+#X floatatom 152 58 5 0 0;
+#X floatatom 272 57 5 0 0;
+#X floatatom 38 134 5 0 0;
+#X floatatom 171 136 5 0 0;
+#X floatatom 305 134 5 0 0;
+#X text 62 321 abbreviation:;
+#X obj 36 80 send help-rcv1;
+#X obj 152 81 send help-rcv1;
+#X obj 271 81 send help-rcv2;
+#X obj 38 110 receive help-rcv1;
+#X obj 171 110 receive help-rcv2;
+#X obj 305 110 receive help-rcv2;
+#X text 31 161 "Receive" outputs messages sent via "send." Sends and receives are named to tell them whom to connect to. They work across windows too. Also \, you can use message boxes as shown:;
+#X msg 84 233 \; help-rcv1 34 \; help-rcv2 67;
+#X obj 161 320 r;
+#X obj 21 10 receive;
+#X text 79 10 -- receive messages without patch cords;
+#X connect 1 0 8 0;
+#X connect 2 0 9 0;
+#X connect 3 0 10 0;
+#X connect 11 0 4 0;
+#X connect 12 0 5 0;
+#X connect 13 0 6 0;
diff --git a/pd/doc/5.reference/route.pd b/pd/doc/5.reference/route.pd
new file mode 100644
index 00000000..224fb0ea
--- /dev/null
+++ b/pd/doc/5.reference/route.pd
@@ -0,0 +1,80 @@
+#N canvas 0 0 815 537 12;
+#X obj 183 213 print x1;
+#X obj 261 213 print x2;
+#X obj 339 213 print x3;
+#X obj 422 213 print x4;
+#X obj 183 185 route 23 54 1;
+#X msg 183 155 234 345 456;
+#X msg 308 155 23 34 45;
+#X msg 414 155 54 43;
+#X msg 485 155 1 foo bar;
+#X msg 254 247 impeach ringo starr;
+#X obj 191 275 route big apple;
+#X msg 435 248 apple pie;
+#X msg 191 247 1 2 3;
+#X msg 523 248 big apple pie;
+#X msg 578 155 walk the dog;
+#X text 45 33 Route checks the first element of a message against each
+of its arguments \, which may be numbers or symbols (but not a mixture
+of the two.);
+#X text 44 85 If a match is found \, the rest of the message appears
+on the corresponding outlet. If no match \, the message is repeated
+to the last "rejection" outlet. The number of outlets is the number
+of arguments plus one.;
+#X text 19 185 numeric arguments:;
+#X text 17 275 symbolic arguments:;
+#X obj 157 489 print z1;
+#X obj 233 489 print z2;
+#X msg 124 424 bang;
+#X msg 170 424 list;
+#X msg 213 424 5;
+#X msg 251 424 float 5;
+#X msg 320 424 list 5;
+#X msg 385 424 symbol pie;
+#X msg 560 424 pie;
+#X msg 483 424 list pie;
+#X msg 70 424 1 2 3;
+#X obj 157 461 route list float symbol bang;
+#X obj 310 489 print z3;
+#X obj 387 489 print z4;
+#X obj 461 489 print z5;
+#X obj 191 305 print y1;
+#X obj 269 305 print y2;
+#X obj 347 305 print y3;
+#X text 76 344 To avoid confusion between \, say \, the number 5 and
+the list contining only the number 5 \, both messages match "float"
+\, and ditto for symbols. An empty list matches "bang". In Pd these
+are all considered special cases of lists.;
+#X text 545 506 updated for Pd version 0.35;
+#X text 97 9 - route messages according to their first element;
+#X obj 43 8 route;
+#X connect 4 0 0 0;
+#X connect 4 1 1 0;
+#X connect 4 2 2 0;
+#X connect 4 3 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 4 0;
+#X connect 7 0 4 0;
+#X connect 8 0 4 0;
+#X connect 9 0 10 0;
+#X connect 10 0 34 0;
+#X connect 10 1 35 0;
+#X connect 10 2 36 0;
+#X connect 11 0 10 0;
+#X connect 12 0 10 0;
+#X connect 13 0 10 0;
+#X connect 14 0 4 0;
+#X connect 21 0 30 0;
+#X connect 22 0 30 0;
+#X connect 23 0 30 0;
+#X connect 24 0 30 0;
+#X connect 25 0 30 0;
+#X connect 26 0 30 0;
+#X connect 27 0 30 0;
+#X connect 28 0 30 0;
+#X connect 29 0 30 0;
+#X connect 30 0 19 0;
+#X connect 30 1 20 0;
+#X connect 30 2 31 0;
+#X connect 30 3 32 0;
+#X connect 30 4 33 0;
diff --git a/pd/doc/5.reference/rsqrt~.pd b/pd/doc/5.reference/rsqrt~.pd
new file mode 100644
index 00000000..fb0bc350
--- /dev/null
+++ b/pd/doc/5.reference/rsqrt~.pd
@@ -0,0 +1,32 @@
+#N canvas 183 264 685 375 12;
+#X obj 68 211 metro 500;
+#X obj 68 186 r metro;
+#X msg 575 106 \; metro 0;
+#X msg 574 48 \; pd dsp 1 \; metro 1;
+#X floatatom 52 112 0 0 0;
+#X floatatom 52 268 0 0 0;
+#X text 419 349 updated for Pd version 0.33;
+#X obj 574 21 loadbang;
+#X obj 52 235 snapshot~;
+#X floatatom 51 351 9 0 0;
+#X obj 51 295 t f f;
+#X obj 51 322 *;
+#X obj 52 138 sig~;
+#X obj 36 16 rsqrt~;
+#X text 105 14 - signal reciprocal square root;
+#X text 18 45 rsqrt~ takes the approximate reciprocal square root of
+the incoming signal \, using a fast \, approximate algorithm which
+is probably accurate to about 120 dB (20 bits).;
+#X obj 52 162 rsqrt~;
+#X connect 0 0 8 0;
+#X connect 1 0 0 0;
+#X connect 1 0 0 0;
+#X connect 4 0 12 0;
+#X connect 5 0 10 0;
+#X connect 7 0 3 0;
+#X connect 8 0 5 0;
+#X connect 10 0 11 0;
+#X connect 10 1 11 1;
+#X connect 11 0 9 0;
+#X connect 12 0 16 0;
+#X connect 16 0 8 0;
diff --git a/pd/doc/5.reference/samphold~.pd b/pd/doc/5.reference/samphold~.pd
new file mode 100644
index 00000000..1a58bd02
--- /dev/null
+++ b/pd/doc/5.reference/samphold~.pd
@@ -0,0 +1,34 @@
+#N canvas 121 54 554 287 10;
+#X obj 32 238 snapshot~;
+#X floatatom 32 257;
+#X obj 41 219 metro 100;
+#X obj 41 197 r start;
+#X msg 387 21 \; pd dsp 1 \; start bang;
+#X text 392 270 updated for version 0.29;
+#X text 454 30 Click to start;
+#X text 447 78 Click to stop;
+#X msg 388 65 \; pd dsp 0 \; start 0;
+#X obj 19 7 samphold~;
+#X text 96 6 - sample and hold unit;
+#X obj 32 170 samphold~;
+#X text 10 26 The samphold~ object samples its left input whenever its right input decreases in value (as a phasor~ does each period \, for example.) Both inputs are audio signals.;
+#X obj 67 129 sig~;
+#X obj 101 148 sig~;
+#X floatatom 67 109;
+#X floatatom 101 127;
+#X msg 32 66 set 34;
+#X msg 38 87 reset;
+#X text 81 65 set output to a number;
+#X text 81 86 force the next sample;
+#X text 97 109 sample signal;
+#X text 135 127 control signal;
+#X connect 0 0 1 0;
+#X connect 2 0 0 0;
+#X connect 3 0 2 0;
+#X connect 11 0 0 0;
+#X connect 13 0 11 0;
+#X connect 14 0 11 1;
+#X connect 15 0 13 0;
+#X connect 16 0 14 0;
+#X connect 17 0 11 0;
+#X connect 18 0 11 0;
diff --git a/pd/doc/5.reference/savepanel.pd b/pd/doc/5.reference/savepanel.pd
new file mode 100644
index 00000000..b5d7e7a6
--- /dev/null
+++ b/pd/doc/5.reference/savepanel.pd
@@ -0,0 +1,12 @@
+#N canvas 9 118 567 234 12;
+#X msg 102 92 bang;
+#X obj 102 145 print;
+#X text 295 199 updated for Pd version 0.24;
+#X text 28 192 see also:;
+#X text 16 35 When Savepanel gets a "bang" a "Save As" file browser appears on the screen \, If you choose a filename \, it appears on the outlet.;
+#X obj 102 120 savepanel;
+#X obj 115 191 openpanel;
+#X obj 19 7 savepanel;
+#X text 104 6 - query you for the name of a file to create;
+#X connect 0 0 5 0;
+#X connect 5 0 1 0;
diff --git a/pd/doc/5.reference/select.pd b/pd/doc/5.reference/select.pd
new file mode 100644
index 00000000..6bc17ad7
--- /dev/null
+++ b/pd/doc/5.reference/select.pd
@@ -0,0 +1,73 @@
+#N canvas 47 29 618 662 12;
+#X floatatom 22 332 0 0 0;
+#X msg 156 120 6;
+#X msg 119 120 234;
+#X floatatom 119 150 0 0 0;
+#X msg 121 301 1;
+#X msg 89 301 54;
+#X obj 22 392 print x1;
+#X obj 100 391 print x2;
+#X msg 58 301 23;
+#X msg 22 302 234;
+#X msg 65 120 6;
+#X obj 28 180 select 6;
+#X msg 28 120 234;
+#X obj 28 210 print x1;
+#X obj 107 211 print x2;
+#X obj 177 391 print x3;
+#X obj 255 392 print x4;
+#X floatatom 28 150 0 0 0;
+#X obj 22 362 select 23 54 1;
+#X text 45 609 abbreviation:;
+#X obj 169 610 sel;
+#X text 20 37 In its simplest form shown below \, Select checks its input agains the constant "6". If they match \, the first outlet gives "bang" and otherwise the input is copied to the second outlet. If Select is used with a single argument \, a second inlet allows you to change the test value.;
+#X text 22 239 You can give several arguments. You get an outlet for each test value and finally an outlet for values which match none of them. In this case you don't get inlets to change the test values:;
+#X obj 32 566 print x1;
+#X obj 114 567 print x2;
+#X msg 34 451 symbol cort;
+#X msg 46 476 symbol zack;
+#X msg 178 476 symbol cort;
+#X msg 184 501 symbol zack;
+#X obj 34 539 select cort;
+#X msg 308 462 symbol cort;
+#X msg 415 462 symbol zack;
+#X obj 308 551 print x1;
+#X obj 385 551 print x2;
+#X obj 308 521 select cort zack;
+#X obj 462 551 print x3;
+#X msg 413 487 symbol bill;
+#X text 24 426 Select can also be used to sort symbols:;
+#X text 83 637 see also:;
+#X obj 175 639 route;
+#X obj 32 10 select;
+#X text 92 10 - compare numbers or symbols;
+#X text 370 629 updated for Pd version 0.33;
+#X connect 0 0 18 0;
+#X connect 1 0 3 0;
+#X connect 2 0 3 0;
+#X connect 3 0 11 1;
+#X connect 4 0 0 0;
+#X connect 5 0 0 0;
+#X connect 8 0 0 0;
+#X connect 9 0 0 0;
+#X connect 10 0 17 0;
+#X connect 11 0 13 0;
+#X connect 11 1 14 0;
+#X connect 12 0 17 0;
+#X connect 17 0 11 0;
+#X connect 18 0 6 0;
+#X connect 18 1 7 0;
+#X connect 18 2 15 0;
+#X connect 18 3 16 0;
+#X connect 25 0 29 0;
+#X connect 26 0 29 0;
+#X connect 27 0 29 1;
+#X connect 28 0 29 1;
+#X connect 29 0 23 0;
+#X connect 29 1 24 0;
+#X connect 30 0 34 0;
+#X connect 31 0 34 0;
+#X connect 34 0 32 0;
+#X connect 34 1 33 0;
+#X connect 34 2 35 0;
+#X connect 36 0 34 0;
diff --git a/pd/doc/5.reference/send.pd b/pd/doc/5.reference/send.pd
new file mode 100644
index 00000000..f8d44a85
--- /dev/null
+++ b/pd/doc/5.reference/send.pd
@@ -0,0 +1,26 @@
+#N canvas 257 45 511 351 12;
+#X text 278 321 updated for Pd version 0.32;
+#X obj 21 10 send;
+#X text 60 11 -- send messages without patch cords;
+#X obj 36 80 send help-send1;
+#X obj 152 81 send help-send1;
+#X obj 271 81 send help-send2;
+#X obj 38 110 receive help-send1;
+#X obj 171 110 receive help-send2;
+#X obj 305 110 receive help-send2;
+#X floatatom 36 55 5 0 0;
+#X floatatom 152 58 5 0 0;
+#X floatatom 272 57 5 0 0;
+#X floatatom 38 134 5 0 0;
+#X floatatom 171 136 5 0 0;
+#X floatatom 305 134 5 0 0;
+#X obj 161 320 s;
+#X text 62 321 abbreviation:;
+#X text 31 161 "Send" sends messages to "receive" objects. Sends and receives are named to tell them whom to connect to. They work across windows too. Also \, you can use message boxes as shown:;
+#X msg 84 233 \; help-send1 34 \; help-send2 67;
+#X connect 6 0 12 0;
+#X connect 7 0 13 0;
+#X connect 8 0 14 0;
+#X connect 9 0 3 0;
+#X connect 10 0 4 0;
+#X connect 11 0 5 0;
diff --git a/pd/doc/5.reference/send~.pd b/pd/doc/5.reference/send~.pd
new file mode 100644
index 00000000..5c9db395
--- /dev/null
+++ b/pd/doc/5.reference/send~.pd
@@ -0,0 +1,32 @@
+#N canvas 31 28 678 406 12;
+#X floatatom 344 238 0 0 0;
+#X obj 344 189 receive~ signal1;
+#X obj 17 215 send~ signal1;
+#X obj 17 192 sig~ 50;
+#X obj 344 213 snapshot~;
+#X obj 42 22 send~;
+#X obj 94 23 receive~;
+#X text 178 23 - one-to-many nonlocal signal connections;
+#X obj 507 133 loadbang;
+#X obj 507 194 metro 200;
+#X msg 517 155 \; pd dsp 1;
+#X floatatom 18 168 4 0 0;
+#X text 48 51 A send~ object copies its input to a local buffer which all receive~ objects of the same name read from. They may be in different windows or even different patches. Any number of receives may be associated with one send~ but it is an error to have two send~s of the same name.;
+#X obj 179 344 tabreceive~;
+#X text 405 368 updated for Pd version 0.33.;
+#X obj 148 187 sig~ 25;
+#X obj 148 215 send~ signal2;
+#X msg 355 139 set signal2;
+#X msg 356 161 set signal1;
+#X text 34 287 Send~/Receive~ only work for the default block size (64) \; for FFT applications see also:;
+#X text 35 262 Receive~ takes "set" messages to switch between send~s.;
+#X connect 1 0 4 0;
+#X connect 3 0 2 0;
+#X connect 4 0 0 0;
+#X connect 8 0 9 0;
+#X connect 8 0 10 0;
+#X connect 9 0 4 0;
+#X connect 11 0 3 0;
+#X connect 15 0 16 0;
+#X connect 17 0 1 0;
+#X connect 18 0 1 0;
diff --git a/pd/doc/5.reference/set.pd b/pd/doc/5.reference/set.pd
new file mode 100644
index 00000000..227b29b4
--- /dev/null
+++ b/pd/doc/5.reference/set.pd
@@ -0,0 +1,45 @@
+#N struct help-set-template1 float x float y;
+#N canvas 300 3 583 365 12;
+#X text 19 263 see also:;
+#X obj 137 308 template;
+#X obj 112 284 append;
+#X obj 170 284 getsize;
+#X obj 237 284 setsize;
+#X obj 215 308 element;
+#X obj 11 308 sublist;
+#X obj 78 308 scalar;
+#X msg 210 155 next;
+#X obj 21 10 get;
+#X floatatom 19 173 5 0 0;
+#X floatatom 108 181 5 0 0;
+#X obj 196 180 pointer;
+#X text 273 113 output first scalar in list;
+#X text 256 155 output next item;
+#X text 262 204 First argument selects template.;
+#X text 263 219 Remaining args are names of fields.;
+#X obj 11 283 pointer;
+#X msg 196 131 traverse pd-help-set-data \, next;
+#N canvas 0 0 276 122 help-set-data 1;
+#X scalar help-set-template1 39 23 \;;
+#X scalar help-set-template1 99 73 \;;
+#X restore 377 244 pd help-set-data;
+#N canvas 164 72 425 146 help-set-template1 0;
+#X obj 41 87 filledpolygon 9 0 1 0 0 20 0 20 30 0 30;
+#X obj 60 21 template float x float y;
+#X restore 377 266 pd help-set-template1;
+#X text 86 10 -- set values in a scalar;
+#X obj 19 204 set help-set-template1 x y;
+#X text 18 155 x value;
+#X text 106 162 y value;
+#X obj 79 283 get;
+#X text 19 32 "Set" takes a pointer to a scalar in its rightmost inlet
+\; the remaining inlets set numeric values of fields. Only the leftmost
+inlet is "hot". You can't "set" arrays or sublists. Instead \, you
+can get pointers into them using "element" and "sublist" (probably
+not working yet) and set individual items.;
+#X text 336 342 updated for Pd version 0.35;
+#X connect 8 0 12 0;
+#X connect 10 0 22 0;
+#X connect 11 0 22 1;
+#X connect 12 0 22 2;
+#X connect 18 0 12 0;
diff --git a/pd/doc/5.reference/setsize.pd b/pd/doc/5.reference/setsize.pd
new file mode 100644
index 00000000..ce68f5fc
--- /dev/null
+++ b/pd/doc/5.reference/setsize.pd
@@ -0,0 +1,54 @@
+#N struct help-setsize-template float x float y array array1 help-setsize-array1-template
+;
+#N struct help-setsize-array1-template float y;
+#N canvas 331 45 678 459 12;
+#X text 31 359 see also:;
+#X obj 28 379 template;
+#N canvas 393 10 491 261 help-setsize-template 0;
+#X obj 27 174 filledpolygon 509 509 0 -10 -10 10 -10 10 10 -10 10;
+#X obj 24 16 template float x float y array array1 help-setsize-array1-template
+;
+#X obj 27 76 plot array1 500 1 10 15 10;
+#X restore 364 261 pd help-setsize-template;
+#N canvas 0 0 295 165 help-setsize-data 1;
+#X scalar help-setsize-template 31 23 \; 0 \; 10 \; 0 \; 10 \; 20 \;
+10 \; 20 \; 70 \; 10 \; \;;
+#X restore 363 240 pd help-setsize-data;
+#N canvas 196 292 365 134 help-setsize-array1-template 0;
+#X obj 30 71 filledpolygon 0 0 0 -5 0 0 5 5 0 0 -5;
+#X obj 32 27 template float y;
+#X restore 363 284 pd help-setsize-array1-template;
+#X obj 107 379 pointer;
+#X obj 242 379 setsize;
+#X obj 272 186 pointer;
+#X msg 272 162 traverse pd-help-setsize-data \, next;
+#X floatatom 25 189 5 0 0;
+#X text 359 210 arguments: template \, field name;
+#X obj 25 213 setsize help-setsize-template array1;
+#X text 115 183 inlet for pointer;
+#X obj 36 11 setsize;
+#X obj 174 379 element;
+#X text 31 156 number sets;
+#X text 30 170 size;
+#X text 99 12 -- resize an array;
+#X text 25 34 "setsize" takes a pointer to a scalar at left and a number
+at right. Its creation arguments specify the template of the pointer
+and the name of an array field. Sending a number then sets the number
+of elements of the array.;
+#X text 24 93 The smallest possible size is one. If the array is resized
+downward the extra data are lost. If resized upward \, the new elements
+are initialized to default values.;
+#X msg 566 335 bang;
+#X text 297 333 click to reload from file -->;
+#X text 274 143 click here first;
+#N canvas 460 63 435 172 readit 1;
+#X msg 66 65 \; pd-help-setsize-data read setsize.txt;
+#X obj 107 18 inlet;
+#X msg 62 123 \; pd-help-setsize-data write setsize.txt;
+#X connect 1 0 0 0;
+#X restore 566 361 pd readit;
+#X text 416 395 updated for Pd version 0.35;
+#X connect 7 0 11 1;
+#X connect 8 0 7 0;
+#X connect 9 0 11 0;
+#X connect 20 0 23 0;
diff --git a/pd/doc/5.reference/setsize.txt b/pd/doc/5.reference/setsize.txt
new file mode 100644
index 00000000..d238fb59
--- /dev/null
+++ b/pd/doc/5.reference/setsize.txt
@@ -0,0 +1,21 @@
+data;
+template help-setsize-template;
+float x;
+float y;
+array array1 help-setsize-array1-template;
+;
+template help-setsize-array1-template;
+float y;
+;
+;
+help-setsize-template 31 23;
+0;
+10;
+0;
+10;
+20;
+10;
+20;
+70;
+10;
+;
diff --git a/pd/doc/5.reference/sigbinops.pd b/pd/doc/5.reference/sigbinops.pd
new file mode 100644
index 00000000..b461c846
--- /dev/null
+++ b/pd/doc/5.reference/sigbinops.pd
@@ -0,0 +1,60 @@
+#N canvas 18 67 1086 595 10;
+#X obj 8 251 +~;
+#X obj 115 249 -~;
+#X obj 222 249 *~;
+#X obj 327 251 /~;
+#X obj 38 17 +~;
+#X obj 73 17 -~;
+#X obj 106 16 *~;
+#X obj 140 16 /~;
+#X graph graph1 0 -1 100 1 678 446 1078 146;
+#X array array1 100 float;
+#X pop;
+#X obj 327 293 tabwrite~ array1;
+#X obj 8 293 tabwrite~ array1;
+#X obj 115 293 tabwrite~ array1;
+#X obj 222 293 tabwrite~ array1;
+#X text 266 14 -- operators on audio signals;
+#X obj 8 160 osc~ 440;
+#X obj 480 157 osc~ 675;
+#X msg 17 271 bang;
+#X obj 173 15 max~;
+#X obj 207 15 min~;
+#X text 487 458 modified for Pd version 0.27;
+#X obj 536 293 tabwrite~ array1;
+#X obj 431 293 tabwrite~ array1;
+#X obj 431 249 max~;
+#X obj 536 251 min~;
+#X msg 127 272 bang;
+#X msg 233 271 bang;
+#X msg 343 272 bang;
+#X msg 443 271 bang;
+#X msg 553 272 bang;
+#X text 52 332 The binary signal operators can be configured to combine two signals as above \, or \, if you give a numeric argument \, audio signals are combined with scalars:;
+#X obj 204 377 +~ 5;
+#X text 60 406 The right inlet takes audio signals or numbers depending on whether the argument is present or not.;
+#X msg 68 71 \; pd dsp 1;
+#X connect 0 0 10 0;
+#X connect 1 0 11 0;
+#X connect 2 0 12 0;
+#X connect 3 0 9 0;
+#X connect 14 0 0 0;
+#X connect 14 0 1 0;
+#X connect 14 0 3 0;
+#X connect 14 0 2 0;
+#X connect 14 0 22 0;
+#X connect 14 0 23 0;
+#X connect 15 0 3 1;
+#X connect 15 0 2 1;
+#X connect 15 0 1 1;
+#X connect 15 0 0 1;
+#X connect 15 0 22 1;
+#X connect 15 0 23 1;
+#X connect 16 0 10 0;
+#X connect 22 0 21 0;
+#X connect 23 0 20 0;
+#X connect 24 0 11 0;
+#X connect 25 0 12 0;
+#X connect 26 0 9 0;
+#X connect 27 0 21 0;
+#X connect 28 0 20 0;
diff --git a/pd/doc/5.reference/sig~.pd b/pd/doc/5.reference/sig~.pd
new file mode 100644
index 00000000..72781487
--- /dev/null
+++ b/pd/doc/5.reference/sig~.pd
@@ -0,0 +1,20 @@
+#N canvas 132 175 547 284 12;
+#X obj 109 221 snapshot~;
+#X floatatom 110 246 0 0 0;
+#X obj 78 12 sig~;
+#X obj 24 133 sig~;
+#X floatatom 24 108 0 0 0;
+#X text 114 14 - convert numbers to audio signal;
+#X text 11 53 In this example \, the sig~ object converts numbers to an audio signal \, which the snapshot~ converts back again.;
+#X text 64 108 <-- Scroll to set value;
+#X obj 109 131 loadbang;
+#X msg 118 155 \; pd dsp 1;
+#X obj 109 195 metro 200;
+#X text 291 249 updated for Pd version 0.33;
+#X connect 0 0 1 0;
+#X connect 0 0 1 0;
+#X connect 3 0 0 0;
+#X connect 4 0 3 0;
+#X connect 8 0 9 0;
+#X connect 8 0 10 0;
+#X connect 10 0 0 0;
diff --git a/pd/doc/5.reference/snapshot~.pd b/pd/doc/5.reference/snapshot~.pd
new file mode 100644
index 00000000..244a2a7c
--- /dev/null
+++ b/pd/doc/5.reference/snapshot~.pd
@@ -0,0 +1,28 @@
+#N canvas 40 55 704 399 12;
+#X obj 205 266 snapshot~;
+#X floatatom 205 292 0 0 0;
+#X obj 105 274 snapshot~;
+#X floatatom 105 318 0 0 0;
+#X obj 74 14 snapshot~;
+#X msg 205 236 bang;
+#X text 201 313 This output updates each time bang is clicked above.;
+#X text 154 14 - convert a signal to a number on demand;
+#X text 9 46 The snapshot~ object takes a signal and converts it to a control value whenever it receives a bang in its left outlet. This object is particularly useful for monitoring outputs.;
+#X text 459 375 updated for Pd version 0.33;
+#X msg 19 212 \; pd dsp 1;
+#X obj 12 187 loadbang;
+#X obj 12 251 metro 200;
+#X text 104 334 This output updates every 200 milliseconds.;
+#X obj 105 214 osc~ 0.1;
+#X text 110 196 0.1 Hz cosine;
+#X text 7 111 In the example below \, the first snapshot~ object prints out the values of a low frequency cosine wave every 200 milliseconds. The second snapshot~ object prints the output value when the bang button above it is clicked.;
+#X connect 0 0 1 0;
+#X connect 0 0 1 0;
+#X connect 2 0 3 0;
+#X connect 2 0 3 0;
+#X connect 5 0 0 0;
+#X connect 11 0 10 0;
+#X connect 11 0 12 0;
+#X connect 12 0 2 0;
+#X connect 14 0 2 0;
+#X connect 14 0 0 0;
diff --git a/pd/doc/5.reference/soundfiler.pd b/pd/doc/5.reference/soundfiler.pd
new file mode 100644
index 00000000..c6384664
--- /dev/null
+++ b/pd/doc/5.reference/soundfiler.pd
@@ -0,0 +1,66 @@
+#N canvas 82 31 926 613 10;
+#X graph graph1 0 -1 77971 1 71 453 371 353;
+#X array array1 77971 float 0;
+#X pop;
+#X graph graph1 0 -1 77971 1 71 575 371 475;
+#X array array2 77971 float 0;
+#X pop;
+#X obj 12 293 soundfiler;
+#X msg 532 476 \; array1 resize 1000 \; array2 resize 1000 \;;
+#X msg 18 221 write -aiff /tmp/foo1 array2;
+#X msg 19 132 read ../sound/bell.aiff array2;
+#X msg 18 179 read -raw 128 2 2 b ../sound/bell.aiff array1 array2
+;
+#X msg 532 522 \; array1 resize 160000 \; array2 resize 160000;
+#X msg 530 431 \; array1 const 0 \; array2 const 0;
+#X text 26 10 SOUNDFILER - read and write soundfiles to arrays;
+#X text 18 31 The soundfiler object reads and writes floating point
+arrays to binary soundfiles which may contain 2 or 3 byte fixed point
+or 4 byte floating point samples in wave \, aiff \, or next formats
+(only next supports floating point \, though.). The number of channels
+of the soundfile need not match the number of arrays given (extras
+are dropped and unsupplied channels are zeroed out.);
+#X text 475 9 When reading you can leave soundfiler to figure out which
+of the three known soundfile formats the file belongs to or override
+all header information using the "-raw" flag.;
+#X text 479 51 Flags for reading:;
+#X text 498 73 -skip <sample frames to skip in file>;
+#X text 498 90 -nframes <maximum number of sample frames to read>;
+#X text 499 148 -raw <headersize> <channels> <bytespersample> <endianness>
+;
+#X text 518 166 This causes all header information to be ignored. Endianness
+is "l" ("little") for Intel machines or "b" ("big") for Macintoshes
+and SGIs. You can give "n" (natural) to take the byte order your machine
+prefers.;
+#X text 499 111 -resize;
+#X text 499 127 -maxsize <maximum number of samples we can resize to>
+;
+#X text 488 225 Flags for writing:;
+#X text 503 246 -wave \, -nextstep \, -aiff;
+#X text 504 265 -big \, -little (nextstep only!);
+#X text 503 287 -skip <number of sample frames to skip in array>;
+#X text 504 309 -nframes <maximum number to write>;
+#X text 505 353 -normalize;
+#X text 504 331 -bytes <2 \, 3 \, or 4>;
+#X floatatom 12 317 0 0 0;
+#X msg 16 155 read -resize ../sound/bell.aiff array2;
+#X text 707 581 updated for Pd version 0.29;
+#X msg 18 268 write -nextstep -bytes 4 /tmp/foo3 array1 array2;
+#X msg 17 245 write -wave -nframes 10000 /tmp/foo2 array2;
+#X text 257 130 read a file;
+#X text 302 155 ...optionally resize;
+#X text 230 196 ...or even overriding everything;
+#X text 235 221 write a file;
+#X text 376 270 write stereo;
+#X text 488 384 The number of channels is limited to 64;
+#X text 790 429 see also:;
+#X obj 784 452 tabwrite~;
+#X obj 785 476 tabread4~;
+#X obj 784 503 tabplay~;
+#X connect 2 0 26 0;
+#X connect 4 0 2 0;
+#X connect 5 0 2 0;
+#X connect 6 0 2 0;
+#X connect 27 0 2 0;
+#X connect 29 0 2 0;
+#X connect 30 0 2 0;
diff --git a/pd/doc/5.reference/spigot.pd b/pd/doc/5.reference/spigot.pd
new file mode 100644
index 00000000..3daf9157
--- /dev/null
+++ b/pd/doc/5.reference/spigot.pd
@@ -0,0 +1,21 @@
+#N canvas 349 223 586 335 12;
+#X msg 25 157 0.5 1000;
+#X floatatom 74 242 1 0 0;
+#X obj 25 267 spigot;
+#X obj 25 300 print;
+#X msg 38 210 walk the cat;
+#X msg 31 182 bang;
+#X obj 35 11 spigot;
+#X text 100 12 - pass or block messages;
+#X text 99 242 control: nonzero to pass messages \, zero to stop them
+;
+#X text 333 303 updated for Pd version 0.33;
+#X text 35 63 Spigot passes messages from its left inlet to its outlet
+\, as long as a nonzero number is sent to its right inlet. When its
+right inlet gets zero \, incoming messages are "blocked \, " i.e. \,
+ignored.;
+#X connect 0 0 2 0;
+#X connect 1 0 2 1;
+#X connect 2 0 3 0;
+#X connect 4 0 2 0;
+#X connect 5 0 2 0;
diff --git a/pd/doc/5.reference/sqrt~.pd b/pd/doc/5.reference/sqrt~.pd
new file mode 100644
index 00000000..b7b8e1a4
--- /dev/null
+++ b/pd/doc/5.reference/sqrt~.pd
@@ -0,0 +1,32 @@
+#N canvas 182 132 778 399 12;
+#X obj 71 201 metro 500;
+#X obj 71 176 r metro;
+#X msg 575 106 \; metro 0;
+#X msg 574 48 \; pd dsp 1 \; metro 1;
+#X floatatom 55 102 0 0 0;
+#X floatatom 55 258 0 0 0;
+#X text 470 371 updated for Pd version 0.33;
+#X obj 574 21 loadbang;
+#X obj 36 16 sqrt~;
+#X text 88 18 - signal square root;
+#X obj 55 152 sqrt~;
+#X obj 55 225 snapshot~;
+#X floatatom 54 341 9 0 0;
+#X obj 54 285 t f f;
+#X obj 54 312 *;
+#X obj 55 128 sig~;
+#X text 18 45 sqrt~ takes the approximate square root of the incoming
+signal \, using a fast \, approximate algorithm which is probably accurate
+to about 120 dB (20 bits).;
+#X connect 0 0 11 0;
+#X connect 1 0 0 0;
+#X connect 1 0 0 0;
+#X connect 4 0 15 0;
+#X connect 5 0 13 0;
+#X connect 7 0 3 0;
+#X connect 10 0 11 0;
+#X connect 11 0 5 0;
+#X connect 13 0 14 0;
+#X connect 13 1 14 1;
+#X connect 14 0 12 0;
+#X connect 15 0 10 0;
diff --git a/pd/doc/5.reference/stripnote.pd b/pd/doc/5.reference/stripnote.pd
new file mode 100644
index 00000000..80a8cecb
--- /dev/null
+++ b/pd/doc/5.reference/stripnote.pd
@@ -0,0 +1,16 @@
+#N canvas 53 36 458 251 10;
+#X msg 39 100 23 0;
+#X obj 39 175 print x1;
+#X obj 96 175 print x2;
+#X obj 39 139 stripnote;
+#X msg 79 100 34.5 67.8;
+#X obj 65 214 makenote;
+#X text 83 12 - send note-on messages and schedule note-off for later;
+#X text 283 220 updated for Pd version 0.28;
+#X text 10 214 see also;
+#X obj 23 10 stripnote;
+#X text 17 44 Stripnote takes note-off (zero-velocity) messages out of a stream of MIDI-style note message and passes the others through unchanged.;
+#X connect 0 0 3 0;
+#X connect 3 0 1 0;
+#X connect 3 1 2 0;
+#X connect 4 0 3 0;
diff --git a/pd/doc/5.reference/struct.pd b/pd/doc/5.reference/struct.pd
new file mode 100644
index 00000000..a18fa6e9
--- /dev/null
+++ b/pd/doc/5.reference/struct.pd
@@ -0,0 +1,26 @@
+#N canvas 343 45 557 321 12;
+#X text 88 11 -- declare the fields in a data structure.;
+#N canvas 345 476 638 171 help-template1 0;
+#X obj 60 21 struct struct-1 float x float y symbol dog array weasel
+struct-2;
+#X text 40 76 In this example \, the "struct-1" structure is defined
+in which "x" and "y" are "floats" \, i.e. \, numbers \, but "dog" is
+a symbol and "weasel" is an array of objects of structure "struct-2".
+;
+#X restore 324 156 pd help-template1;
+#N canvas 10 274 588 157 help-template2 0;
+#X text 28 95 Here is one which specifies only the floating point "y"
+\; it's used for the elements of the array shown in the other template.
+;
+#X obj 60 21 struct struct-2 float y;
+#X restore 324 183 pd help-template2;
+#X obj 36 215 drawpolygon;
+#X text 36 195 see also:;
+#X obj 141 215 drawnumber;
+#X obj 236 216 plot;
+#X text 281 290 updated for Pd version 0.35;
+#X obj 21 10 struct;
+#X text 16 49 There should be one "struct" object in each Pd window
+you are using as a data structure template. The arguments specify the
+types and names of the fields \; and for array fields \, a third argument
+specifies the template that the array elements should belong to.;
diff --git a/pd/doc/5.reference/sublist.pd b/pd/doc/5.reference/sublist.pd
new file mode 100644
index 00000000..a3067d5a
--- /dev/null
+++ b/pd/doc/5.reference/sublist.pd
@@ -0,0 +1,10 @@
+#N canvas 252 0 559 226 12;
+#X text 311 181 updated for Pd version 0.32;
+#X obj 21 10 sublist;
+#X text 86 10 -- get a list from a field of a scalar;
+#X text 31 37 Don't try this yet -- it's untested.;
+#X text 36 89 "sublist" will take as creation arguments a template
+name and a field name \; its one input takes a pointer. If you send
+a pointer (which should agree with the template name) \, "sublist"
+will output the field (which should be of type "list".) The output
+is in fact a pointer to the head of the sublist.;
diff --git a/pd/doc/5.reference/swap.pd b/pd/doc/5.reference/swap.pd
new file mode 100644
index 00000000..987a5844
--- /dev/null
+++ b/pd/doc/5.reference/swap.pd
@@ -0,0 +1,20 @@
+#N canvas 376 130 488 326 12;
+#X msg 67 124 bang;
+#X floatatom 67 252;
+#X floatatom 79 154;
+#X floatatom 118 194;
+#X obj 66 15 swap;
+#X text 114 16 - SWAP TWO NUMBERS \, RESPECTING RIGHT-TO-LEFT ORDER;
+#X text 284 309 updated for Pd version 0.27;
+#X text 12 42 The swap object stores numbers from its left inlet to output on its right inlet -- after repeating its right hand input out the left.;
+#X text 112 123 outputs 2 stored values;
+#X obj 67 226 swap 6.5;
+#X text 110 154 sets second value and outputs both;
+#X text 150 195 sets first value;
+#X text 142 226 creation argument initializes first value;
+#X floatatom 118 254;
+#X connect 0 0 9 0;
+#X connect 2 0 9 0;
+#X connect 3 0 9 1;
+#X connect 9 0 1 0;
+#X connect 9 1 13 0;
diff --git a/pd/doc/5.reference/switch~.pd b/pd/doc/5.reference/switch~.pd
new file mode 100644
index 00000000..c3ab8797
--- /dev/null
+++ b/pd/doc/5.reference/switch~.pd
@@ -0,0 +1,45 @@
+#N canvas 218 166 619 368 12;
+#X msg 382 133 \; metro 0;
+#X text 462 92 <-Click to start;
+#X text 455 137 <-Click to stop;
+#X text 47 13 switch and block - turn DSP on and off for subpatches
+and control block size;
+#N canvas 15 32 598 301 switched 1;
+#X obj 265 148 switch~;
+#X floatatom 265 121 1 0 0;
+#X floatatom 75 168 4 0 0;
+#X obj 75 104 noise~;
+#X obj 75 136 env~ 512;
+#X text 25 26 DSP in this subwindow is turned on and off by the switch~
+object. Any subwindows of this window can also be switched off here.
+If a patch and a superpatch both have switches \, both must be "on"
+for DSP to run in the patch.;
+#X text 32 203 switch~ takes optional arguments the same as block~.
+If you supply arguments to switch \, the patch will be switched AND
+reblocked.;
+#X text 31 258 Only one switch~ or block~ may appear in any window.
+;
+#X connect 1 0 0 0;
+#X connect 3 0 4 0;
+#X connect 4 0 2 0;
+#X restore 139 124 pd switched;
+#N canvas 13 421 564 200 blocked 1;
+#X obj 184 35 block~ 1024 4;
+#X text 14 76 This object specified that DSP in this subwindow is to
+be computed at a block size of 1024 \, and an overlap of 4 \, i.e.
+\, every 256 samples. You may not (yet) specify a block size smaller
+than your superpatch. This is useful for writing FFT based patches
+(see the "fft examples" tutorial series.);
+#X restore 141 158 pd blocked;
+#X msg 382 87 \; pd dsp 1;
+#X obj 382 61 loadbang;
+#X text 38 82 see the subpatches for explanation:;
+#X text 362 334 updated for Pd version 0.34;
+#X text 34 195 BUG! -- dac~ and adc~ work only with a blocksize of
+64 If you want to reblock audio computation \, do so in a sub-patch
+and keep the adc~ and dac~ objects in a super-patch. Also \, you can't
+send~ or receive~ between windows with different block sizes or overlapping.
+Only the inlet~ and outlet~ objects know how to reblock signals. In
+this example \, you could put a dac~ in this \, outer window \, or
+in the switched subwindow \, but not the blocked one.;
+#X connect 7 0 6 0;
diff --git a/pd/doc/5.reference/table.txt b/pd/doc/5.reference/table.txt
new file mode 100644
index 00000000..f2a27ae0
--- /dev/null
+++ b/pd/doc/5.reference/table.txt
@@ -0,0 +1 @@
+0 0.1 0.2 0.3 0.4 0.5 0.6 0.5 0.4 0.3 0.2 0.1 0
diff --git a/pd/doc/5.reference/tabosc4~.pd b/pd/doc/5.reference/tabosc4~.pd
new file mode 100644
index 00000000..ada694e1
--- /dev/null
+++ b/pd/doc/5.reference/tabosc4~.pd
@@ -0,0 +1,86 @@
+#N canvas 307 35 742 511 12;
+#X floatatom 66 450 0 0 0;
+#N canvas 159 26 495 270 output 0;
+#X obj 414 196 t b;
+#X obj 414 134 f;
+#X obj 414 73 inlet;
+#X text 421 36 mute;
+#X obj 414 227 f;
+#X msg 521 218 0;
+#X msg 414 104 bang;
+#X obj 414 166 moses 1;
+#X obj 521 187 t b f;
+#X obj 486 143 moses 1;
+#X obj 102 181 dbtorms;
+#X obj 486 113 r master-lvl;
+#X obj 102 52 r master-lvl;
+#X obj 414 257 s master-lvl;
+#X obj 26 222 inlet~;
+#X obj 244 50 inlet;
+#X text 244 22 level;
+#X obj 244 122 s master-lvl;
+#X msg 118 80 set \$1;
+#X obj 118 109 outlet;
+#X msg 262 78 \; pd dsp 1;
+#X obj 102 238 line~;
+#X obj 26 259 *~;
+#X obj 26 295 dac~;
+#X obj 102 210 pack 0 50;
+#X text 24 195 audio;
+#X text 114 135 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 32 476 pd output;
+#X msg 102 450 MUTE;
+#X text 148 449 <--- volume in dB;
+#X floatatom 32 296 4 0 0;
+#X obj 32 326 sig~ 100;
+#X obj 547 52 table table1;
+#X obj 547 80 table table2;
+#X msg 372 287 \; table1 sinesum 512 0.5 0.5 0.5 0.5 \; table2 cosinesum 512 0 1;
+#X text 433 474 Updated for Pd version 0.33;
+#X obj 20 11 tabosc4~;
+#X text 110 12 4-point interpolating oscillator;
+#X msg 52 372 set table1;
+#X obj 32 413 tabosc4~ table1;
+#X msg 156 372 set table2;
+#X text 372 343 click above \, start DSP \, and turn output;
+#X text 372 361 volume up to hear this;
+#X text 14 40 tabosc4~ is a traditional computer music style wavetable lookup oscillator using 4-point polynomial interpolation. The table should have a poiwer of two points plus three "guard points" \, one at the beginning and two at the end \, which should be wraparound copies of the last point and the first two points \, respectively. The "sinesum" and "cosinesum" methods for arrays do this automatically for you if you just want to specify partial strengths.;
+#X text 14 178 For good results use 512 points for up to about 15 partials \, or 32*npartials (rounded up to a power of 2) for more than 15;
+#X floatatom 275 391 4 0 0;
+#X text 12 233 Don't send new "sinesum" messages to tables while you're running -- instead \, use "set" messages to switch between tables.;
+#X text 80 298 signal input for frequency (Hz.);
+#X text 46 349 message to switch tables;
+#X text 325 391 inlet to reset phase;
+#X text 166 414 creation argument initializes table;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 4 0 5 0;
+#X connect 5 0 13 0;
+#X connect 12 0 13 0;
+#X connect 13 0 1 0;
+#X connect 14 0 13 0;
+#X connect 19 0 13 1;
diff --git a/pd/doc/5.reference/tabplay~.pd b/pd/doc/5.reference/tabplay~.pd
new file mode 100644
index 00000000..92cdb81e
--- /dev/null
+++ b/pd/doc/5.reference/tabplay~.pd
@@ -0,0 +1,66 @@
+#N canvas 28 13 707 471 10;
+#X msg 639 93 \; pd dsp 0;
+#X graph graph1 0 -1 155948 1 428 369 678 169;
+#X array array99 155948 float 0;
+#X pop;
+#X floatatom 11 342 0 0 0;
+#X msg 11 109 set array99;
+#X text 93 109 "set" message permits you to switch between arrays;
+#X text 128 228 creation argument initializes array name;
+#X text 5 392 see also the "array" tutorial in section 2 of the Pd
+documentation \, and these objects:;
+#X obj 6 438 tabwrite~;
+#X obj 140 439 tabread;
+#X obj 194 439 tabwrite;
+#X obj 254 439 tabsend~;
+#X obj 315 439 tabreceive~;
+#X obj 41 13 tabplay~;
+#X text 108 14 play a table as a sample (non-transposing);
+#X obj 11 228 tabplay~ array99;
+#X obj 452 82 soundfiler;
+#X msg 452 48 read -resize ../sound/bell.aiff array99 \; pd dsp 1 \;
+;
+#X floatatom 452 104 0 0 0;
+#X obj 11 316 env~ 16384;
+#X obj 396 439 soundfiler;
+#X obj 73 439 tabread4~;
+#X obj 87 360 dac~ 1;
+#X obj 87 323 *~;
+#X obj 100 304 line~;
+#X msg 100 263 0.1 100;
+#X msg 116 284 0 100;
+#X text 162 264 on;
+#X text 157 283 off;
+#X text 148 301 envelope;
+#X text 148 312 generator;
+#X text 101 248 amplitude controls:;
+#X text 131 362 audio output;
+#X obj 87 342 hip~ 5;
+#X msg 26 179 0 44100;
+#X msg 27 158 44100;
+#X msg 26 138 bang;
+#X text 475 449 updated for Pd version 0.29;
+#X text 29 43 The tabplay~ object plays a sample \, or part of one
+\, with no transposition or interpolation. It is cheaper than tabread4~
+and there are none of tabread4~'s interpolation artifacts.;
+#X text 509 25 click here to load table;
+#X text 80 136 "bang" or 0 plays whole sample;
+#X text 82 157 play starting at 44100th sample;
+#X text 93 177 play starting at beginning for 44100 samples;
+#X msg 25 199 44100 1000;
+#X text 103 198 play from 44100 through 45099 (1000 samples);
+#X connect 3 0 14 0;
+#X connect 14 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 16 0 15 0;
+#X connect 18 0 2 0;
+#X connect 22 0 32 0;
+#X connect 23 0 22 1;
+#X connect 24 0 23 0;
+#X connect 25 0 23 0;
+#X connect 32 0 21 0;
+#X connect 33 0 14 0;
+#X connect 34 0 14 0;
+#X connect 35 0 14 0;
+#X connect 42 0 14 0;
diff --git a/pd/doc/5.reference/tabread.pd b/pd/doc/5.reference/tabread.pd
new file mode 100644
index 00000000..fa671a21
--- /dev/null
+++ b/pd/doc/5.reference/tabread.pd
@@ -0,0 +1,21 @@
+#N canvas 44 26 703 454 12;
+#X text 52 181 index;
+#X obj 36 9 tabread;
+#X obj 15 244 tabread array99;
+#X floatatom 15 182 0 0 0;
+#X floatatom 15 278 0 0 0;
+#X graph graph1 0 0 10 10 362 379 612 179;
+#X array array99 10 float;
+#X pop;
+#X msg 31 56 \; readout 1 \; array99 resize 10 \; array99 bounds 0 0 10 10 \; array99 xlabel -0.5 0 1 2 3 4 5 6 7 8 9 10 \; array99 ylabel -1 0 1 2 3 4 5 6 7 8 9 10 \; array99 0 1 4 2 8 5 6 1 4 2 8;
+#X text 60 276 output = array99[index];
+#X text 141 33 click here to initialize;
+#X text 159 236 creation argument;
+#X text 155 254 gives array name;
+#X msg 25 204 set array99;
+#X text 137 204 change array name;
+#X text 422 407 updated for Pd version 0.33;
+#X text 110 8 - read numbers from a table;
+#X connect 2 0 4 0;
+#X connect 3 0 2 0;
+#X connect 11 0 2 0;
diff --git a/pd/doc/5.reference/tabread4~.pd b/pd/doc/5.reference/tabread4~.pd
new file mode 100644
index 00000000..c28f580a
--- /dev/null
+++ b/pd/doc/5.reference/tabread4~.pd
@@ -0,0 +1,43 @@
+#N canvas 59 33 814 475 10;
+#X obj 11 228 tabread4~ array99;
+#X text 21 207 signal input x(n);
+#X msg 727 51 \; pd dsp 0;
+#X graph graph1 0 -1 9 1 514 373 764 173;
+#X array array99 10 float 0;
+#X pop;
+#X text 127 21 4-point-interpolating table lookup;
+#X obj 11 316 snapshot~;
+#X obj 30 290 metro 200;
+#X obj 11 124 sig~;
+#X floatatom 11 98 0 0 0;
+#X obj 30 264 r readout;
+#X floatatom 11 342 0 0 0;
+#X msg 452 50 \; readout 1 \; array99 resize 10 \; array99 0 -0.5 -0.5
+-0.5 0.5 0.5 0.5 \; pd dsp 1 \;;
+#X text 49 94 incoming signal is index. Indices should range from 1
+to (size-2) so that the 4-point interpolation is meaningful. You can
+shift-drag the number box to see the effect of interpolation.;
+#X msg 34 158 set array99;
+#X text 116 158 "set" message permits you to switch between arrays
+;
+#X text 139 228 creation argument initializes array name;
+#X text 5 392 see also the "array" tutorial in section 2 of the Pd
+documentation \, and these objects:;
+#X obj 47 21 tabread4~;
+#X text 509 27 click here to test;
+#X obj 12 442 tabwrite~;
+#X obj 157 442 tabread;
+#X obj 216 442 tabwrite;
+#X obj 281 442 tabsend~;
+#X obj 346 442 tabreceive~;
+#X text 7 58 Tabread4~ is used to build samplers and other table lookup
+algorithms. The interpolation scheme is 4-point polynomial.;
+#X text 616 460 updated for Pd version 0.29;
+#X obj 83 442 tabplay~;
+#X connect 0 0 5 0;
+#X connect 5 0 10 0;
+#X connect 6 0 5 0;
+#X connect 7 0 0 0;
+#X connect 8 0 7 0;
+#X connect 9 0 6 0;
+#X connect 13 0 0 0;
diff --git a/pd/doc/5.reference/tabreceive~.pd b/pd/doc/5.reference/tabreceive~.pd
new file mode 100644
index 00000000..7de98346
--- /dev/null
+++ b/pd/doc/5.reference/tabreceive~.pd
@@ -0,0 +1,6 @@
+#N canvas 109 83 646 239 12;
+#X obj 21 18 tabreceive~;
+#X text 17 53 creation argument: name of array;
+#X text 16 83 By default a block is 64 samples \; this can be reset using the block~ object.;
+#X text 380 201 updated for Pd version 0.33;
+#X text 129 18 - read a block of a signal from an array continuously;
diff --git a/pd/doc/5.reference/tabsend~.pd b/pd/doc/5.reference/tabsend~.pd
new file mode 100644
index 00000000..85a4183f
--- /dev/null
+++ b/pd/doc/5.reference/tabsend~.pd
@@ -0,0 +1,6 @@
+#N canvas 151 91 596 222 12;
+#X obj 31 27 tabsend~;
+#X text 113 26 writes one block of a signal continuously to an array;
+#X text 41 60 creation argument: name of array;
+#X text 29 96 By default a block is 64 samples \; this can be reset using the block~ object.;
+#X text 318 186 updated for Pd version 0.33;
diff --git a/pd/doc/5.reference/tabwrite.pd b/pd/doc/5.reference/tabwrite.pd
new file mode 100644
index 00000000..60b31513
--- /dev/null
+++ b/pd/doc/5.reference/tabwrite.pd
@@ -0,0 +1,21 @@
+#N canvas 44 17 653 456 12;
+#X obj 31 27 tabwrite;
+#X floatatom 9 176 0 0 0;
+#X obj 9 282 tabwrite array99;
+#X text 113 28 write numbers to a table;
+#X graph graph1 0 0 10 10 355 389 605 189;
+#X array array99 10 float;
+#X pop;
+#X msg 9 53 \; readout 1 \; array99 resize 10 \; array99 bounds 0 0 10 10 \; array99 xlabel -0.5 0 1 2 3 4 5 6 7 8 9 10 \; array99 ylabel -1 0 1 2 3 4 5 6 7 8 9 10 \; array99 0 1 4 2 8 5 6 1 4 2 8;
+#X text 406 94 click here to initialize;
+#X floatatom 146 257 0 0 0;
+#X text 158 279 creation argument;
+#X text 160 297 is array name;
+#X text 46 174 set y value;
+#X text 44 239 right inlet selects x value;
+#X msg 25 204 set array99;
+#X text 133 203 change array name;
+#X text 389 423 updated for Pd version 0.33;
+#X connect 1 0 2 0;
+#X connect 7 0 2 1;
+#X connect 12 0 2 0;
diff --git a/pd/doc/5.reference/tabwrite~.pd b/pd/doc/5.reference/tabwrite~.pd
new file mode 100644
index 00000000..606f4f30
--- /dev/null
+++ b/pd/doc/5.reference/tabwrite~.pd
@@ -0,0 +1,30 @@
+#N canvas 119 134 697 332 10;
+#X obj 31 27 tabwrite~;
+#X text 110 27 object to write a signal in an array;
+#X msg 43 131 bang;
+#X obj 23 211 tabwrite~ array99;
+#X graph graph1 0 -1 100 1 460 235 610 135;
+#X array array99 100 float;
+#X pop;
+#X obj 23 82 sig~ 3000;
+#X obj 23 110 phasor~;
+#X text 149 213 creation argument initializes array name;
+#X msg 40 181 set array99;
+#X msg 445 35 \; pd dsp 1;
+#X msg 524 37 \; pd dsp 0;
+#X text 85 133 bang to start recording;
+#X text 126 180 set the destination array;
+#X text 18 251 see also the "array" tutorial in section 2 of the Pd documentation \, and these objects:;
+#X obj 90 282 tabread;
+#X obj 149 282 tabwrite;
+#X obj 214 282 tabsend~;
+#X obj 279 282 tabreceive~;
+#X obj 17 282 tabread4~;
+#X msg 43 153 stop;
+#X text 85 154 stop recording;
+#X text 458 288 updated for Pd version 0.29;
+#X connect 2 0 3 0;
+#X connect 5 0 6 0;
+#X connect 6 0 3 0;
+#X connect 8 0 3 0;
+#X connect 19 0 3 0;
diff --git a/pd/doc/5.reference/text.pd b/pd/doc/5.reference/text.pd
new file mode 100644
index 00000000..96664048
--- /dev/null
+++ b/pd/doc/5.reference/text.pd
@@ -0,0 +1,4 @@
+#N canvas 74 127 544 214 12;
+#X text 281 174 updated for Pd version 0.26;
+#X text 107 13 comments;
+#X text 38 73 This is Pd's help window for comments \, which don't do anything.;
diff --git a/pd/doc/5.reference/textfile.pd b/pd/doc/5.reference/textfile.pd
new file mode 100644
index 00000000..8da1dde6
--- /dev/null
+++ b/pd/doc/5.reference/textfile.pd
@@ -0,0 +1,59 @@
+#N canvas 12 43 1181 529 12;
+#X msg 582 27 rewind;
+#X obj 577 416 print done;
+#X text 745 185 read a file;
+#X text 801 214 write one;
+#X text 97 472 see also:;
+#X obj 521 365 textfile;
+#X msg 584 188 read textfile.txt;
+#X obj 176 473 qlist;
+#X obj 145 20 textfile;
+#X text 236 20 read and write text files;
+#X text 34 92 The textfile object reads and writes text files to and
+from memory. You can read a file and output sequential lines as lists
+\, or collect lines and write them out. You can use this object to
+generate "models" for Gem \, for instance.;
+#X text 665 28 go to beginning;
+#X msg 582 54 bang;
+#X text 665 53 output one line as a list;
+#X msg 584 216 write /tmp/textfile.txt;
+#X msg 584 243 write /tmp/textfile2.txt cr;
+#X text 593 264 write a file \, terminating lines only with carriage
+return (omitting semicolons.) You can read files this way too \, in
+which case carriage returns are mapped to semicolons.;
+#X obj 521 438 print list;
+#X msg 583 312 read textfile.txt cr;
+#X msg 582 82 clear;
+#X text 737 83 empty the object;
+#X text 737 111 add a message;
+#X text 521 464 this outlet gets the lines in sequence.;
+#X text 35 246 You can also use this object simply for storing heterogeneous
+sequences of lists.;
+#X text 608 385 This outlet gets a bang when you hit the end of the
+sequence.;
+#X msg 582 162 set 2 4 6 8;
+#X text 740 163 clear and then add one message;
+#X msg 582 109 add cis boom bah;
+#X msg 582 136 add2 bang;
+#X text 734 136 add an unterminated message;
+#X text 31 160 To record textual messages and save them to a file \,
+first send "clear" to empty the qlist and "add" to add messages (terminated
+with semicolons.) The message \, "add2" adds a list of atoms without
+finishing with a semicolon in case you want to make variable-length
+messages.;
+#X msg 582 339 print;
+#X text 636 342 debugging printout;
+#X text 901 500 updated for Pd version 0.33;
+#X connect 0 0 5 0;
+#X connect 5 0 17 0;
+#X connect 5 1 1 0;
+#X connect 6 0 5 0;
+#X connect 12 0 5 0;
+#X connect 14 0 5 0;
+#X connect 15 0 5 0;
+#X connect 18 0 5 0;
+#X connect 19 0 5 0;
+#X connect 25 0 5 0;
+#X connect 27 0 5 0;
+#X connect 28 0 5 0;
+#X connect 31 0 5 0;
diff --git a/pd/doc/5.reference/textfile.txt b/pd/doc/5.reference/textfile.txt
new file mode 100644
index 00000000..99d21c09
--- /dev/null
+++ b/pd/doc/5.reference/textfile.txt
@@ -0,0 +1,6 @@
+2 4 6 8;
+cis boom bah;
+cis boom bah;
+cis boom bah;
+cis boom bah;
+cis boom bah;
diff --git a/pd/doc/5.reference/threshold~.pd b/pd/doc/5.reference/threshold~.pd
new file mode 100644
index 00000000..5922d15b
--- /dev/null
+++ b/pd/doc/5.reference/threshold~.pd
@@ -0,0 +1,31 @@
+#N canvas 114 43 685 360 12;
+#X msg 452 58 \; pd dsp 0;
+#X msg 452 24 \; pd dsp 1;
+#X obj 124 11 threshold~;
+#X text 200 12 - TRIGGER FROM AUDIO SIGNAL;
+#X obj 49 183 sig~;
+#X obj 49 261 threshold~ 10 100 0 100;
+#X text 303 255 Arguments:;
+#X text 384 255 1 trigger threshold;
+#X floatatom 49 156 5 0 0;
+#X obj 49 289 print trigger;
+#X obj 205 287 print rest;
+#X text 385 271 2 trigger debounce time;
+#X text 385 288 3 rest threshold;
+#X text 384 303 4 rest debounce time;
+#X text 486 342 updated for Pd version 0.32;
+#X msg 205 209 1;
+#X msg 235 210 0;
+#X text 12 39 threshold~ monitors its input signal and outputs bangs when the signal exceeds a specified "trigger" value \, and also when the signal recedes below a "rest" value. You can specify debounce times in milliseconds \, for the threshold~ to wait after the two event types before triggering again.;
+#X msg 131 151 set 0 2000 1 2000;
+#X msg 131 174 set 10 100 0 100;
+#X text 262 150 "set" to change the parameters;
+#X text 268 210 zero or nonszero in inlet to set the state to "high" or "low". There is no debounce period after this.;
+#X connect 4 0 5 0;
+#X connect 5 0 9 0;
+#X connect 5 1 10 0;
+#X connect 8 0 4 0;
+#X connect 15 0 5 1;
+#X connect 16 0 5 1;
+#X connect 18 0 5 0;
+#X connect 19 0 5 0;
diff --git a/pd/doc/5.reference/throw~.pd b/pd/doc/5.reference/throw~.pd
new file mode 100644
index 00000000..c9a1ce9d
--- /dev/null
+++ b/pd/doc/5.reference/throw~.pd
@@ -0,0 +1,34 @@
+#N canvas 80 120 664 313 12;
+#X obj 97 159 throw~ signal1;
+#X floatatom 268 211 0 0 0;
+#X obj 90 193 sig~ 50;
+#X obj 268 181 snapshot~;
+#X obj 90 221 throw~ signal1;
+#X obj 97 88 sig~ 25;
+#X obj 268 135 catch~ signal1;
+#X obj 35 17 throw~;
+#X obj 97 17 catch~;
+#X text 163 16 - summing signal bus and non-local connection;
+#X obj 551 88 loadbang;
+#X msg 561 110 \; pd dsp 1;
+#X obj 551 151 metro 200;
+#X text 33 48 Any number of throw~ objects can add into one catch~ object (but two catch~ objects cannot share the same name.);
+#X floatatom 401 206 0 0 0;
+#X obj 402 182 snapshot~;
+#X obj 402 134 catch~ signal2;
+#X msg 113 111 set signal2;
+#X msg 114 135 set signal1;
+#X text 75 252 You can redirect throw~ via a "set" message.;
+#X text 410 283 updated for Pd version 0.33;
+#X connect 2 0 4 0;
+#X connect 3 0 1 0;
+#X connect 5 0 0 0;
+#X connect 6 0 3 0;
+#X connect 10 0 11 0;
+#X connect 10 0 12 0;
+#X connect 12 0 3 0;
+#X connect 12 0 15 0;
+#X connect 15 0 14 0;
+#X connect 16 0 15 0;
+#X connect 17 0 0 0;
+#X connect 18 0 0 0;
diff --git a/pd/doc/5.reference/timer.pd b/pd/doc/5.reference/timer.pd
new file mode 100644
index 00000000..0f7b3829
--- /dev/null
+++ b/pd/doc/5.reference/timer.pd
@@ -0,0 +1,15 @@
+#N canvas 440 273 514 280 10;
+#X msg 60 146 bang;
+#X msg 30 115 bang;
+#X obj 30 175 timer;
+#X obj 66 15 timer;
+#X text 111 16 - measure logical time;
+#X floatatom 30 206;
+#X text 71 113 Click here to reset;
+#X text 98 147 Click here to get elapsed logical time;
+#X text 27 232 Output is in milliseconds;
+#X text 6 51 The timer object measures elapsed logical time. Logical time moves forward as if all computation were instantaneous and as if all "delay" and "metro" objects were exact.;
+#X text 319 260 updated for Pd version 0.25;
+#X connect 0 0 2 1;
+#X connect 1 0 2 0;
+#X connect 2 0 5 0;
diff --git a/pd/doc/5.reference/toggle.pd b/pd/doc/5.reference/toggle.pd
new file mode 100644
index 00000000..5cb9ae75
--- /dev/null
+++ b/pd/doc/5.reference/toggle.pd
@@ -0,0 +1,273 @@
+#N canvas 205 140 489 376 10;
+#X obj 1 1 cnv 8 100 60 empty empty toggle=tgl 20 20 1 18 -262144 -1109
+0;
+#X text 21 296 (c) musil@iem.kug.ac.at;
+#X text 63 309 IEM KUG;
+#X text 115 41 click properties to;
+#X text 103 52 modify geometry \, colors \, etc.;
+#X obj 168 113 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144
+-1 -1;
+#X obj 168 179 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144
+-1 -1;
+#X obj 168 133 s foo6_rcv;
+#X obj 168 159 r foo6_snd;
+#X text 153 14 gui-toggle:;
+#X obj 26 270 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X obj 10 117 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X msg 26 39 33;
+#X obj 26 180 tgl 60 1 foo6_snd foo6_rcv big_toggle 63 20 194 13 -228992
+-4033 -34 1 1;
+#X msg 42 79 1;
+#X msg 49 99 0;
+#X floatatom 26 249 4 0 0;
+#X msg 33 59 -0.001;
+#X msg 103 135 set 1;
+#X msg 108 157 set 0;
+#X obj 3 155 tgl 15 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0 1
+;
+#X obj 65 249 tgl 15 0 empty empty empty 8 -8 0 10 -262144 -1 -1 1
+1;
+#X msg 95 114 set -0.23;
+#X obj 189 93 tgl 15 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 188 179 tgl 15 0 empty empty empty 8 -8 0 10 -262144 -1 -1 1
+1;
+#X msg 93 93 0 3 4.55;
+#X msg 85 73 0.22 0 -5.44;
+#X msg 189 113 set \$1;
+#X text 96 222 UP- \, DOWN- \, LEFT- or RIGHT-key;
+#X text 95 233 for moving selected gui-objects;
+#N canvas 440 175 699 530 edit 0;
+#X msg 47 151 \; foo6_rcv color \$1 \$2 \$3;
+#X obj 47 126 pack 0 0 0;
+#X obj 47 98 f;
+#X msg 24 50 bang;
+#X floatatom 63 48 3 0 29;
+#X floatatom 79 68 3 0 29;
+#X floatatom 112 84 3 0 29;
+#X text 91 48 background;
+#X text 106 68 front-color;
+#X text 140 85 label-color;
+#X obj 49 223 f;
+#X msg 27 202 bang;
+#X floatatom 65 201 3 63 88;
+#X floatatom 100 223 3 0 37;
+#X obj 49 246 pack 0 0;
+#X text 127 223 y-label;
+#X text 93 201 x-label;
+#X msg 49 271 \; foo6_rcv label_pos \$1 \$2;
+#X floatatom 505 55 3 8 75;
+#X text 532 55 size;
+#X msg 505 76 \; foo6_rcv size \$1;
+#X obj 282 182 f;
+#X msg 260 161 bang;
+#X floatatom 298 160 3 -10 10;
+#X floatatom 333 182 3 -10 10;
+#X obj 282 205 pack 0 0;
+#X msg 282 230 \; foo6_rcv delta \$1 \$2;
+#X obj 296 301 f;
+#X msg 274 280 bang;
+#X floatatom 312 279 3 20 60;
+#X floatatom 347 301 3 150 200;
+#X obj 296 324 pack 0 0;
+#X msg 296 349 \; foo6_rcv pos \$1 \$2;
+#X text 326 160 x-delta;
+#X text 360 182 y-delta;
+#X text 340 279 x-position;
+#X text 374 301 y-position;
+#X obj 305 423 f;
+#X msg 283 402 bang;
+#X floatatom 321 401 3 -10 10;
+#X floatatom 356 423 3 -10 10;
+#X obj 305 446 pack 0 0;
+#X text 383 423 y-label;
+#X text 349 401 x-label;
+#X msg 305 471 \; foo6_rcv delta \$1 \$2;
+#X msg 499 140 \; foo6_rcv send foo6a_snd;
+#X msg 499 178 \; foo6_rcv send foo6_snd;
+#X msg 494 216 \; foo6_rcv receive foo6a_rcv;
+#X msg 494 254 \; foo6a_rcv receive foo6_rcv;
+#X msg 41 448 \; foo6_rcv label blabla;
+#X msg 41 484 \; foo6_rcv label big_toggle;
+#X obj 69 338 f;
+#X msg 47 317 bang;
+#X floatatom 85 316 3 0 2;
+#X floatatom 120 338 3 4 36;
+#X obj 69 361 pack 0 0;
+#X msg 69 386 \; foo6_rcv label_font \$1 \$2;
+#X text 113 316 font;
+#X text 149 338 height;
+#X floatatom 498 307 5 -200 200;
+#X text 542 307 nonzero-value;
+#X msg 498 331 \; foo6_rcv nonzero \$1;
+#X msg 503 412 \; foo6_rcv init 0;
+#X msg 510 479 \; foo6_rcv init 1;
+#X text 524 393 no init;
+#X text 500 461 init value on loadbang;
+#X msg 285 47 back;
+#X msg 285 67 front;
+#X msg 285 87 label;
+#X msg 247 47 bang;
+#N canvas 15 207 606 448 RGB_____________ 0;
+#X obj 97 56 inlet;
+#X obj 262 53 inlet;
+#X obj 339 55 inlet;
+#X obj 405 56 inlet;
+#X obj 97 270 bang;
+#X msg 77 295 0;
+#X msg 104 295 1;
+#X obj 146 268 bang;
+#X msg 132 295 0;
+#X msg 160 295 1;
+#X obj 196 269 bang;
+#X msg 187 295 0;
+#X msg 214 295 1;
+#X obj 265 313 spigot;
+#X obj 312 313 spigot;
+#X obj 359 313 spigot;
+#X obj 249 385 outlet;
+#X text 93 33 select;
+#X text 267 28 red;
+#X text 337 30 green;
+#X text 409 30 blue;
+#X obj 405 102 t b f;
+#X obj 339 160 +;
+#X obj 339 185 t b f;
+#X obj 339 216 +;
+#X obj 296 385 outlet;
+#X obj 343 385 outlet;
+#X obj 28 180 loadbang;
+#X obj 97 135 route back front label bang;
+#X obj 343 362 f;
+#X obj 296 361 f;
+#X obj 249 361 f;
+#X obj 262 79 * -65536;
+#X obj 339 80 * -256;
+#X obj 405 80 * -1;
+#X obj 339 247 - 1;
+#X obj 235 168 t b b b b;
+#X connect 0 0 28 0;
+#X connect 1 0 32 0;
+#X connect 2 0 33 0;
+#X connect 3 0 34 0;
+#X connect 4 0 5 0;
+#X connect 4 0 6 0;
+#X connect 5 0 14 1;
+#X connect 5 0 15 1;
+#X connect 6 0 13 1;
+#X connect 7 0 8 0;
+#X connect 7 0 9 0;
+#X connect 8 0 13 1;
+#X connect 8 0 15 1;
+#X connect 9 0 14 1;
+#X connect 10 0 11 0;
+#X connect 10 0 12 0;
+#X connect 11 0 13 1;
+#X connect 11 0 14 1;
+#X connect 12 0 15 1;
+#X connect 13 0 31 1;
+#X connect 14 0 30 1;
+#X connect 15 0 29 1;
+#X connect 21 0 22 0;
+#X connect 21 1 22 1;
+#X connect 22 0 23 0;
+#X connect 23 0 24 0;
+#X connect 23 1 24 1;
+#X connect 24 0 35 0;
+#X connect 27 0 6 0;
+#X connect 28 0 4 0;
+#X connect 28 1 7 0;
+#X connect 28 2 10 0;
+#X connect 28 3 36 0;
+#X connect 29 0 26 0;
+#X connect 30 0 25 0;
+#X connect 31 0 16 0;
+#X connect 32 0 24 0;
+#X connect 33 0 22 0;
+#X connect 34 0 21 0;
+#X connect 35 0 15 0;
+#X connect 35 0 14 0;
+#X connect 35 0 13 0;
+#X connect 36 0 31 0;
+#X connect 36 1 30 0;
+#X connect 36 2 29 0;
+#X connect 36 3 35 0;
+#X restore 285 108 pd RGB_____________;
+#X floatatom 327 77 3 0 255;
+#X floatatom 370 77 3 0 255;
+#X floatatom 413 78 3 0 255;
+#X text 34 22 preset-colors;
+#X text 296 19 RGB-colors;
+#X text 327 59 red;
+#X text 363 58 green;
+#X text 411 58 blue;
+#X connect 1 0 0 0;
+#X connect 2 0 1 0;
+#X connect 3 0 2 0;
+#X connect 4 0 2 1;
+#X connect 5 0 1 1;
+#X connect 6 0 1 2;
+#X connect 10 0 14 0;
+#X connect 11 0 10 0;
+#X connect 12 0 10 1;
+#X connect 13 0 14 1;
+#X connect 14 0 17 0;
+#X connect 18 0 20 0;
+#X connect 21 0 25 0;
+#X connect 22 0 21 0;
+#X connect 23 0 21 1;
+#X connect 24 0 25 1;
+#X connect 25 0 26 0;
+#X connect 27 0 31 0;
+#X connect 28 0 27 0;
+#X connect 29 0 27 1;
+#X connect 30 0 31 1;
+#X connect 31 0 32 0;
+#X connect 37 0 41 0;
+#X connect 38 0 37 0;
+#X connect 39 0 37 1;
+#X connect 40 0 41 1;
+#X connect 41 0 44 0;
+#X connect 51 0 55 0;
+#X connect 52 0 51 0;
+#X connect 53 0 51 1;
+#X connect 54 0 55 1;
+#X connect 55 0 56 0;
+#X connect 59 0 61 0;
+#X connect 66 0 70 0;
+#X connect 67 0 70 0;
+#X connect 68 0 70 0;
+#X connect 69 0 70 0;
+#X connect 70 0 1 0;
+#X connect 70 1 1 1;
+#X connect 70 2 1 2;
+#X connect 71 0 70 1;
+#X connect 72 0 70 2;
+#X connect 73 0 70 3;
+#X restore 278 136 pd edit;
+#X obj 222 276 tgl 15 0 bbb bbb empty 20 8 192 8 -262144 -1 -1 1 1
+;
+#X text 191 320 updated for Pd version 0.35;
+#X text 38 321 graz \, austria 2002;
+#X obj 127 255 x_all_guis aaa bbb ccc ddd eee fff ggg hhh iii;
+#X connect 5 0 7 0;
+#X connect 8 0 6 0;
+#X connect 8 0 24 0;
+#X connect 11 0 13 0;
+#X connect 12 0 13 0;
+#X connect 13 0 16 0;
+#X connect 13 0 21 0;
+#X connect 14 0 13 0;
+#X connect 15 0 13 0;
+#X connect 16 0 10 0;
+#X connect 17 0 13 0;
+#X connect 18 0 13 0;
+#X connect 19 0 13 0;
+#X connect 20 0 13 0;
+#X connect 22 0 13 0;
+#X connect 23 0 27 0;
+#X connect 25 0 13 0;
+#X connect 26 0 13 0;
+#X connect 27 0 7 0;
diff --git a/pd/doc/5.reference/trigger.pd b/pd/doc/5.reference/trigger.pd
new file mode 100644
index 00000000..a32d5def
--- /dev/null
+++ b/pd/doc/5.reference/trigger.pd
@@ -0,0 +1,37 @@
+#N canvas 58 142 685 355 12;
+#X msg 28 149 2.5;
+#X msg 126 151 bang;
+#X msg 68 150 23 64;
+#X obj 28 242 print x1;
+#X obj 112 242 print x2;
+#X obj 196 240 print x3;
+#X obj 43 26 trigger;
+#X obj 286 241 print x4;
+#X text 114 27 - sequence messages in right-to-left order;
+#X text 417 331 updated for Pd version 0.33;
+#X text 81 290 the above can be abbreviated as:;
+#X msg 172 152 symbol dog;
+#X text 39 59 The trigger object outputs its input from right to left
+\, converting to the types indicated by its creation arguments. There
+is also a "pointer" argument type (see the pointer object.);
+#X obj 381 293 t f b l s a;
+#X msg 466 167 dog my cats;
+#X obj 466 199 trigger bang anything;
+#X obj 374 242 print x5;
+#X obj 466 240 print y1;
+#X obj 552 242 print y2;
+#X obj 28 202 trigger float bang symbol list anything;
+#X text 464 122 "anythings" can only;
+#X text 461 142 be converted to bang:;
+#X connect 0 0 19 0;
+#X connect 1 0 19 0;
+#X connect 2 0 19 0;
+#X connect 11 0 19 0;
+#X connect 14 0 15 0;
+#X connect 15 0 17 0;
+#X connect 15 1 18 0;
+#X connect 19 0 3 0;
+#X connect 19 1 4 0;
+#X connect 19 2 5 0;
+#X connect 19 3 7 0;
+#X connect 19 4 16 0;
diff --git a/pd/doc/5.reference/unpack.pd b/pd/doc/5.reference/unpack.pd
new file mode 100644
index 00000000..5f1a4120
--- /dev/null
+++ b/pd/doc/5.reference/unpack.pd
@@ -0,0 +1,28 @@
+#N canvas 234 84 730 277 12;
+#X floatatom 80 180 0 0 0;
+#X floatatom 205 180 0 0 0;
+#X floatatom 243 180 0 0 0;
+#X floatatom 46 180 0 0 0;
+#X obj 117 180 print foo;
+#X obj 133 243 pack;
+#X text 51 242 See also;
+#X obj 101 9 unpack;
+#X text 164 8 - split a message to atoms;
+#X text 196 139 <-- creation arguments specify the types of atoms expected
+;
+#X msg 46 102 1 2;
+#X msg 85 102 3 4 shut;
+#X msg 164 102 5 6 pick 7 8;
+#X text 368 239 updated for Pd version 0.33;
+#X obj 46 139 unpack 0 0 s 0 0;
+#X text 25 36 unpack takes lists of atoms and distributes them to its
+outlets. The creation arguments specify float (any number or the symbol
+'f') \, pointer (symbol 'p') or symbol (symbol 's').;
+#X connect 10 0 14 0;
+#X connect 11 0 14 0;
+#X connect 12 0 14 0;
+#X connect 14 0 3 0;
+#X connect 14 1 0 0;
+#X connect 14 2 4 0;
+#X connect 14 3 1 0;
+#X connect 14 4 2 0;
diff --git a/pd/doc/5.reference/until.pd b/pd/doc/5.reference/until.pd
new file mode 100644
index 00000000..9da7a9ce
--- /dev/null
+++ b/pd/doc/5.reference/until.pd
@@ -0,0 +1,25 @@
+#N canvas 142 97 410 273 10;
+#X msg 65 116 bang;
+#X obj 66 15 until;
+#X text 114 16 - LOOP;
+#X text 24 36 The until object's left inlet starts a loop in which it outputs "bang" until its right inlet gets a bang which stops it. If you start "until" with a number \, it iterates at most that number of times \, as in the Max "uzi" object.;
+#X text 24 85 WARNING: if you bang an "until" which doesn't have a stopping mechanism \, Pd goes into an infinite loop!;
+#X obj 65 168 until;
+#X text 110 115 start;
+#X msg 73 137 3;
+#X text 109 138 start limited to 3 iterations;
+#X obj 65 196 f;
+#X obj 96 198 + 1;
+#X obj 140 198 sel 0;
+#X obj 65 240 print;
+#X obj 96 219 mod 10;
+#X text 225 247 updated for Pd version 0.28;
+#X connect 0 0 5 0;
+#X connect 5 0 9 0;
+#X connect 7 0 5 0;
+#X connect 9 0 10 0;
+#X connect 9 0 12 0;
+#X connect 10 0 13 0;
+#X connect 11 0 5 1;
+#X connect 13 0 9 1;
+#X connect 13 0 11 0;
diff --git a/pd/doc/5.reference/value.pd b/pd/doc/5.reference/value.pd
new file mode 100644
index 00000000..66c457a3
--- /dev/null
+++ b/pd/doc/5.reference/value.pd
@@ -0,0 +1,30 @@
+#N canvas 257 45 500 281 12;
+#X text 290 257 updated for Pd version 0.32;
+#X floatatom 36 55 5 0 0;
+#X text 50 249 abbreviation:;
+#X text 79 10 -- nonlocal shared value (named variable);
+#X floatatom 36 130 5 0 0;
+#X msg 46 78 bang;
+#X obj 21 10 value;
+#X obj 36 105 value help-value1;
+#X obj 149 248 v;
+#X floatatom 163 55 5 0 0;
+#X floatatom 163 130 5 0 0;
+#X msg 173 78 bang;
+#X obj 163 105 value help-value1;
+#X floatatom 291 55 5 0 0;
+#X floatatom 291 130 5 0 0;
+#X msg 301 78 bang;
+#X obj 291 105 value help-value2;
+#X text 31 171 "Value" stores a numeric value which is shared between all values with the same name (which need not be in the same Pd window.);
+#X text 345 54 numbers set the value;
+#X text 349 77 bang retrieves it;
+#X connect 1 0 7 0;
+#X connect 5 0 7 0;
+#X connect 7 0 4 0;
+#X connect 9 0 12 0;
+#X connect 11 0 12 0;
+#X connect 12 0 10 0;
+#X connect 13 0 16 0;
+#X connect 15 0 16 0;
+#X connect 16 0 14 0;
diff --git a/pd/doc/5.reference/vcf~.pd b/pd/doc/5.reference/vcf~.pd
new file mode 100644
index 00000000..45dde1d8
--- /dev/null
+++ b/pd/doc/5.reference/vcf~.pd
@@ -0,0 +1,35 @@
+#N canvas 0 0 644 422 12;
+#X obj 257 200 sig~;
+#X text 14 193 test signal;
+#X text 100 341 amp in (db);
+#X text 92 144 test frequency;
+#X text 224 340 amp out (db);
+#X obj 220 264 vcf~;
+#X text 246 144 center frequency;
+#X text 374 184 q;
+#X floatatom 122 168 5 0 0;
+#X floatatom 257 171 5 0 0;
+#X obj 122 193 osc~;
+#X floatatom 353 203 5 0 0;
+#X obj 122 291 env~ 8192;
+#X obj 220 290 env~ 8192;
+#X floatatom 121 318 5 0 0;
+#X floatatom 220 319 5 0 0;
+#X obj 80 13 vcf~;
+#X text 135 13 -- voltage-controlled bandpass filter;
+#X text 26 395 see also:;
+#X obj 115 394 bp~;
+#X text 302 394 updated for Pd version 0.35;
+#X text 12 45 vcf~ is like bp~ except that it takes an audio signal
+to set center frequency \, which may thus change continuously in time.
+The "Q" or filter sharpness is still only set by messages. More expensive
+than bp~ in CPU time but more powerful too.;
+#X connect 0 0 5 1;
+#X connect 5 0 13 0;
+#X connect 8 0 10 0;
+#X connect 9 0 0 0;
+#X connect 10 0 5 0;
+#X connect 10 0 12 0;
+#X connect 11 0 5 2;
+#X connect 12 0 14 0;
+#X connect 13 0 15 0;
diff --git a/pd/doc/5.reference/vdial.pd b/pd/doc/5.reference/vdial.pd
new file mode 100644
index 00000000..048c4c2b
--- /dev/null
+++ b/pd/doc/5.reference/vdial.pd
@@ -0,0 +1,282 @@
+#N canvas 106 314 558 455 10;
+#X obj 1 1 cnv 8 100 60 empty empty vdial=vdl 20 20 1 18 -262144 -1109
+0;
+#X text 13 390 (c) musil@iem.kug.ac.at;
+#X text 55 403 IEM KUG;
+#X text 132 122 click properties to;
+#X text 120 133 modify geometry \, colors \, etc.;
+#X obj 159 261 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144
+-1 -1;
+#X obj 21 54 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X msg 41 319 \$1;
+#X floatatom 41 341 4 0 0;
+#X obj 41 363 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X obj 86 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 86 317 route 0 1 2 3 4 5 6 7 8 9;
+#X msg 194 92 set \$1;
+#X floatatom 194 71 4 0 9;
+#X floatatom 44 54 4 0 9;
+#X msg 91 41 7 0 -5.44;
+#X msg 95 63 3 3 4.55;
+#X obj 103 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 120 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 1
+1;
+#X obj 137 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 154 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 171 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 188 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 205 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 222 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 239 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
+1;
+#X obj 79 355 print;
+#X floatatom 183 287 4 0 0;
+#X msg 183 261 \$1;
+#X msg 158 192 set \$1;
+#X floatatom 158 171 4 0 9;
+#X text 125 355 UP- \, DOWN- \, LEFT- or RIGHT-key;
+#X text 124 366 for moving selected gui-objects;
+#N canvas 230 247 699 530 edit 0;
+#X obj 42 198 f;
+#X msg 20 177 bang;
+#X floatatom 58 176 3 63 156;
+#X floatatom 93 198 3 -20 37;
+#X obj 42 221 pack 0 0;
+#X text 120 198 y-label;
+#X text 86 176 x-label;
+#X floatatom 270 187 3 8 50;
+#X text 297 187 size;
+#X obj 286 293 f;
+#X msg 264 272 bang;
+#X floatatom 302 271 3 -10 10;
+#X floatatom 337 293 3 -10 10;
+#X obj 286 316 pack 0 0;
+#X obj 300 412 f;
+#X msg 278 391 bang;
+#X floatatom 316 390 3 20 60;
+#X floatatom 351 412 3 100 200;
+#X obj 300 435 pack 0 0;
+#X text 330 271 x-delta;
+#X text 364 293 y-delta;
+#X text 344 390 x-position;
+#X text 378 412 y-position;
+#X obj 62 313 f;
+#X msg 40 292 bang;
+#X floatatom 78 291 3 0 2;
+#X floatatom 113 313 3 4 36;
+#X obj 62 336 pack 0 0;
+#X text 106 291 font;
+#X text 142 313 height;
+#X text 504 293 no init;
+#X text 475 348 init value on loadbang;
+#X floatatom 482 228 5 2 20;
+#X text 491 417 changing-behavior;
+#X text 526 228 number of buttons;
+#X obj 47 104 pack 0 0 0;
+#X obj 47 76 f;
+#X msg 24 28 bang;
+#X floatatom 63 26 3 0 29;
+#X floatatom 79 46 3 0 29;
+#X floatatom 112 62 3 0 29;
+#X text 91 26 background;
+#X text 106 46 front-color;
+#X text 140 63 label-color;
+#X msg 285 25 back;
+#X msg 285 45 front;
+#X msg 285 65 label;
+#X msg 247 25 bang;
+#N canvas 15 207 606 448 RGB_____________ 0;
+#X obj 97 56 inlet;
+#X obj 262 53 inlet;
+#X obj 339 55 inlet;
+#X obj 405 56 inlet;
+#X obj 97 270 bang;
+#X msg 77 295 0;
+#X msg 104 295 1;
+#X obj 146 268 bang;
+#X msg 132 295 0;
+#X msg 160 295 1;
+#X obj 196 269 bang;
+#X msg 187 295 0;
+#X msg 214 295 1;
+#X obj 265 313 spigot;
+#X obj 312 313 spigot;
+#X obj 359 313 spigot;
+#X obj 249 385 outlet;
+#X text 93 33 select;
+#X text 267 28 red;
+#X text 337 30 green;
+#X text 409 30 blue;
+#X obj 405 102 t b f;
+#X obj 339 160 +;
+#X obj 339 185 t b f;
+#X obj 339 216 +;
+#X obj 296 385 outlet;
+#X obj 343 385 outlet;
+#X obj 28 180 loadbang;
+#X obj 97 135 route back front label bang;
+#X obj 343 362 f;
+#X obj 296 361 f;
+#X obj 249 361 f;
+#X obj 262 79 * -65536;
+#X obj 339 80 * -256;
+#X obj 405 80 * -1;
+#X obj 339 247 - 1;
+#X obj 235 168 t b b b b;
+#X connect 0 0 28 0;
+#X connect 1 0 32 0;
+#X connect 2 0 33 0;
+#X connect 3 0 34 0;
+#X connect 4 0 5 0;
+#X connect 4 0 6 0;
+#X connect 5 0 14 1;
+#X connect 5 0 15 1;
+#X connect 6 0 13 1;
+#X connect 7 0 8 0;
+#X connect 7 0 9 0;
+#X connect 8 0 13 1;
+#X connect 8 0 15 1;
+#X connect 9 0 14 1;
+#X connect 10 0 11 0;
+#X connect 10 0 12 0;
+#X connect 11 0 13 1;
+#X connect 11 0 14 1;
+#X connect 12 0 15 1;
+#X connect 13 0 31 1;
+#X connect 14 0 30 1;
+#X connect 15 0 29 1;
+#X connect 21 0 22 0;
+#X connect 21 1 22 1;
+#X connect 22 0 23 0;
+#X connect 23 0 24 0;
+#X connect 23 1 24 1;
+#X connect 24 0 35 0;
+#X connect 27 0 6 0;
+#X connect 28 0 4 0;
+#X connect 28 1 7 0;
+#X connect 28 2 10 0;
+#X connect 28 3 36 0;
+#X connect 29 0 26 0;
+#X connect 30 0 25 0;
+#X connect 31 0 16 0;
+#X connect 32 0 24 0;
+#X connect 33 0 22 0;
+#X connect 34 0 21 0;
+#X connect 35 0 15 0;
+#X connect 35 0 14 0;
+#X connect 35 0 13 0;
+#X connect 36 0 31 0;
+#X connect 36 1 30 0;
+#X connect 36 2 29 0;
+#X connect 36 3 35 0;
+#X restore 285 86 pd RGB_____________;
+#X floatatom 327 55 3 0 255;
+#X floatatom 370 55 3 0 255;
+#X floatatom 413 56 3 0 255;
+#X text 34 0 preset-colors;
+#X text 296 -3 RGB-colors;
+#X text 327 37 red;
+#X text 363 36 green;
+#X text 411 36 blue;
+#X msg 47 125 \; foo12_rcv color \$1 \$2 \$3;
+#X msg 42 246 \; foo12_rcv label_pos \$1 \$2;
+#X msg 62 361 \; foo12_rcv label_font \$1 \$2;
+#X msg 34 423 \; foo12_rcv label blabla;
+#X msg 34 459 \; foo12_rcv label vdial_0_9;
+#X msg 300 460 \; foo12_rcv pos \$1 \$2;
+#X msg 286 341 \; foo12_rcv delta \$1 \$2;
+#X msg 270 216 \; foo12_rcv size \$1;
+#X msg 483 50 \; foo12_rcv send foo12a_snd;
+#X msg 483 88 \; foo12_rcv send foo12_snd;
+#X msg 482 171 \; foo12a_rcv receive foo12_rcv;
+#X msg 483 133 \; foo12_rcv receive foo12a_rcv;
+#X msg 482 254 \; foo12_rcv number \$1;
+#X msg 483 312 \; foo12_rcv init 0;
+#X msg 485 366 \; foo12_rcv init 1;
+#X msg 490 436 \; foo12_rcv single_change;
+#X msg 490 470 \; foo12_rcv double_change;
+#X connect 0 0 4 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 1;
+#X connect 3 0 4 1;
+#X connect 4 0 58 0;
+#X connect 7 0 64 0;
+#X connect 9 0 13 0;
+#X connect 10 0 9 0;
+#X connect 11 0 9 1;
+#X connect 12 0 13 1;
+#X connect 13 0 63 0;
+#X connect 14 0 18 0;
+#X connect 15 0 14 0;
+#X connect 16 0 14 1;
+#X connect 17 0 18 1;
+#X connect 18 0 62 0;
+#X connect 23 0 27 0;
+#X connect 24 0 23 0;
+#X connect 25 0 23 1;
+#X connect 26 0 27 1;
+#X connect 27 0 59 0;
+#X connect 32 0 69 0;
+#X connect 35 0 57 0;
+#X connect 36 0 35 0;
+#X connect 37 0 36 0;
+#X connect 38 0 36 1;
+#X connect 39 0 35 1;
+#X connect 40 0 35 2;
+#X connect 44 0 48 0;
+#X connect 45 0 48 0;
+#X connect 46 0 48 0;
+#X connect 47 0 48 0;
+#X connect 48 0 35 0;
+#X connect 48 1 35 1;
+#X connect 48 2 35 2;
+#X connect 49 0 48 1;
+#X connect 50 0 48 2;
+#X connect 51 0 48 3;
+#X restore 267 222 pd edit;
+#X obj 221 11 x_all_guis aaa bbb ccc ddd eee fff ggg hhh iii;
+#X text 30 415 graz \, austria 2002;
+#X text 223 401 updated for Pd version 0.35;
+#X text 144 11 gui-vdial:;
+#X obj 79 110 vdl 25 1 0 8 foo12_snd foo12_rcv vdial_0_9 20 -8 192
+10 -99865 -262144 -260818 0;
+#X obj 352 36 vdl 15 1 0 8 iii iii empty 20 8 192 8 -262144 -1 -1 0
+;
+#X obj 158 213 s foo12_rcv;
+#X obj 159 239 r foo12_snd;
+#X connect 6 0 38 0;
+#X connect 7 0 8 0;
+#X connect 8 0 9 0;
+#X connect 11 0 10 0;
+#X connect 11 1 17 0;
+#X connect 11 2 18 0;
+#X connect 11 3 19 0;
+#X connect 11 4 20 0;
+#X connect 11 5 21 0;
+#X connect 11 6 22 0;
+#X connect 11 7 23 0;
+#X connect 11 8 24 0;
+#X connect 11 9 25 0;
+#X connect 12 0 38 0;
+#X connect 13 0 12 0;
+#X connect 14 0 38 0;
+#X connect 15 0 38 0;
+#X connect 16 0 38 0;
+#X connect 28 0 27 0;
+#X connect 29 0 40 0;
+#X connect 30 0 29 0;
+#X connect 38 0 11 0;
+#X connect 38 0 26 0;
+#X connect 38 0 7 0;
+#X connect 41 0 5 0;
+#X connect 41 0 28 0;
diff --git a/pd/doc/5.reference/vd~.pd b/pd/doc/5.reference/vd~.pd
new file mode 100644
index 00000000..5a36ff73
--- /dev/null
+++ b/pd/doc/5.reference/vd~.pd
@@ -0,0 +1,19 @@
+#N canvas 109 10 654 410 12;
+#X floatatom 50 194 0 0 0;
+#X obj 50 287 outlet~;
+#X text 130 286 signal output (delayed signal);
+#X obj 24 16 vd~;
+#X text 60 9 reads a signal from a delay line at a variable delay time (4-point-interpolation);
+#X obj 50 222 sig~;
+#X text 99 219 signal input (delay time in ms);
+#X obj 50 254 vd~ del_example;
+#X text 193 252 creation argument: name of delay line;
+#X text 31 51 vd~ implements a 4-point interpolating delay tap from a corresponding delwrite~ object. The delay in milliseconds of the tap is specified by the incoming signal.;
+#X text 35 340 see also:;
+#X obj 123 343 delwrite~;
+#X obj 212 342 delread~;
+#X text 354 373 updated for Pd version 0.33;
+#X text 28 103 The delay time is always at least one sample and at most the length of the delay line (specified by hte delwrite~). In addition \, in case the delwrite~ runs later in the DSP loop than the vd~ \, the delay is constrained below by one vector length (64 samples.);
+#X connect 0 0 5 0;
+#X connect 5 0 7 0;
+#X connect 7 0 1 0;
diff --git a/pd/doc/5.reference/vslider.pd b/pd/doc/5.reference/vslider.pd
new file mode 100644
index 00000000..69452ad6
--- /dev/null
+++ b/pd/doc/5.reference/vslider.pd
@@ -0,0 +1,302 @@
+#N canvas 147 201 617 416 10;
+#X obj 1 1 cnv 8 100 60 empty empty vslider=vsl 20 20 1 18 -262144
+-1109 0;
+#X floatatom 38 300 9 0 0;
+#X msg 47 84 set \$1;
+#X floatatom 38 43 7 0 0;
+#X text 25 363 (c) musil@iem.kug.ac.at;
+#X text 67 376 IEM KUG;
+#X obj 38 324 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X obj 18 47 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
+-1;
+#X floatatom 47 63 7 0 0;
+#X floatatom 116 324 9 0 0;
+#X floatatom 106 42 7 0 0;
+#X floatatom 147 113 7 0 0;
+#X obj 75 249 ftom;
+#X floatatom 75 271 9 0 0;
+#X floatatom 111 244 9 0 0;
+#X floatatom 185 266 9 0 0;
+#X text 181 151 click properties to;
+#X floatatom 75 112 9 0 0;
+#X obj 75 134 mtof;
+#X text 166 12 gui-vertical-slider:;
+#X obj 38 162 vsl 15 101 100 300 0 1 foo3_snd foo3_rcv empty 8 -8 192
+10 -225280 -1109 -1 2500 1;
+#X obj 75 168 vsl 15 73 55 3520 1 1 goo4_snd goo4_rcv log.freq. 11
+-6 192 10 -261681 -260818 -90881 0 1;
+#X obj 185 244 r goo4_snd;
+#X obj 147 133 s goo4_rcv;
+#X text 202 65 (0.01 pixels);
+#X text 57 99 ------------------------------------------;
+#X text 57 286 --------------------------------------------;
+#X text 169 162 modify geometry \, colors \, etc.;
+#X obj 106 84 s foo3_rcv;
+#X obj 116 302 r foo3_snd;
+#X msg 106 63 set \$1;
+#X text 188 44 shift-click & drag;
+#X text 194 54 for fine-tuning;
+#X text 119 192 UP- \, DOWN- \, LEFT- or RIGHT-key;
+#X text 118 203 for moving selected gui-objects;
+#N canvas 239 379 699 530 edit 0;
+#X obj 37 233 f;
+#X msg 15 212 bang;
+#X floatatom 53 211 3 6 88;
+#X floatatom 88 233 3 -20 37;
+#X obj 37 256 pack 0 0;
+#X text 115 233 y-label;
+#X text 81 211 x-label;
+#X obj 287 271 f;
+#X msg 265 250 bang;
+#X floatatom 303 249 3 -10 10;
+#X floatatom 338 271 3 -10 10;
+#X obj 287 294 pack 0 0;
+#X obj 299 381 f;
+#X msg 277 360 bang;
+#X floatatom 315 359 3 20 90;
+#X floatatom 350 381 3 150 200;
+#X obj 299 404 pack 0 0;
+#X text 331 249 x-delta;
+#X text 365 271 y-delta;
+#X text 343 359 x-position;
+#X text 377 381 y-position;
+#X obj 57 348 f;
+#X msg 35 327 bang;
+#X floatatom 73 326 3 0 2;
+#X floatatom 108 348 3 4 36;
+#X obj 57 371 pack 0 0;
+#X text 101 326 font;
+#X text 137 348 height;
+#X floatatom 476 188 1 0 1;
+#X text 523 401 no init;
+#X text 493 453 init value on loadbang;
+#X text 520 188 steady;
+#X obj 486 291 f;
+#X msg 464 270 bang;
+#X floatatom 502 269 4 55 440;
+#X floatatom 537 291 6 440 3520;
+#X obj 486 314 pack 0 0;
+#X text 269 469 linear / logarithmical;
+#X msg 47 158 \; goo4_rcv color \$1 \$2 \$3;
+#X msg 37 281 \; goo4_rcv label_pos \$1 \$2;
+#X msg 57 396 \; goo4_rcv label_font \$1 \$2;
+#X msg 40 442 \; goo4_rcv label blabla;
+#X msg 269 487 \; goo4_rcv lin;
+#X msg 363 486 \; goo4_rcv log;
+#X msg 299 429 \; goo4_rcv pos \$1 \$2;
+#X msg 287 319 \; goo4_rcv delta \$1 \$2;
+#X msg 475 21 \; goo4_rcv send goo4a_snd;
+#X msg 475 59 \; goo4_rcv send goo4_snd;
+#X msg 476 105 \; goo4_rcv receive goo4a_rcv;
+#X msg 476 143 \; goo4a_rcv receive goo4_rcv;
+#X msg 486 339 \; goo4_rcv range \$1 \$2;
+#X msg 502 420 \; goo4_rcv init 0;
+#X msg 503 471 \; goo4_rcv init 1;
+#X text 539 270 bottom-range-bound;
+#X text 586 292 top-range-bound;
+#X obj 286 160 f;
+#X msg 264 139 bang;
+#X floatatom 302 138 3 4 55;
+#X floatatom 337 160 3 15 73;
+#X obj 286 183 pack 0 0;
+#X msg 286 208 \; goo4_rcv size \$1 \$2;
+#X text 330 138 width;
+#X text 368 161 height;
+#X msg 41 478 \; goo4_rcv label log.freq.;
+#X msg 476 212 \; goo4_rcv steady \$1;
+#X obj 47 116 pack 0 0 0;
+#X obj 47 88 f;
+#X msg 24 40 bang;
+#X floatatom 63 38 3 0 29;
+#X floatatom 79 58 3 0 29;
+#X floatatom 112 74 3 0 29;
+#X text 91 38 background;
+#X text 106 58 front-color;
+#X text 140 75 label-color;
+#X msg 285 37 back;
+#X msg 285 57 front;
+#X msg 285 77 label;
+#X msg 247 37 bang;
+#N canvas 15 207 606 448 RGB_____________ 0;
+#X obj 97 56 inlet;
+#X obj 262 53 inlet;
+#X obj 339 55 inlet;
+#X obj 405 56 inlet;
+#X obj 97 270 bang;
+#X msg 77 295 0;
+#X msg 104 295 1;
+#X obj 146 268 bang;
+#X msg 132 295 0;
+#X msg 160 295 1;
+#X obj 196 269 bang;
+#X msg 187 295 0;
+#X msg 214 295 1;
+#X obj 265 313 spigot;
+#X obj 312 313 spigot;
+#X obj 359 313 spigot;
+#X obj 249 385 outlet;
+#X text 93 33 select;
+#X text 267 28 red;
+#X text 337 30 green;
+#X text 409 30 blue;
+#X obj 405 102 t b f;
+#X obj 339 160 +;
+#X obj 339 185 t b f;
+#X obj 339 216 +;
+#X obj 296 385 outlet;
+#X obj 343 385 outlet;
+#X obj 28 180 loadbang;
+#X obj 97 135 route back front label bang;
+#X obj 343 362 f;
+#X obj 296 361 f;
+#X obj 249 361 f;
+#X obj 262 79 * -65536;
+#X obj 339 80 * -256;
+#X obj 405 80 * -1;
+#X obj 339 247 - 1;
+#X obj 235 168 t b b b b;
+#X connect 0 0 28 0;
+#X connect 1 0 32 0;
+#X connect 2 0 33 0;
+#X connect 3 0 34 0;
+#X connect 4 0 5 0;
+#X connect 4 0 6 0;
+#X connect 5 0 14 1;
+#X connect 5 0 15 1;
+#X connect 6 0 13 1;
+#X connect 7 0 8 0;
+#X connect 7 0 9 0;
+#X connect 8 0 13 1;
+#X connect 8 0 15 1;
+#X connect 9 0 14 1;
+#X connect 10 0 11 0;
+#X connect 10 0 12 0;
+#X connect 11 0 13 1;
+#X connect 11 0 14 1;
+#X connect 12 0 15 1;
+#X connect 13 0 31 1;
+#X connect 14 0 30 1;
+#X connect 15 0 29 1;
+#X connect 21 0 22 0;
+#X connect 21 1 22 1;
+#X connect 22 0 23 0;
+#X connect 23 0 24 0;
+#X connect 23 1 24 1;
+#X connect 24 0 35 0;
+#X connect 27 0 6 0;
+#X connect 28 0 4 0;
+#X connect 28 1 7 0;
+#X connect 28 2 10 0;
+#X connect 28 3 36 0;
+#X connect 29 0 26 0;
+#X connect 30 0 25 0;
+#X connect 31 0 16 0;
+#X connect 32 0 24 0;
+#X connect 33 0 22 0;
+#X connect 34 0 21 0;
+#X connect 35 0 15 0;
+#X connect 35 0 14 0;
+#X connect 35 0 13 0;
+#X connect 36 0 31 0;
+#X connect 36 1 30 0;
+#X connect 36 2 29 0;
+#X connect 36 3 35 0;
+#X restore 285 98 pd RGB_____________;
+#X floatatom 327 67 3 0 255;
+#X floatatom 370 67 3 0 255;
+#X floatatom 413 68 3 0 255;
+#X text 34 12 preset-colors;
+#X text 296 9 RGB-colors;
+#X text 327 49 red;
+#X text 363 48 green;
+#X text 411 48 blue;
+#X connect 0 0 4 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 1;
+#X connect 3 0 4 1;
+#X connect 4 0 39 0;
+#X connect 7 0 11 0;
+#X connect 8 0 7 0;
+#X connect 9 0 7 1;
+#X connect 10 0 11 1;
+#X connect 11 0 45 0;
+#X connect 12 0 16 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 1;
+#X connect 15 0 16 1;
+#X connect 16 0 44 0;
+#X connect 21 0 25 0;
+#X connect 22 0 21 0;
+#X connect 23 0 21 1;
+#X connect 24 0 25 1;
+#X connect 25 0 40 0;
+#X connect 28 0 64 0;
+#X connect 32 0 36 0;
+#X connect 33 0 32 0;
+#X connect 34 0 32 1;
+#X connect 35 0 36 1;
+#X connect 36 0 50 0;
+#X connect 55 0 59 0;
+#X connect 56 0 55 0;
+#X connect 57 0 55 1;
+#X connect 58 0 59 1;
+#X connect 59 0 60 0;
+#X connect 65 0 38 0;
+#X connect 66 0 65 0;
+#X connect 67 0 66 0;
+#X connect 68 0 66 1;
+#X connect 69 0 65 1;
+#X connect 70 0 65 2;
+#X connect 74 0 78 0;
+#X connect 75 0 78 0;
+#X connect 76 0 78 0;
+#X connect 77 0 78 0;
+#X connect 78 0 65 0;
+#X connect 78 1 65 1;
+#X connect 78 2 65 2;
+#X connect 79 0 78 1;
+#X connect 80 0 78 2;
+#X connect 81 0 78 3;
+#X restore 327 48 pd edit;
+#X obj 61 345 print;
+#N canvas 276 200 290 224 once 0;
+#X obj 38 47 t b b f;
+#X msg 56 85 1;
+#X obj 31 108 f 0;
+#X obj 31 131 pack 0 0;
+#X obj 31 156 route 0;
+#X obj 38 24 inlet;
+#X obj 31 180 outlet;
+#X connect 0 0 1 0;
+#X connect 0 1 2 0;
+#X connect 0 2 3 1;
+#X connect 1 0 2 1;
+#X connect 2 0 3 0;
+#X connect 3 0 4 0;
+#X connect 4 0 6 0;
+#X connect 5 0 0 0;
+#X restore 61 322 pd once;
+#X obj 377 110 vsl 15 128 0 127 0 0 ccc ccc empty 20 8 192 8 -262144
+-1 -1 4200 1;
+#X obj 249 87 x_all_guis aaa bbb ccc ddd eee fff ggg hhh iii;
+#X text 218 387 updated for Pd version 0.35;
+#X text 42 388 graz \, austria 2002;
+#X connect 1 0 6 0;
+#X connect 2 0 20 0;
+#X connect 3 0 20 0;
+#X connect 7 0 20 0;
+#X connect 8 0 2 0;
+#X connect 10 0 30 0;
+#X connect 11 0 23 0;
+#X connect 12 0 13 0;
+#X connect 17 0 18 0;
+#X connect 18 0 21 0;
+#X connect 20 0 1 0;
+#X connect 20 0 37 0;
+#X connect 21 0 14 0;
+#X connect 21 0 12 0;
+#X connect 22 0 15 0;
+#X connect 29 0 9 0;
+#X connect 30 0 28 0;
+#X connect 37 0 36 0;
diff --git a/pd/doc/5.reference/vu.pd b/pd/doc/5.reference/vu.pd
new file mode 100644
index 00000000..3c94ba52
--- /dev/null
+++ b/pd/doc/5.reference/vu.pd
@@ -0,0 +1,247 @@
+#N canvas 171 210 549 418 10;
+#X obj 1 1 cnv 8 100 60 empty empty vu 20 20 1 18 -262144 -1109 0;
+#X text 19 363 (c) musil@iem.kug.ac.at;
+#X text 61 376 IEM KUG;
+#X floatatom 177 129 7 -110 20;
+#X text 202 41 click properties to;
+#X text 190 52 modify geometry \, colors \, etc.;
+#X text 49 13 gui-vu-meter-display:;
+#X obj 99 39 tgl 15 1 empty empty empty 8 -8 0 10 -262144 -1 -1 1 1
+;
+#X obj 12 179 vu 16 120 foo7_rcv vu-meter 60 0 64 10 -1 -355 1 0;
+#X floatatom 11 332 6 0 0;
+#X floatatom 22 310 6 0 0;
+#X text 75 309 dB;
+#X text 63 333 dB;
+#X text 71 128 dB;
+#X text 80 148 dB;
+#X text 103 146 peak-level;
+#X text 101 125 rms-level;
+#X text 96 308 peak-level;
+#X text 83 332 rms-level;
+#X text 108 99 <list> of rms \, peak;
+#X obj 177 231 s foo7_rcv;
+#X obj 177 211 pack 0 0;
+#X floatatom 195 150 7 -110 20;
+#X obj 195 191 t b f;
+#X text 236 129 dB;
+#X text 255 151 dB;
+#X text 71 258 UP- \, DOWN- \, LEFT- or RIGHT-key;
+#X text 70 269 for moving selected gui-objects;
+#X floatatom 11 128 7 -110 20;
+#X floatatom 22 149 7 -110 20;
+#N canvas 236 62 699 530 edit 0;
+#X obj 37 222 f;
+#X msg 15 201 bang;
+#X floatatom 53 200 3 50 88;
+#X floatatom 88 222 3 0 37;
+#X obj 37 245 pack 0 0;
+#X text 115 222 y-label;
+#X text 81 200 x-label;
+#X obj 292 313 f;
+#X msg 270 292 bang;
+#X floatatom 308 291 3 -10 10;
+#X floatatom 343 313 3 -10 10;
+#X obj 292 336 pack 0 0;
+#X obj 304 435 f;
+#X msg 282 414 bang;
+#X floatatom 320 413 3 20 140;
+#X floatatom 355 435 3 150 200;
+#X obj 304 458 pack 0 0;
+#X text 336 291 x-delta;
+#X text 370 313 y-delta;
+#X text 348 413 x-position;
+#X text 382 435 y-position;
+#X obj 57 337 f;
+#X msg 35 316 bang;
+#X floatatom 73 315 3 0 2;
+#X floatatom 108 337 3 8 36;
+#X obj 57 360 pack 0 0;
+#X text 101 315 font;
+#X text 137 337 height;
+#X floatatom 471 106 1 0 1;
+#X msg 52 131 \; foo7_rcv color \$1 \$2;
+#X msg 37 270 \; foo7_rcv label_pos \$1 \$2;
+#X msg 57 386 \; foo7_rcv label_font \$1 \$2;
+#X msg 37 427 \; foo7_rcv label blabla;
+#X msg 292 361 \; foo7_rcv delta \$1 \$2;
+#X msg 304 483 \; foo7_rcv pos \$1 \$2;
+#X msg 469 23 \; foo7_rcv receive foo7a_rcv;
+#X msg 469 60 \; foo7a_rcv receive foo7_rcv;
+#X text 492 106 display scale;
+#X msg 471 132 \; foo7_rcv scale \$1;
+#X obj 279 193 f;
+#X msg 257 172 bang;
+#X floatatom 295 171 3 8 50;
+#X floatatom 330 193 3 110 200;
+#X obj 279 216 pack 0 0;
+#X text 323 171 width;
+#X text 357 193 height;
+#X msg 279 241 \; foo7_rcv size \$1 \$2;
+#X msg 37 463 \; foo7_rcv label vu-meter;
+#X obj 52 79 f;
+#X msg 29 31 bang;
+#X floatatom 68 29 3 0 29;
+#X floatatom 103 47 3 0 29;
+#X text 96 29 background;
+#X text 131 48 label-color;
+#X msg 290 25 back;
+#X msg 290 49 label;
+#X msg 252 25 bang;
+#N canvas 15 207 606 448 RGB_____________ 0;
+#X obj 97 56 inlet;
+#X obj 262 53 inlet;
+#X obj 339 55 inlet;
+#X obj 405 56 inlet;
+#X obj 97 270 bang;
+#X msg 77 295 0;
+#X msg 104 295 1;
+#X obj 146 268 bang;
+#X msg 132 295 0;
+#X msg 160 295 1;
+#X obj 265 313 spigot;
+#X obj 312 313 spigot;
+#X obj 249 385 outlet;
+#X text 93 33 select;
+#X text 267 28 red;
+#X text 337 30 green;
+#X text 409 30 blue;
+#X obj 405 102 t b f;
+#X obj 339 160 +;
+#X obj 339 185 t b f;
+#X obj 339 216 +;
+#X obj 296 385 outlet;
+#X obj 28 180 loadbang;
+#X obj 296 361 f;
+#X obj 249 361 f;
+#X obj 262 79 * -65536;
+#X obj 339 80 * -256;
+#X obj 405 80 * -1;
+#X obj 339 247 - 1;
+#X obj 97 135 route back label bang;
+#X obj 235 168 t b b b;
+#X connect 0 0 29 0;
+#X connect 1 0 25 0;
+#X connect 2 0 26 0;
+#X connect 3 0 27 0;
+#X connect 4 0 5 0;
+#X connect 4 0 6 0;
+#X connect 5 0 11 1;
+#X connect 6 0 10 1;
+#X connect 7 0 8 0;
+#X connect 7 0 9 0;
+#X connect 8 0 10 1;
+#X connect 9 0 11 1;
+#X connect 10 0 24 1;
+#X connect 11 0 23 1;
+#X connect 17 0 18 0;
+#X connect 17 1 18 1;
+#X connect 18 0 19 0;
+#X connect 19 0 20 0;
+#X connect 19 1 20 1;
+#X connect 20 0 28 0;
+#X connect 22 0 6 0;
+#X connect 23 0 21 0;
+#X connect 24 0 12 0;
+#X connect 25 0 20 0;
+#X connect 26 0 18 0;
+#X connect 27 0 17 0;
+#X connect 28 0 11 0;
+#X connect 28 0 10 0;
+#X connect 29 0 4 0;
+#X connect 29 1 7 0;
+#X connect 29 2 30 0;
+#X connect 30 0 24 0;
+#X connect 30 1 23 0;
+#X connect 30 2 28 0;
+#X restore 290 86 pd RGB_____________;
+#X floatatom 332 55 3 0 255;
+#X floatatom 375 55 3 0 255;
+#X floatatom 418 56 3 0 255;
+#X text 39 3 preset-colors;
+#X text 301 0 RGB-colors;
+#X text 332 37 red;
+#X text 368 36 green;
+#X text 416 36 blue;
+#X obj 52 104 pack 0 0;
+#X connect 0 0 4 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 1;
+#X connect 3 0 4 1;
+#X connect 4 0 30 0;
+#X connect 7 0 11 0;
+#X connect 8 0 7 0;
+#X connect 9 0 7 1;
+#X connect 10 0 11 1;
+#X connect 11 0 33 0;
+#X connect 12 0 16 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 1;
+#X connect 15 0 16 1;
+#X connect 16 0 34 0;
+#X connect 21 0 25 0;
+#X connect 22 0 21 0;
+#X connect 23 0 21 1;
+#X connect 24 0 25 1;
+#X connect 25 0 31 0;
+#X connect 28 0 38 0;
+#X connect 39 0 43 0;
+#X connect 40 0 39 0;
+#X connect 41 0 39 1;
+#X connect 42 0 43 1;
+#X connect 43 0 46 0;
+#X connect 48 0 66 0;
+#X connect 49 0 48 0;
+#X connect 50 0 48 1;
+#X connect 51 0 66 1;
+#X connect 54 0 57 0;
+#X connect 55 0 57 0;
+#X connect 56 0 57 0;
+#X connect 57 0 66 0;
+#X connect 57 1 66 1;
+#X connect 58 0 57 1;
+#X connect 59 0 57 2;
+#X connect 60 0 57 3;
+#X connect 66 0 29 0;
+#X restore 313 188 pd edit;
+#N canvas 147 336 290 278 source 0;
+#X obj 40 95 random 102;
+#X obj 40 171 - 101;
+#X obj 40 205 pack 0 0;
+#X obj 40 45 metro 300;
+#X obj 40 69 t b b;
+#X obj 133 95 random 20;
+#X obj 40 117 t f f;
+#X obj 91 147 +;
+#X obj 91 172 - 101;
+#X obj 40 21 inlet;
+#X obj 40 230 outlet;
+#X connect 0 0 6 0;
+#X connect 1 0 2 0;
+#X connect 2 0 10 0;
+#X connect 3 0 4 0;
+#X connect 4 0 0 0;
+#X connect 4 1 5 0;
+#X connect 5 0 7 1;
+#X connect 6 0 1 0;
+#X connect 6 1 7 0;
+#X connect 7 0 8 0;
+#X connect 8 0 2 1;
+#X connect 9 0 3 0;
+#X restore 99 62 pd source;
+#X obj 263 94 s fff;
+#X obj 186 302 x_all_guis aaa bbb ccc ddd eee fff ggg hhh iii;
+#X text 210 386 updated for Pd version 0.35;
+#X text 36 388 graz \, austria 2002;
+#X connect 3 0 21 0;
+#X connect 7 0 31 0;
+#X connect 8 0 9 0;
+#X connect 8 1 10 0;
+#X connect 21 0 20 0;
+#X connect 22 0 23 0;
+#X connect 23 0 21 0;
+#X connect 23 1 21 1;
+#X connect 28 0 8 0;
+#X connect 29 0 8 1;
+#X connect 31 0 8 0;
+#X connect 31 0 32 0;
diff --git a/pd/doc/5.reference/wrap~.pd b/pd/doc/5.reference/wrap~.pd
new file mode 100644
index 00000000..81681f30
--- /dev/null
+++ b/pd/doc/5.reference/wrap~.pd
@@ -0,0 +1,26 @@
+#N canvas 182 132 703 319 12;
+#X obj 58 220 metro 500;
+#X obj 58 195 r metro;
+#X msg 575 106 \; metro 0;
+#X msg 574 48 \; pd dsp 1 \; metro 1;
+#X floatatom 42 121 0 0 0;
+#X floatatom 42 277 0 0 0;
+#X text 443 271 updated for Pd version 0.33;
+#X obj 574 21 loadbang;
+#X obj 42 244 snapshot~;
+#X obj 42 147 sig~;
+#X obj 36 16 wrap~;
+#X text 93 16 - remainder modulo 1;
+#X text 18 45 wrap~ gives the difference between the input and the
+largest integer not exceeding it (for positive numbers this is the
+fractional part).;
+#X obj 42 171 wrap~;
+#X text 127 123 <-- shift-drag here to get non-integers to try;
+#X connect 0 0 8 0;
+#X connect 1 0 0 0;
+#X connect 1 0 0 0;
+#X connect 4 0 9 0;
+#X connect 7 0 3 0;
+#X connect 8 0 5 0;
+#X connect 9 0 13 0;
+#X connect 13 0 8 0;
diff --git a/pd/doc/5.reference/writesf~.pd b/pd/doc/5.reference/writesf~.pd
new file mode 100644
index 00000000..053e0dee
--- /dev/null
+++ b/pd/doc/5.reference/writesf~.pd
@@ -0,0 +1,27 @@
+#N canvas 388 177 450 290 10;
+#X msg 365 19 \; pd dsp 1;
+#X msg 141 133 print;
+#X msg 56 50 bang;
+#X msg 140 85 start;
+#X msg 142 111 stop;
+#X obj 189 133 osc~ 441;
+#X obj 56 81 del 1000;
+#X text 41 9 writesf~ -- write audio signals to a soundfile;
+#X text 269 276 updated for Pd version 0.32.;
+#X text 205 158 creation argument is number of channels;
+#X text 206 169 (1 to 64).;
+#X text 250 58 create a new soundfile;
+#X text 181 85 start streaming audio;
+#X text 176 111 stop streaming audio;
+#X text 27 186 writesf~ creates a subthread whose task is to write audio streams to disk. You need not provide any access time between "open" and "start" \, but between "stop" and the next "open" you must give the object time to flush all the output to disk.;
+#X text 27 240 writesf~ works best with pd in "realtime" mode (pd -rt). Available for linux only at the moment...;
+#X obj 132 158 writesf~ 2;
+#X msg 136 60 open /tmp/foo.wav;
+#X connect 1 0 16 0;
+#X connect 2 0 3 0;
+#X connect 2 0 6 0;
+#X connect 3 0 16 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 4 0;
+#X connect 17 0 16 0;
diff --git a/pd/doc/5.reference/x_all_guis.pd b/pd/doc/5.reference/x_all_guis.pd
new file mode 100644
index 00000000..3c18031b
--- /dev/null
+++ b/pd/doc/5.reference/x_all_guis.pd
@@ -0,0 +1,20 @@
+#N canvas 209 342 290 271 10;
+#X obj 23 31 bng 15 250 50 532480 \$1 \$1 empty 20 8 192 8 -262144
+-1 -1;
+#X obj 23 63 tgl 15 1.06496e+06 \$2 \$2 empty 20 8 192 8 -262144 -1
+-1 0 1;
+#X obj 22 95 vsl 15 128 0 127 0 1.59744e+06 \$3 \$3 empty 20 8 192
+8 -262144 -1 -1 0 1;
+#X obj 65 30 hsl 128 15 0 127 0 2.12992e+06 \$4 \$4 empty 20 8 192
+8 -262144 -1 -1 0 1;
+#X obj 63 63 hdl 15 1 2.6624e+06 8 \$5 \$5 empty 20 8 192 8 -262144
+-1 -1 0;
+#X obj 62 99 vu 15 120 \$6 empty 35 8 64 8 -66577 -1 1 49152;
+#X obj 115 99 cnv 15 100 60 \$7 \$7 \$7 20 12 917696 14 -233017 -66577
+3.72736e+06;
+#X obj 41 308 inlet;
+#X obj 41 334 outlet;
+#X obj 227 30 vdl 15 1 4.79232e+06 8 \$9 \$9 empty 20 8 192 8 -262144
+-1 -1 0;
+#X obj 116 176 nbx 5 14 -1e+37 1e+37 0 4.25984e+06 \$8 \$8 empty 45
+7 192 10 -262144 -1 -1 0;
diff --git a/pd/doc/6.externs/0.README.txt b/pd/doc/6.externs/0.README.txt
new file mode 100644
index 00000000..3b130116
--- /dev/null
+++ b/pd/doc/6.externs/0.README.txt
@@ -0,0 +1,9 @@
+EXTERNAL OBJECTS in Pd.
+
+Here are the sources for three simple external objects in Pd.
+To compile, type "make pd_linux", "nmake pd_nt", "make pd_irix5" or "make
+pd_irix6".
+
+The objects "foo1" and "foo2" are intended as very simple control objects, and
+"dspobj" is a tilde object.
+
diff --git a/pd/doc/6.externs/dspobj~.c b/pd/doc/6.externs/dspobj~.c
new file mode 100644
index 00000000..a8841f21
--- /dev/null
+++ b/pd/doc/6.externs/dspobj~.c
@@ -0,0 +1,49 @@
+#include "m_pd.h"
+#ifdef NT
+#pragma warning( disable : 4244 )
+#pragma warning( disable : 4305 )
+#endif
+
+/* ------------------------ dspobj~ ----------------------------- */
+
+/* tilde object to take absolute value. */
+
+static t_class *dspobj_class;
+
+typedef struct _dspobj
+{
+ t_object x_obj;
+} t_dspobj;
+
+static t_int *dspobj_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ while (n--)
+ {
+ float f = *(in++);
+ *out++ = (f > 0 ? f : -f);
+ }
+ return (w+4);
+}
+
+static void dspobj_dsp(t_dspobj *x, t_signal **sp)
+{
+ dsp_add(dspobj_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+static void *dspobj_new(void)
+{
+ t_dspobj *x = (t_dspobj *)pd_new(dspobj_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ return (x);
+}
+
+void dspobj_tilde_setup(void)
+{
+ dspobj_class = class_new(gensym("dspobj~"), (t_newmethod)dspobj_new, 0,
+ sizeof(t_dspobj), 0, A_DEFFLOAT, 0);
+ class_addmethod(dspobj_class, nullfn, gensym("signal"), 0);
+ class_addmethod(dspobj_class, (t_method)dspobj_dsp, gensym("dsp"), 0);
+}
diff --git a/pd/doc/6.externs/foo1.c b/pd/doc/6.externs/foo1.c
new file mode 100644
index 00000000..48d0d344
--- /dev/null
+++ b/pd/doc/6.externs/foo1.c
@@ -0,0 +1,37 @@
+/* code for foo1 pd class */
+
+#include "m_pd.h"
+
+typedef struct foo1
+{
+ t_object t_ob;
+} t_foo1;
+
+void foo1_float(t_foo1 *x, t_floatarg f)
+{
+ post("foo1: %f", f);
+}
+
+void foo1_rats(t_foo1 *x)
+{
+ post("foo1: rats");
+}
+
+t_class *foo1_class;
+
+void *foo1_new(void)
+{
+ t_foo1 *x = (t_foo1 *)pd_new(foo1_class);
+ post("foo1_new");
+ return (void *)x;
+}
+
+void foo1_setup(void)
+{
+ post("foo1_setup");
+ foo1_class = class_new(gensym("foo1"), (t_newmethod)foo1_new, 0,
+ sizeof(t_foo1), 0, 0);
+ class_addmethod(foo1_class, (t_method)foo1_rats, gensym("rats"), 0);
+ class_addfloat(foo1_class, foo1_float);
+}
+
diff --git a/pd/doc/6.externs/foo2.c b/pd/doc/6.externs/foo2.c
new file mode 100644
index 00000000..e68ed996
--- /dev/null
+++ b/pd/doc/6.externs/foo2.c
@@ -0,0 +1,49 @@
+/* code for foo2 pd class */
+
+#include "m_pd.h"
+
+typedef struct foo2
+{
+ t_object t_ob;
+} t_foo2;
+
+void foo2_float(t_foo2 *x, t_floatarg f)
+{
+ post("foo2: %f", f);
+}
+
+void foo2_rats(t_foo2 *x)
+{
+ post("foo2: rats");
+}
+
+void foo2_ft1(t_foo2 *x, t_floatarg g)
+{
+ post("ft1: %f", g);
+}
+
+void foo2_free(void)
+{
+ post("foo2_free");
+}
+
+t_class *foo2_class;
+
+void *foo2_new(void)
+{
+ t_foo2 *x = (t_foo2 *)pd_new(foo2_class);
+ inlet_new(&x->t_ob, &x->t_ob.ob_pd, gensym("float"), gensym("ft1"));
+ post("foo2_new");
+ return (void *)x;
+}
+
+void foo2_setup(void)
+{
+ post("foo2_setup");
+ foo2_class = class_new(gensym("foo2"), (t_newmethod)foo2_new,
+ (t_method)foo2_free, sizeof(t_foo2), 0, 0);
+ class_addmethod(foo2_class, (t_method)foo2_rats, gensym("rats"), 0);
+ class_addmethod(foo2_class, (t_method)foo2_ft1, gensym("ft1"), A_FLOAT, 0);
+ class_addfloat(foo2_class, foo2_float);
+}
+
diff --git a/pd/doc/6.externs/makefile b/pd/doc/6.externs/makefile
new file mode 100644
index 00000000..f807ba0b
--- /dev/null
+++ b/pd/doc/6.externs/makefile
@@ -0,0 +1,75 @@
+current:
+ echo make pd_linux, pd_nt, pd_irix5, or pd_irix6
+
+clean: ; rm -f *.pd_linux *.o
+
+# ----------------------- NT -----------------------
+
+pd_nt: foo1.dll foo2.dll dspobj~.dll
+
+.SUFFIXES: .dll
+
+PDNTCFLAGS = /W3 /WX /DNT /DPD /nologo
+VC="C:\Program Files\Microsoft Visual Studio\Vc98"
+
+PDNTINCLUDE = /I. /I\tcl\include /I\ftp\pd\src /I$(VC)\include
+
+PDNTLDIR = $(VC)\lib
+PDNTLIB = $(PDNTLDIR)\libc.lib \
+ $(PDNTLDIR)\oldnames.lib \
+ $(PDNTLDIR)\kernel32.lib \
+ \ftp\pd\bin\pd.lib
+
+.c.dll:
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c
+ link /dll /export:$*_setup $*.obj $(PDNTLIB)
+
+# ----------------------- IRIX 5.x -----------------------
+
+pd_irix5: foo1.pd_irix5 foo2.pd_irix5 dspobj~.pd_irix5
+
+.SUFFIXES: .pd_irix5
+
+SGICFLAGS5 = -o32 -DPD -DUNIX -DIRIX -O2
+
+
+SGIINCLUDE = -I../../src/
+
+.c.pd_irix5:
+ cc $(SGICFLAGS5) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -elf -shared -rdata_shared -o $*.pd_irix5 $*.o
+ rm $*.o
+
+# ----------------------- IRIX 6.x -----------------------
+
+pd_irix6: foo1.pd_irix6 foo2.pd_irix6 dspobj~.pd_irix6
+
+.SUFFIXES: .pd_irix6
+
+SGICFLAGS6 = -n32 -DPD -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \
+ -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \
+ -Ofast=ip32
+
+.c.pd_irix6:
+ cc $(SGICFLAGS6) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -IPA -n32 -shared -rdata_shared -o $*.pd_irix6 $*.o
+ rm $*.o
+
+# ----------------------- LINUX i386 -----------------------
+
+pd_linux: foo1.pd_linux foo2.pd_linux dspobj~.pd_linux
+
+.SUFFIXES: .pd_linux
+
+LINUXCFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer \
+ -Wall -W -Wshadow -Wstrict-prototypes -Werror \
+ -Wno-unused -Wno-parentheses -Wno-switch
+
+LINUXINCLUDE = -I../../src
+
+.c.pd_linux:
+ cc $(LINUXCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c
+ ld -export_dynamic -shared -o $*.pd_linux $*.o -lc -lm
+ strip --strip-unneeded $*.pd_linux
+ rm $*.o
+
diff --git a/pd/doc/6.externs/test-dspobj~.pd b/pd/doc/6.externs/test-dspobj~.pd
new file mode 100644
index 00000000..4d1030b3
--- /dev/null
+++ b/pd/doc/6.externs/test-dspobj~.pd
@@ -0,0 +1,11 @@
+#N canvas 0 0 335 239 10;
+#X obj 90 124 dspobj~;
+#X obj 90 96 sig~ 0;
+#X msg 106 149 bang;
+#X obj 90 177 print~;
+#X floatatom 89 71;
+#X msg 202 37 \; pd dsp 1;
+#X connect 0 0 3 0;
+#X connect 1 0 0 0;
+#X connect 2 0 3 0;
+#X connect 4 0 1 0;
diff --git a/pd/doc/6.externs/test-foo1.pd b/pd/doc/6.externs/test-foo1.pd
new file mode 100644
index 00000000..280be821
--- /dev/null
+++ b/pd/doc/6.externs/test-foo1.pd
@@ -0,0 +1,6 @@
+#N canvas 68 38 269 168 10;
+#X obj 68 90 foo1;
+#X msg 68 52 5;
+#X msg 100 52 rats;
+#X connect 1 0 0 0;
+#X connect 2 0 0 0;
diff --git a/pd/doc/6.externs/test-foo2.pd b/pd/doc/6.externs/test-foo2.pd
new file mode 100644
index 00000000..be62e140
--- /dev/null
+++ b/pd/doc/6.externs/test-foo2.pd
@@ -0,0 +1,8 @@
+#N canvas 171 53 269 164 10;
+#X msg 100 52 rats;
+#X obj 68 90 foo2;
+#X msg 155 55 7;
+#X msg 68 52 4;
+#X connect 0 0 1 0;
+#X connect 2 0 1 1;
+#X connect 3 0 1 0;
diff --git a/pd/doc/7.stuff/audio-playpen/1_DSP_INTRO.pd b/pd/doc/7.stuff/audio-playpen/1_DSP_INTRO.pd
new file mode 100644
index 00000000..17ee73c6
--- /dev/null
+++ b/pd/doc/7.stuff/audio-playpen/1_DSP_INTRO.pd
@@ -0,0 +1,84 @@
+#N canvas 10 10 551 484 10;
+#X text 41 3 ABSTRACTIONS IN THIS DIRECTORY;
+#X text 117 22 sampler;
+#X text 117 36 gain control;
+#X text 117 50 ring mod;
+#X text 21 142 OTHER OBJECTS YOU WILL NEED;
+#X obj 21 171 dac~;
+#X text 54 173 output;
+#X text 21 156 audio:;
+#X obj 21 188 adc~;
+#X text 54 187 input;
+#X obj 21 205 +~;
+#X obj 45 205 -~;
+#X obj 21 222 *~;
+#X obj 45 222 /~;
+#X text 71 215 arithmetic;
+#X obj 28 280 bp~;
+#X text 56 278 bandpass filter;
+#X text 58 293 bandpass with "audio";
+#X text 55 301 control for second input;
+#X obj 28 297 vcf~;
+#X obj 261 37 metro;
+#X text 294 40 repeated message;
+#X obj 253 157 float;
+#X text 292 156 store numbers;
+#X obj 252 398 random;
+#X text 298 397 random;
+#X obj 253 174 pack;
+#X text 292 170 combines numbers;
+#X obj 253 191 unpack;
+#X text 297 196 opposite;
+#X obj 253 208 trigger;
+#X text 302 215 control order of execution;
+#X text 261 22 TIME;
+#X obj 261 54 del;
+#X text 294 54 wait then bang;
+#X obj 261 71 timer;
+#X text 294 68 measure time intervals;
+#X text 278 142 GLUE;
+#X obj 253 225 select;
+#X text 302 229 test values;
+#X obj 253 259 moses;
+#X text 302 257 parting the waters;
+#X obj 253 242 route;
+#X text 302 243 heavy select;
+#X obj 252 415 line;
+#X text 291 412 control ramp generator;
+#X obj 23 324 delwrite~;
+#X obj 23 341 delread~;
+#X obj 23 359 vd~;
+#X text 84 323 declare a delay line;
+#X text 83 340 read a delay line;
+#X text 282 303 UNIT CONVERSION;
+#X obj 258 324 mtof;
+#X obj 287 324 ftom;
+#X obj 258 341 dbtorms;
+#X obj 305 341 rmstodb;
+#X obj 258 358 dbtopow;
+#X obj 305 358 powtodb;
+#X text 357 323 frequency to MIDI;
+#X text 355 339 db to amplitude;
+#X text 356 356 db to power;
+#N canvas 0 0 600 400 /SUBPATCH/ 0;
+#X restore 64 405 pd;
+#X text 100 404 subpatch;
+#X obj 64 422 inlet;
+#X obj 100 419 inlet~;
+#X obj 64 439 outlet;
+#X obj 105 436 outlet~;
+#X obj 28 246 hip~;
+#X text 55 245 high pass filter;
+#X obj 28 263 lop~;
+#X text 55 263 low pass filter;
+#X text 257 101 ARITHMETIC;
+#X text 117 64 control-to-signal;
+#X text 116 94 delay loop;
+#X text 116 108 variable delay loop;
+#X obj 41 36 qgain;
+#X obj 41 53 qring;
+#X obj 41 70 qslew;
+#X obj 41 89 qdelay;
+#X obj 41 18 qsample ...;
+#X obj 42 108 qvd ...;
+#X text 50 359 variable time delay read;
diff --git a/pd/doc/7.stuff/audio-playpen/2_sampler.pd b/pd/doc/7.stuff/audio-playpen/2_sampler.pd
new file mode 100644
index 00000000..327e4116
--- /dev/null
+++ b/pd/doc/7.stuff/audio-playpen/2_sampler.pd
@@ -0,0 +1,33 @@
+#N canvas 8 0 476 273 10;
+#X floatatom 108 154;
+#X floatatom 282 62;
+#X floatatom 244 62;
+#X obj 81 127 qsample ---------------;
+#X msg 113 60 record;
+#X msg 158 60 play;
+#X text 184 42 transpose;
+#X floatatom 197 60;
+#X text 242 47 start;
+#X text 280 48 length;
+#X obj 81 203 dac~;
+#X obj 81 176 qgain;
+#X text 139 155 loudnesss;
+#X text 225 151 TO USE ME:;
+#X text 243 167 turn DSP on (ctrl + "/");
+#X text 244 192 turn loudness to 80 (say);
+#X text 244 221 hit "play" button.;
+#X obj 81 59 adc~;
+#X msg 227 128 read;
+#X text 242 179 read a sample;
+#X text 244 207 set length to 2000;
+#X connect 0 0 11 1;
+#X connect 1 0 3 5;
+#X connect 2 0 3 4;
+#X connect 3 0 11 0;
+#X connect 4 0 3 1;
+#X connect 5 0 3 2;
+#X connect 7 0 3 3;
+#X connect 11 0 10 0;
+#X connect 11 0 10 1;
+#X connect 17 0 3 0;
+#X connect 18 0 3 6;
diff --git a/pd/doc/7.stuff/audio-playpen/3_filter_and_ring.pd b/pd/doc/7.stuff/audio-playpen/3_filter_and_ring.pd
new file mode 100644
index 00000000..a994cc85
--- /dev/null
+++ b/pd/doc/7.stuff/audio-playpen/3_filter_and_ring.pd
@@ -0,0 +1,64 @@
+#N canvas 4 21 576 397 10;
+#X floatatom 221 271;
+#X floatatom 209 250;
+#X floatatom 78 269;
+#X floatatom 68 250;
+#X floatatom 104 217;
+#X floatatom 169 68;
+#X floatatom 83 314;
+#X floatatom 298 149;
+#X floatatom 297 83;
+#X obj 61 181 qsample ---------------;
+#X msg 101 114 record;
+#X msg 169 113 play;
+#X text 339 63 transpose;
+#X floatatom 297 62;
+#X text 339 81 start;
+#X text 339 152 length;
+#X obj 58 371 dac~;
+#X obj 58 334 qgain;
+#X text 324 189 read a file;
+#X text 335 241 TO USE ME:;
+#X text 371 272 turn DSP on (ctrl + "/");
+#X text 370 297 read the sample;
+#X text 373 322 turn loudness to 80 (say);
+#X text 373 346 hit "play" button.;
+#X obj 61 113 adc~;
+#X obj 169 90 metro 50;
+#X floatatom 212 67;
+#X obj 63 213 qring;
+#X obj 297 112 loadbang;
+#X msg 298 131 2000;
+#X obj 59 289 vcf~;
+#X obj 200 296 vcf~;
+#X text 118 315 amp 1;
+#X floatatom 224 317;
+#X obj 199 337 qgain;
+#X text 259 318 amp 2;
+#X msg 292 190 read;
+#X connect 0 0 31 2;
+#X connect 1 0 31 1;
+#X connect 2 0 30 2;
+#X connect 3 0 30 1;
+#X connect 4 0 27 1;
+#X connect 5 0 25 0;
+#X connect 6 0 17 1;
+#X connect 7 0 9 5;
+#X connect 8 0 9 4;
+#X connect 9 0 27 0;
+#X connect 10 0 9 1;
+#X connect 11 0 9 2;
+#X connect 13 0 9 3;
+#X connect 17 0 16 0;
+#X connect 24 0 9 0;
+#X connect 25 0 11 0;
+#X connect 26 0 25 1;
+#X connect 27 0 30 0;
+#X connect 27 0 31 0;
+#X connect 28 0 29 0;
+#X connect 29 0 7 0;
+#X connect 30 0 17 0;
+#X connect 31 0 34 0;
+#X connect 33 0 34 1;
+#X connect 34 0 16 1;
+#X connect 36 0 9 6;
diff --git a/pd/doc/7.stuff/audio-playpen/4_more_filters.pd b/pd/doc/7.stuff/audio-playpen/4_more_filters.pd
new file mode 100644
index 00000000..aaa81f34
--- /dev/null
+++ b/pd/doc/7.stuff/audio-playpen/4_more_filters.pd
@@ -0,0 +1,55 @@
+#N canvas 55 0 373 335 10;
+#X floatatom 163 156;
+#X floatatom 220 175;
+#X floatatom 98 154;
+#X floatatom 118 221;
+#X floatatom 151 33;
+#X floatatom 134 266;
+#X floatatom 234 99;
+#X floatatom 235 75;
+#X obj 81 127 qsample ---------------;
+#X msg 111 93 record;
+#X msg 156 93 play;
+#X text 279 49 transpose;
+#X floatatom 234 51;
+#X text 279 73 start;
+#X text 280 97 length;
+#X obj 92 292 dac~;
+#X obj 94 264 qgain;
+#X text 163 269 loudnesss;
+#X obj 81 93 adc~;
+#X obj 153 60 metro 50;
+#X floatatom 194 29;
+#X obj 93 243 qring;
+#X obj 77 212 vcf~;
+#X obj 282 23 random 2000;
+#X msg 257 135 read;
+#X obj 98 174 qring;
+#X floatatom 130 155;
+#X obj 163 175 sig~;
+#X obj 252 241 env~ 16384;
+#X floatatom 258 270;
+#X connect 0 0 27 0;
+#X connect 1 0 22 2;
+#X connect 2 0 25 0;
+#X connect 3 0 21 1;
+#X connect 4 0 19 0;
+#X connect 5 0 16 1;
+#X connect 6 0 8 5;
+#X connect 7 0 8 4;
+#X connect 8 0 22 0;
+#X connect 9 0 8 1;
+#X connect 10 0 8 2;
+#X connect 12 0 8 3;
+#X connect 16 0 15 0;
+#X connect 16 0 15 1;
+#X connect 18 0 8 0;
+#X connect 19 0 10 0;
+#X connect 20 0 19 1;
+#X connect 21 0 16 0;
+#X connect 22 0 21 0;
+#X connect 24 0 8 6;
+#X connect 25 0 22 1;
+#X connect 26 0 25 1;
+#X connect 27 0 22 1;
+#X connect 28 0 29 0;
diff --git a/pd/doc/7.stuff/audio-playpen/5_delay.pd b/pd/doc/7.stuff/audio-playpen/5_delay.pd
new file mode 100644
index 00000000..63006b8d
--- /dev/null
+++ b/pd/doc/7.stuff/audio-playpen/5_delay.pd
@@ -0,0 +1,55 @@
+#N canvas 34 147 523 368 10;
+#X floatatom 58 216;
+#X obj 7 235 qgain;
+#X floatatom 150 176;
+#X floatatom 146 151;
+#X floatatom 151 33;
+#X floatatom 148 220;
+#X floatatom 234 99;
+#X floatatom 235 75;
+#X obj 84 123 qsample ---------------;
+#X msg 111 93 record;
+#X msg 156 93 play;
+#X text 279 49 transpose;
+#X floatatom 234 51;
+#X text 279 73 start;
+#X text 280 97 length;
+#X obj 23 272 dac~;
+#X obj 97 239 qgain;
+#X text 261 220 TO USE ME:;
+#X text 279 236 turn DSP on (ctrl + "/");
+#X text 279 248 read the sample;
+#X text 280 261 turn loudness to 80 (say);
+#X text 280 273 hit "play" button.;
+#X obj 81 93 adc~;
+#X obj 153 60 metro 50;
+#X floatatom 203 29;
+#X obj 96 199 qdelay;
+#X text 47 188 direct;
+#X text 49 201 gain;
+#X text 179 220 delay gain;
+#X text 173 152 delay time;
+#X text 177 176 recirculation (0-100);
+#X text 255 294 the direct signal comes out;
+#X text 255 304 channel 1 and the delays out;
+#X text 255 315 channel 2;
+#X msg 257 135 read;
+#X connect 0 0 1 1;
+#X connect 1 0 15 0;
+#X connect 2 0 25 2;
+#X connect 3 0 25 1;
+#X connect 4 0 23 0;
+#X connect 5 0 16 1;
+#X connect 6 0 8 5;
+#X connect 7 0 8 4;
+#X connect 8 0 1 0;
+#X connect 8 0 25 0;
+#X connect 9 0 8 1;
+#X connect 10 0 8 2;
+#X connect 12 0 8 3;
+#X connect 16 0 15 1;
+#X connect 22 0 8 0;
+#X connect 23 0 10 0;
+#X connect 24 0 23 1;
+#X connect 25 0 16 0;
+#X connect 34 0 8 6;
diff --git a/pd/doc/7.stuff/audio-playpen/6_flanger.pd b/pd/doc/7.stuff/audio-playpen/6_flanger.pd
new file mode 100644
index 00000000..cf927c32
--- /dev/null
+++ b/pd/doc/7.stuff/audio-playpen/6_flanger.pd
@@ -0,0 +1,71 @@
+#N canvas 42 0 531 395 10;
+#X floatatom 301 189;
+#X floatatom 237 154;
+#X floatatom 59 241;
+#X obj 8 260 qgain;
+#X floatatom 147 223;
+#X floatatom 118 148;
+#X floatatom 151 33;
+#X floatatom 149 244;
+#X floatatom 273 87;
+#X floatatom 330 85;
+#X obj 84 123 qsample ---------------;
+#X msg 111 93 record;
+#X msg 156 93 play;
+#X text 192 72 transpose;
+#X floatatom 193 86;
+#X text 374 83 start;
+#X text 268 73 length;
+#X obj 24 297 dac~;
+#X obj 98 263 qgain;
+#X obj 81 93 adc~;
+#X obj 153 60 metro 50;
+#X floatatom 202 41;
+#X text 48 213 direct;
+#X text 50 225 gain;
+#X text 180 244 delay gain;
+#X text 156 147 delay time;
+#X text 173 223 recirculation (0-100);
+#X obj 97 223 qvd;
+#X obj 116 170 qslew;
+#X obj 237 190 qring;
+#X obj 237 172 sig~;
+#X text 266 154 osc depth;
+#X text 327 188 osc speed;
+#X obj 202 23 r metro-time;
+#X msg 385 191 \; metro-on 0 \; metro-time 100;
+#X obj 152 7 r metro-on;
+#X msg 384 234 \; metro-on 1 \; metro-time 50;
+#X obj 329 37 r start-time;
+#X obj 330 61 line;
+#X msg 374 280 \; start-time 0;
+#X msg 288 317 \; start-time 0 \, 2000 10000;
+#X msg 289 131 read;
+#X connect 0 0 29 1;
+#X connect 1 0 30 0;
+#X connect 2 0 3 1;
+#X connect 3 0 17 0;
+#X connect 4 0 27 2;
+#X connect 5 0 28 0;
+#X connect 6 0 20 0;
+#X connect 7 0 18 1;
+#X connect 8 0 10 5;
+#X connect 9 0 10 4;
+#X connect 10 0 3 0;
+#X connect 10 0 27 0;
+#X connect 11 0 10 1;
+#X connect 12 0 10 2;
+#X connect 14 0 10 3;
+#X connect 18 0 17 0;
+#X connect 19 0 10 0;
+#X connect 20 0 12 0;
+#X connect 21 0 20 1;
+#X connect 27 0 18 0;
+#X connect 28 0 27 1;
+#X connect 29 0 27 1;
+#X connect 30 0 29 0;
+#X connect 33 0 21 0;
+#X connect 35 0 6 0;
+#X connect 37 0 38 0;
+#X connect 38 0 9 0;
+#X connect 41 0 10 6;
diff --git a/pd/doc/7.stuff/audio-playpen/README.txt b/pd/doc/7.stuff/audio-playpen/README.txt
new file mode 100644
index 00000000..e6a94951
--- /dev/null
+++ b/pd/doc/7.stuff/audio-playpen/README.txt
@@ -0,0 +1,9 @@
+The "audio playpen" is intended as a collection of analog-style modules
+for people who just want to patch stuff together without worrying much
+about how Pd works. A fair amount of work would be needed to make this into
+a more usable package than it is now. At the moment it's unclear what
+should be the relationship between this collection, the "examples," and
+the "tools".
+
+At the very least there should be oscillators and envelope generators here,
+and audio-rate db/rms/dB, mtof, ftom conversion...
diff --git a/pd/doc/7.stuff/audio-playpen/qdelay.pd b/pd/doc/7.stuff/audio-playpen/qdelay.pd
new file mode 100644
index 00000000..c22edc3a
--- /dev/null
+++ b/pd/doc/7.stuff/audio-playpen/qdelay.pd
@@ -0,0 +1,34 @@
+#N canvas 94 85 429 238 10;
+#X floatatom 252 62;
+#X obj 252 153 line~;
+#X obj 252 134 pack 0 100;
+#X obj 232 180 *~;
+#X obj 252 44 inlet;
+#X obj 106 52 inlet;
+#X obj 26 78 inlet~;
+#X floatatom 106 70;
+#X obj 22 176 outlet~;
+#X text 20 61 audio in;
+#X text 22 193 audio out;
+#X text 220 11 recirculation;
+#X text 92 23 delay time;
+#X obj 106 88 delread~ qdelay1;
+#X obj 120 210 delwrite~ qdelay1 2000;
+#X obj 252 116 * 0.01;
+#X text 228 27 (0-100);
+#X text 100 35 (msec);
+#X obj 252 80 max 0;
+#X obj 252 98 min 100;
+#X connect 0 0 18 0;
+#X connect 1 0 3 1;
+#X connect 2 0 1 0;
+#X connect 3 0 14 0;
+#X connect 4 0 0 0;
+#X connect 5 0 7 0;
+#X connect 6 0 14 0;
+#X connect 7 0 13 0;
+#X connect 13 0 3 0;
+#X connect 13 0 8 0;
+#X connect 15 0 2 0;
+#X connect 18 0 19 0;
+#X connect 19 0 15 0;
diff --git a/pd/doc/7.stuff/audio-playpen/qgain.pd b/pd/doc/7.stuff/audio-playpen/qgain.pd
new file mode 100644
index 00000000..20f228e8
--- /dev/null
+++ b/pd/doc/7.stuff/audio-playpen/qgain.pd
@@ -0,0 +1,14 @@
+#N canvas 236 128 179 191 10;
+#X obj 89 112 line~;
+#X obj 89 93 pack 0 100;
+#X obj 89 55 inlet;
+#X obj 89 74 dbtorms;
+#X obj 30 67 inlet~;
+#X obj 30 118 outlet~;
+#X obj 30 86 *~;
+#X connect 0 0 6 1;
+#X connect 1 0 0 0;
+#X connect 2 0 3 0;
+#X connect 3 0 1 0;
+#X connect 4 0 6 0;
+#X connect 6 0 5 0;
diff --git a/pd/doc/7.stuff/audio-playpen/qring.pd b/pd/doc/7.stuff/audio-playpen/qring.pd
new file mode 100644
index 00000000..25b99c2e
--- /dev/null
+++ b/pd/doc/7.stuff/audio-playpen/qring.pd
@@ -0,0 +1,14 @@
+#N canvas 163 135 216 211 10;
+#X obj 112 91 inlet;
+#X obj 54 93 inlet~;
+#X obj 55 117 *~;
+#X obj 54 142 outlet~;
+#X text 54 76 audio in;
+#X text 49 173 audio out;
+#X text 117 59 modulation;
+#X text 117 72 frequency;
+#X obj 112 112 osc~;
+#X connect 0 0 8 0;
+#X connect 1 0 2 0;
+#X connect 2 0 3 0;
+#X connect 8 0 2 1;
diff --git a/pd/doc/7.stuff/audio-playpen/qsample.pd b/pd/doc/7.stuff/audio-playpen/qsample.pd
new file mode 100644
index 00000000..bc4ebb2f
--- /dev/null
+++ b/pd/doc/7.stuff/audio-playpen/qsample.pd
@@ -0,0 +1,114 @@
+#N canvas 0 9 731 358 10;
+#X obj 483 52 inlet;
+#X obj 386 66 inlet;
+#X obj 314 78 inlet;
+#X obj 223 42 inlet;
+#X obj 152 63 inlet;
+#X obj 75 38 inlet;
+#X obj 19 48 inlet~;
+#X text 11 33 audio in;
+#X msg 250 268 0 5;
+#X floatatom 299 217;
+#X floatatom 386 87;
+#X floatatom 223 60;
+#X msg 152 81 bang;
+#X text 152 47 button;
+#X obj 39 354 outlet~;
+#X graph sample1-graph 0 -1 88200 1 404 352 704 202;
+#X array sample1 88200 float;
+#X pop;
+#X obj 19 91 tabwrite~ sample1;
+#X msg 75 56 bang;
+#X text 74 12 record;
+#X text 75 22 button;
+#X text 157 33 play;
+#X obj 39 299 tabread4~ sample1;
+#X obj 39 317 *~;
+#X obj 174 313 line~;
+#X obj 39 335 hip~ 5;
+#X obj 199 235 del 5;
+#X msg 169 266 0 5;
+#X msg 199 266 1 5;
+#X floatatom 316 116;
+#X text 211 22 transpose;
+#X obj 223 78 mtof;
+#X text 386 50 length;
+#X obj 223 96 t b f;
+#X obj 223 114 8.1758;
+#X obj 223 132 /;
+#X obj 299 143 t b f;
+#X obj 250 250 del 2000;
+#X obj 299 179 * 1980;
+#X obj 299 161 f 1;
+#X text 304 52 beginning;
+#X text 314 62 point;
+#X obj 39 281 line~;
+#X obj 73 205 f;
+#X obj 22 243 + 88200;
+#X obj 22 261 pack;
+#X obj 110 261 * 2000;
+#X text 482 26 read;
+#X text 483 38 file;
+#X obj 73 223 t f f b;
+#X floatatom 110 280;
+#X obj 110 243 f 1;
+#X obj 89 182 * 44.1;
+#X obj 483 91 openpanel;
+#X msg 483 73 bang;
+#X obj 386 110 min 1980;
+#X obj 315 96 max 1;
+#X obj 484 133 soundfiler;
+#X msg 485 114 read \$1 sample1;
+#X obj 299 198 max 6;
+#X connect 0 0 53 0;
+#X connect 1 0 10 0;
+#X connect 2 0 55 0;
+#X connect 3 0 11 0;
+#X connect 4 0 12 0;
+#X connect 5 0 17 0;
+#X connect 6 0 16 0;
+#X connect 8 0 23 0;
+#X connect 9 0 36 1;
+#X connect 10 0 54 0;
+#X connect 11 0 30 0;
+#X connect 12 0 26 0;
+#X connect 12 0 25 0;
+#X connect 12 0 36 0;
+#X connect 17 0 16 0;
+#X connect 21 0 22 0;
+#X connect 22 0 24 0;
+#X connect 23 0 22 1;
+#X connect 24 0 14 0;
+#X connect 25 0 27 0;
+#X connect 25 0 42 0;
+#X connect 26 0 23 0;
+#X connect 27 0 23 0;
+#X connect 28 0 51 0;
+#X connect 30 0 32 0;
+#X connect 32 0 33 0;
+#X connect 32 1 34 1;
+#X connect 33 0 34 0;
+#X connect 34 0 38 0;
+#X connect 34 0 50 1;
+#X connect 35 0 38 0;
+#X connect 35 1 37 1;
+#X connect 36 0 8 0;
+#X connect 37 0 58 0;
+#X connect 38 0 37 0;
+#X connect 41 0 21 0;
+#X connect 42 0 48 0;
+#X connect 43 0 44 0;
+#X connect 44 0 41 0;
+#X connect 45 0 44 1;
+#X connect 45 0 49 0;
+#X connect 48 0 43 0;
+#X connect 48 1 41 0;
+#X connect 48 2 50 0;
+#X connect 50 0 45 0;
+#X connect 51 0 42 1;
+#X connect 52 0 57 0;
+#X connect 53 0 52 0;
+#X connect 54 0 35 0;
+#X connect 55 0 28 0;
+#X connect 57 0 56 0;
+#X connect 58 0 9 0;
diff --git a/pd/doc/7.stuff/audio-playpen/qslew.pd b/pd/doc/7.stuff/audio-playpen/qslew.pd
new file mode 100644
index 00000000..d56bb90f
--- /dev/null
+++ b/pd/doc/7.stuff/audio-playpen/qslew.pd
@@ -0,0 +1,8 @@
+#N canvas 236 128 164 154 10;
+#X obj 47 79 line~;
+#X obj 47 58 pack 0 100;
+#X obj 47 38 inlet;
+#X obj 47 100 outlet~;
+#X connect 0 0 3 0;
+#X connect 1 0 0 0;
+#X connect 2 0 1 0;
diff --git a/pd/doc/7.stuff/audio-playpen/qvd.pd b/pd/doc/7.stuff/audio-playpen/qvd.pd
new file mode 100644
index 00000000..8a03d9c3
--- /dev/null
+++ b/pd/doc/7.stuff/audio-playpen/qvd.pd
@@ -0,0 +1,32 @@
+#N canvas 78 104 411 254 10;
+#X obj 246 47 inlet;
+#X floatatom 246 66;
+#X obj 245 162 line~;
+#X obj 245 143 pack 0 100;
+#X obj 232 180 *~;
+#X obj 104 66 inlet~;
+#X obj 22 175 outlet~;
+#X text 19 60 audio in;
+#X text 18 193 audio out;
+#X text 213 7 recirculation;
+#X text 92 25 delay time;
+#X obj 246 125 * 0.01;
+#X text 212 23 (0-100);
+#X text 100 37 (msec);
+#X obj 105 94 vd~ qdel2;
+#X obj 120 210 delwrite~ qdel2 2000;
+#X obj 20 75 inlet~;
+#X obj 246 84 max 0;
+#X obj 246 104 min 100;
+#X connect 0 0 1 0;
+#X connect 1 0 17 0;
+#X connect 2 0 4 1;
+#X connect 3 0 2 0;
+#X connect 4 0 15 0;
+#X connect 5 0 14 0;
+#X connect 11 0 3 0;
+#X connect 14 0 4 0;
+#X connect 14 0 6 0;
+#X connect 16 0 15 0;
+#X connect 17 0 18 0;
+#X connect 18 0 11 0;
diff --git a/pd/doc/7.stuff/data-structures/0.intro.txt b/pd/doc/7.stuff/data-structures/0.intro.txt
new file mode 100644
index 00000000..a1df9a88
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/0.intro.txt
@@ -0,0 +1,113 @@
+Pd release 0.23 and onward include objects for managing lists of data. The
+objects allow you to describe data structures and how they are viewed
+("template objects") and to traverse lists ("traversal objects.")
+
+The rest of this file gives a highly condensed summary of what's there; the
+patches, starting with "1.scalars.pd", act as a tutorial.
+
+1. TEMPLATE OBJECTS.
+
+templates describe data structures. You can add an item to a data structure
+using "field" or ask for a shape to be drawn using a "display command."
+
+1.1. "template" -- data structure.
+
+usage, "template <field1> <field2> ..."
+
+where the fields are either "float <name>", "symbol <name>", "list <name>"
+(don't try that yet); or "array <name> <template-for-elements>.
+
+1.2. DISPLAY COMMANDS.
+
+
+These are objects which ask Pd to draw a shape corresponding to some fields
+of the datum.
+
+1.2.1. POLYGONS and CURVES.
+
+polygons: polygon <outline-color> <line-width> <x, y> ...
+filled polygons: fpolygon <fill-color> <outline-color> <line-width> <x, y> ...
+curves: curve <outline-color> <line-width> <x, y> ...
+filled curves: fcurve <fill-color> <outline-color> <line-width> <x, y> ...
+
+Each argument can either be a number or a symbol. If a symbol, it's the
+name of a field (which must be a "float) which specifies the vaiue.
+So for instance in the "1.scalar.pd" example, in the template "template1",
+the object "fpolygon 244 q 5 0 0 20 z 40 0" draws a filled polygon whose
+interior color is 244 (red 2, green 4, blue 4) but whose outline color
+depends on the value of the field "q". Its coordinates describe a triangle
+whose altitude is given by "z."
+
+1.2.2 PLOT.
+
+The "plot" objects plots an array field as shown in 5_array.pd.
+
+2. TRAVERSAL.
+
+In this release of Pd, you can only traverse lists all of whose elements
+belong to the same template; this restriction will be relaxed in a future
+release. You "traverse" a list either to build it, to get its elements,
+or to change their values.
+
+2.1. POINTER.
+
+The "pointer" object can be used to refer to an element of a list. Its
+methods are:
+
+2.1.1. traverse <symbol>.
+
+Point to the "head" of a list. The symbol should match the name of a Pd
+window holding the list. The pointer is output, but you can't set or get the
+fields of the "head" pointer; you can only get the "next" element or "append"
+to the list.
+
+2.1.2. next. Goes to the next element of the list. Either the pointer
+is output on the left side, or else a "bang" at right tells you that no
+more objects are forthcoming.
+
+2.1.3. bang.
+outputs the current pointer.
+
+2.2. APPEND. Adds an element of the specified template to the list. You
+specify what fields you want to supply and the last inlet takes a pointer to
+the element you want to "append" after.
+
+2.3. GET.
+
+ get <template> <field...>
+
+send it a pointer to an object belonging to the <template> and it outputs
+the (floating-point) fields.
+
+2.4. SET.
+
+ set <template> <field...>
+
+send it a pointer (at the rightmost inlet) and values for the specified
+fields, and their values are changed accordingly.
+
+2.5. GETSIZE.
+
+ getsize <template> <array-field>
+
+outputs the size of the named field, which must be an array, when it receives
+a pointer to the owner as input.
+
+2.6. SETSIZE.
+
+ setsize <template> <array-field>
+
+Send it a pointer to the owner (right inlet) and then the desired size
+(left inlet) and the array is resized. If a template contains an array,
+each scalar belonging to the template can have its own size for the array.
+
+2.7. ELEMENT.
+
+ element <template> <array-field>
+
+Pass it an index and a pointer and it outputs a pointer to an element of the
+array.
+
+
+
+
diff --git a/pd/doc/7.stuff/data-structures/1.scalars.pd b/pd/doc/7.stuff/data-structures/1.scalars.pd
new file mode 100644
index 00000000..c2c465bf
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/1.scalars.pd
@@ -0,0 +1,60 @@
+#N canvas 363 11 579 461 12;
+#N canvas 13 22 297 180 data 1;
+#X restore 60 347 pd data;
+#N canvas 10 274 550 324 template1 1;
+#X obj 60 46 filledpolygon 244 q 5 0 0 20 z 40 0;
+#X obj 60 21 template float x float y float z float q;
+#X text 3 67 This subpatch acts as a template which describes the data
+structure. The "template" specifies four floating point values named
+x \, y \, z \, and q. The "filledpolygon" is a drawing instruction.
+Templates should have only one template object but may have any number
+of drawing instructions.;
+#X text 4 164 The filledpolygon's arguments are interior color \, border
+color \, border width \, and then the points of the polygon. Arguments
+which are symbols ("q" and "z" in this case) mean to take the values
+from the data structure. Other values are constant. The position of
+the object is automatically controlled by fields named "x" and "y".
+;
+#X restore 60 371 pd template1;
+#N canvas 0 0 440 292 stuff 0;
+#X obj 235 185 pointer;
+#X obj 28 187 append template1 x y z q;
+#X msg 235 127 \; pd-data clear;
+#X msg 235 163 traverse pd-data \, bang;
+#X obj 125 128 t b b b;
+#X msg 125 87 bang;
+#X obj 125 56 loadbang;
+#X text 159 87 click here to re-initialize;
+#X text 25 243 This subpatch sets up the "data" window with two objects.
+How this works will get explained later.;
+#X msg 28 164 50 100 30 9 \, 150 100 -20 900;
+#X connect 0 0 1 4;
+#X connect 3 0 0 0;
+#X connect 4 0 9 0;
+#X connect 4 1 3 0;
+#X connect 4 2 2 0;
+#X connect 5 0 4 0;
+#X connect 6 0 5 0;
+#X connect 9 0 1 0;
+#X restore 59 397 pd stuff;
+#X text 37 72 The positions \, border color \, and altitude of each
+triangle are numeric values which can control \, or be controlled by
+\, other elements of the patch.;
+#X text 37 124 When the data window is locked (not in edit mode) you
+can drag the apex of either triangle up or down to change the altitude
+(you should see the cursor change with dragging is meaningful.) In
+edit (unlocked) mode \, you can move teh entire triangles around \,
+or cut \, copy \, and paste them.;
+#X text 47 325 subpatches:;
+#X text 37 281 Data is not persistent. If you save a Pd patch and reopen
+it \, the "data" isn't preserved.;
+#X text 37 5 This patch shows a simple data window with two objects
+in it. The objects' data structures and appearances are defined by
+the "template1" subpatch. This kind of object is called a "scalar."
+;
+#X text 37 207 Scalars are described by "templates" \, which are subwindows.
+The subwindows are found by their name \, in this case "template1."
+The template describes what form the data take and how it is shown.
+It's possible to mix data of many different templates in the same collection.
+;
+#X text 294 398 updated for Pd version 0.35.;
diff --git a/pd/doc/7.stuff/data-structures/2.getting.data.pd b/pd/doc/7.stuff/data-structures/2.getting.data.pd
new file mode 100644
index 00000000..bebd7371
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/2.getting.data.pd
@@ -0,0 +1,73 @@
+#N canvas 363 11 630 603 12;
+#X text 311 559 updated for Pd version 0.32.;
+#N canvas 42 312 598 266 stuff 0;
+#X obj 353 159 pointer;
+#X obj 117 103 t b b b;
+#X msg 117 62 bang;
+#X obj 117 31 loadbang;
+#X text 151 62 click here to re-initialize;
+#X text 126 206 Explained later...;
+#X msg 20 139 50 250 30 9 \, 200 200 -20 900 \, 100 200 -50 30;
+#X obj 20 162 append template2 x y z q;
+#X msg 353 101 \; pd-data2 clear;
+#X msg 353 137 traverse pd-data2 \, bang;
+#X connect 0 0 7 4;
+#X connect 1 0 6 0;
+#X connect 1 1 9 0;
+#X connect 1 2 8 0;
+#X connect 2 0 1 0;
+#X connect 3 0 2 0;
+#X connect 6 0 7 0;
+#X connect 9 0 0 0;
+#X restore 506 310 pd stuff;
+#X text 506 242 subpatches:;
+#X obj 15 303 pointer;
+#X msg 27 271 next;
+#X text 75 301 <- object that outputs pointers to scalars;
+#N canvas 13 22 345 271 data2 1;
+#X restore 506 265 pd data2;
+#N canvas 15 278 427 138 template2 0;
+#X obj 60 46 filledpolygon 244 q 5 0 0 20 z 40 0;
+#X obj 60 21 template float x float y float z float q;
+#X text 13 79 The template for the two scalars \, as in the last patch
+;
+#X restore 506 288 pd template2;
+#X obj 15 355 get template2 x y z q;
+#X floatatom 15 384 5 0 0;
+#X floatatom 76 384 5 0 0;
+#X floatatom 137 384 5 0 0;
+#X floatatom 199 385 5 0 0;
+#X msg 15 246 traverse pd-data2;
+#X obj 59 330 print;
+#X text 100 330 <- this gets a bang when we reach the end;
+#X text 211 353 <- this takes incoming pointers;
+#X text 214 367 and outputs the values of x \, y \, z \, and q.;
+#X text 172 245 <- go to head of list (click first);
+#X text 68 273 <- output next item (click 4 times);
+#X text 14 5 The simplest thing you can do with a collection of scalars
+(a list) is to traverse it \, getting the numbers back out. This is
+done using two objects \, "pointer" which does the traversal \, and
+"get" which \, given a pointer to a scalar \, extracts numeric quantities
+from it.;
+#X text 14 85 You can send the "pointer" object a "traverse" message
+to point it to the head of the list. The argument "pd-data2" indicates
+the Pd window named "data2." The head of the list means \, not the
+first scalar in the list \, but the position before the first scalar
+\, which is a valid pointer in Pd but has no data or template.;
+#X text 14 180 The "next" message tells the "pointer" object to go
+to the next scalar in the list and output it. If there are no more
+\, "pointer" outputs a bang at right.;
+#X text 19 424 The "get" object takes a pointer \, checks that its
+template agrees with what "get" is expecting \, i.e. \, "template2"
+\, and if so outputs the values of x \, y \, z \, and q in the usual
+reverse order.;
+#X text 18 492 The pointer sent from "pointer" to "get" is an elementary
+Pd type on a level with "float" and "symbol".;
+#X connect 3 0 8 0;
+#X connect 3 1 14 0;
+#X connect 4 0 3 0;
+#X connect 8 0 9 0;
+#X connect 8 1 10 0;
+#X connect 8 2 11 0;
+#X connect 8 3 12 0;
+#X connect 13 0 3 0;
diff --git a/pd/doc/7.stuff/data-structures/3.setting.data.pd b/pd/doc/7.stuff/data-structures/3.setting.data.pd
new file mode 100644
index 00000000..d951a0a8
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/3.setting.data.pd
@@ -0,0 +1,105 @@
+#N canvas 401 39 490 472 12;
+#X floatatom 60 371 0 0 0;
+#X floatatom 60 323 0 0 0;
+#X floatatom 60 275 0 0 0;
+#X floatatom 60 227 0 0 0;
+#X floatatom 324 322 0 0 0;
+#X floatatom 283 322 0 0 0;
+#X floatatom 240 322 0 0 0;
+#X obj 197 274 pointer;
+#X msg 205 249 next;
+#X floatatom 197 322 0 0 0;
+#N canvas 19 29 363 341 data3 1;
+#X restore 269 425 pd data3;
+#N canvas 100 436 466 223 template3 0;
+#X obj 25 68 filledpolygon q 0 1 0 0 w 0 w h 0 h;
+#X obj 26 163 drawnumber q 0 0 0;
+#X obj 24 19 template float x float y float w float h float q;
+#X text 22 39 five numeric ("float") fields;
+#X text 25 88 drawing a rectangle \, interior color q \, border black and one unit thick \, through the points (0 \, 0) \, (w \, 0) \, (w \, h) \, and (0 \, h). Note that the three points containing variables become hot spots for mouse dragging.;
+#X text 26 184 Draw the value of q as an Araboc numeral \, at (0 \, 0) \, in black.;
+#X restore 269 446 pd template3;
+#N canvas 313 223 587 367 stuff 0;
+#X obj 352 180 pointer;
+#X obj 352 204 t b b p;
+#X obj 222 333 append template3 x y w h q;
+#X obj 288 9 loadbang;
+#X obj 288 62 t b b b;
+#X msg 331 138 traverse pd-data3;
+#X msg 477 136 \; pd-data3 clear;
+#X msg 240 110 0;
+#X obj 187 136 f;
+#X obj 220 136 + 1;
+#X obj 189 112 until;
+#X obj 201 159 sel 20;
+#X obj 251 159 t b;
+#X msg 290 32 bang;
+#X obj 25 237 random 300;
+#X obj 100 237 random 300;
+#X obj 323 236 random 1000;
+#X obj 177 237 random 80;
+#X obj 252 237 random 80;
+#X obj 101 263 - 30;
+#X obj 354 11 inlet;
+#X connect 0 0 1 0;
+#X connect 1 0 14 0;
+#X connect 1 1 15 0;
+#X connect 1 1 16 0;
+#X connect 1 1 17 0;
+#X connect 1 1 18 0;
+#X connect 1 2 2 5;
+#X connect 3 0 13 0;
+#X connect 4 0 10 0;
+#X connect 4 1 5 0;
+#X connect 4 1 7 0;
+#X connect 4 2 6 0;
+#X connect 5 0 0 0;
+#X connect 7 0 8 1;
+#X connect 8 0 11 0;
+#X connect 8 0 9 0;
+#X connect 9 0 8 1;
+#X connect 10 0 8 0;
+#X connect 11 0 10 1;
+#X connect 11 1 12 0;
+#X connect 12 0 0 0;
+#X connect 13 0 4 0;
+#X connect 14 0 2 0;
+#X connect 15 0 19 0;
+#X connect 16 0 2 4;
+#X connect 17 0 2 2;
+#X connect 18 0 2 3;
+#X connect 19 0 2 1;
+#X connect 20 0 13 0;
+#X restore 269 404 pd stuff;
+#X msg 269 379 remake;
+#X obj 197 298 get template3 x y w h q;
+#X floatatom 356 322 0 0 0;
+#X obj 60 251 set template3 x;
+#X obj 60 299 set template3 y;
+#X obj 60 347 set template3 w;
+#X obj 60 394 set template3 h;
+#X floatatom 60 418 0 0 0;
+#X obj 60 441 set template3 q;
+#X msg 197 226 traverse pd-data3;
+#X text 46 5 The "set" object allows you to change numeric values. In this example \, the template specifies five fields describing the (x \, y) location \, width \, height \, and color. A new feature is that the color is also getting printed out under the rectangles. This is done using the "drawnumber" object in the template.;
+#X text 323 378 <- click to randomize;
+#X text 46 87 Getting parameter values is as inthe previous patch \; however \, as you traverse the list with "next" messages the new pointers are also sent to the five "set" objects. These have as arguments the template name and the name of the field they will set. You can drag on the five number boxes (after selecting an object with "traverse" and "next" messages) to change its location \, shape \, and color.;
+#X connect 0 0 19 0;
+#X connect 1 0 18 0;
+#X connect 2 0 17 0;
+#X connect 3 0 16 0;
+#X connect 7 0 14 0;
+#X connect 7 0 16 1;
+#X connect 7 0 17 1;
+#X connect 7 0 18 1;
+#X connect 7 0 19 1;
+#X connect 7 0 21 1;
+#X connect 8 0 7 0;
+#X connect 13 0 12 0;
+#X connect 14 0 9 0;
+#X connect 14 1 6 0;
+#X connect 14 2 5 0;
+#X connect 14 3 4 0;
+#X connect 14 4 15 0;
+#X connect 20 0 21 0;
+#X connect 22 0 7 0;
diff --git a/pd/doc/7.stuff/data-structures/4.append.pd b/pd/doc/7.stuff/data-structures/4.append.pd
new file mode 100644
index 00000000..2c1991d9
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/4.append.pd
@@ -0,0 +1,36 @@
+#N canvas 308 71 688 415 12;
+#X obj 421 332 pointer;
+#X obj 108 277 t b b b;
+#X msg 120 241 bang;
+#X text 161 240 click here to re-initialize;
+#X msg 11 313 50 250 30 9 \, 200 200 -20 900 \, 100 200 -50 30;
+#X text 56 27 The objects below put three items in the data window.
+First the window is cleared. Then a "pointer" object is instructed
+to point to the beginning of the data window ("traverse pd-data") \,
+and to output its value ("bang") to the "append" object. This object
+is then given numeric values to create three items.;
+#X obj 11 336 append template4 x y z q;
+#X msg 421 269 \; pd-data4 clear;
+#N canvas 0 0 318 188 data4 1;
+#X restore 430 219 pd data4;
+#N canvas 15 278 427 138 template4 0;
+#X obj 60 46 filledpolygon 244 q 5 0 0 20 z 40 0;
+#X obj 60 21 template float x float y float z float q;
+#X text 13 79 The template for the two scalars \, as in the last patch
+;
+#X restore 428 243 pd template4;
+#X msg 421 309 traverse pd-data4 \, bang;
+#X text 57 165 The outlet of "append" is a pointer to the newly created
+scalar. You can pass that on to other append objects if you want to
+build heterogenous lists.;
+#X text 363 375 Updated for Pd version 0.32;
+#X text 57 121 The "append" object is given the argument "template4"
+to specify what kind of data structure to append. The other arguments
+are the names of variables we'll set.;
+#X connect 0 0 6 4;
+#X connect 1 0 4 0;
+#X connect 1 1 10 0;
+#X connect 1 2 7 0;
+#X connect 2 0 1 0;
+#X connect 4 0 6 0;
+#X connect 10 0 0 0;
diff --git a/pd/doc/7.stuff/data-structures/5.array.pd b/pd/doc/7.stuff/data-structures/5.array.pd
new file mode 100644
index 00000000..9c1996a7
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/5.array.pd
@@ -0,0 +1,112 @@
+#N canvas 67 294 709 456 12;
+#X obj 235 323 pointer;
+#X floatatom 232 183 0 0 0;
+#X msg 235 300 bang;
+#X floatatom 15 200 0 0 0;
+#X floatatom 17 350 0 0 0;
+#X floatatom 235 369 0 0 0;
+#X floatatom 451 276 0 0 0;
+#X obj 451 229 pointer;
+#X obj 318 163 pointer;
+#X msg 449 194 bang;
+#N canvas 0 0 384 196 data5 1;
+#X restore 508 314 pd data5;
+#N canvas 5 272 431 226 template5 0;
+#X obj 8 91 filledpolygon 244 q 3 0 0 20 z 40 0;
+#X obj 6 8 template float x float y float z float q array bazoo template5-element
+;
+#X obj 8 113 plot bazoo 700 3 30 10 4;
+#X text 6 44 this declares an array named "bazoo" whose elements are
+described by "template5-element." Array declarations take three arguments
+while "float" declarations take only two.;
+#X text 6 136 Here we ask to plot the array \, color 700 \, line width
+3 \, starting location (30 \, 10) relative to the scalar \, points
+spaced 4 apart.;
+#X text 7 186 You can also do (x \, y) plots and/or make the line thickness
+variable---see the help window for "plot".;
+#X restore 508 337 pd template5;
+#N canvas 65 248 442 101 template5-element 0;
+#X obj 35 11 template float y;
+#X text 12 36 This says that array elements will have a single floating-point
+number named "y". The variable name "y" is automatically assumed to
+control screen height \; if you don't have at least that variable you
+can't plot the array..;
+#X restore 508 360 pd template5-element;
+#N canvas 515 84 589 429 stuff 0;
+#X obj 354 163 pointer;
+#X obj 136 102 t b b b;
+#X msg 136 61 bang;
+#X text 170 61 click here to re-initialize;
+#X obj 134 163 append template5 x y z q;
+#X msg 354 100 \; pd-data5 clear;
+#X msg 354 140 traverse pd-data5 \, bang;
+#X msg 283 280 50;
+#X obj 284 307 setsize template5 bazoo;
+#X obj 137 23 loadbang;
+#X msg 134 140 50 150 30 9;
+#X obj 134 191 t b b p;
+#X floatatom 68 327 0 0 0;
+#X floatatom 14 332 0 0 0;
+#X obj 14 376 set template5-element y;
+#X obj 68 350 element template5 bazoo;
+#X obj 20 303 unpack;
+#X msg 12 258 3 5 \, 7 9 \, -30 10 \, 43 45;
+#X connect 0 0 4 4;
+#X connect 1 0 10 0;
+#X connect 1 1 6 0;
+#X connect 1 2 5 0;
+#X connect 2 0 1 0;
+#X connect 4 0 11 0;
+#X connect 6 0 0 0;
+#X connect 7 0 8 0;
+#X connect 9 0 2 0;
+#X connect 10 0 4 0;
+#X connect 11 0 17 0;
+#X connect 11 1 7 0;
+#X connect 11 2 8 1;
+#X connect 11 2 15 1;
+#X connect 12 0 15 0;
+#X connect 13 0 14 0;
+#X connect 15 0 14 1;
+#X connect 16 0 13 0;
+#X connect 16 1 12 0;
+#X connect 17 0 16 0;
+#X restore 508 383 pd stuff;
+#X msg 318 140 traverse pd-data5 \, next;
+#X obj 451 252 getsize template5 bazoo;
+#X obj 232 229 setsize template5 bazoo;
+#X obj 17 373 set template5-element y;
+#X obj 235 346 get template5-element y;
+#X obj 15 223 element template5 bazoo;
+#X text 38 15 Scalars may contain arrays \, and moreover the elements
+of an array can be of any scalar type (and can have sub-arrays recursively.)
+The type of the element of an array is fixed in the template. In this
+case \, "template5" contains the definition of the top-level scalar
+and "template5-element" is the template of each array element (see
+the template subpatch.);
+#X text 328 121 click to get pointer;
+#X text 449 173 get size;
+#X text 221 158 set size;
+#X text 16 133 select an individual;
+#X text 16 153 element \, which is a;
+#X text 14 169 scalar with template;
+#X text 104 189 template5;
+#X text 12 413 work as before \, but on;
+#X text 12 433 array elements...;
+#X text 433 424 Updated for Pd version 0.35;
+#X text 17 395 normal "set" amd "get";
+#X connect 0 0 18 0;
+#X connect 1 0 16 0;
+#X connect 2 0 0 0;
+#X connect 3 0 19 0;
+#X connect 4 0 17 0;
+#X connect 7 0 15 0;
+#X connect 8 0 16 1;
+#X connect 8 0 19 1;
+#X connect 8 0 7 0;
+#X connect 9 0 7 0;
+#X connect 14 0 8 0;
+#X connect 15 0 6 0;
+#X connect 18 0 5 0;
+#X connect 19 0 0 0;
+#X connect 19 0 17 1;
diff --git a/pd/doc/7.stuff/data-structures/6.file.pd b/pd/doc/7.stuff/data-structures/6.file.pd
new file mode 100644
index 00000000..1196d1a1
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/6.file.pd
@@ -0,0 +1,68 @@
+#N canvas 405 27 291 318 12;
+#N canvas 0 0 377 383 data 1;
+#X restore 40 153 pd data;
+#N canvas 50 470 523 157 template-toplevel 0;
+#X obj 120 112 plot bazoo 700 3 10 20 20;
+#X obj 120 48 template float x float y float z float q array bazoo template-element;
+#X obj 120 86 drawpolygon q 4 0 0 20 z z -5 10 20;
+#X restore 40 174 pd template-toplevel;
+#N canvas 199 231 600 239 template-element 0;
+#X obj 59 48 template float x float y float w;
+#X obj 80 89 drawpolygon 10 2 5 0 0 -5 -5 0 0 5 5 0;
+#X restore 40 197 pd template-element;
+#X msg 45 16 \; pd-data clear;
+#N canvas 125 240 709 410 traversal 0;
+#X floatatom 212 353 0 0 0;
+#X obj 212 376 set template-toplevel q;
+#X floatatom 212 307 0 0 0;
+#X floatatom 210 255 0 0 0;
+#X floatatom 96 62 0 0 0;
+#X floatatom 97 114 0 0 0;
+#X floatatom 23 144 0 0 0;
+#X floatatom 210 209 0 0 0;
+#X floatatom 617 194 0 0 0;
+#X floatatom 550 192 0 0 0;
+#X floatatom 486 191 0 0 0;
+#X obj 419 116 pointer;
+#X obj 419 168 get template-toplevel x y z q;
+#X msg 450 90 next;
+#X floatatom 419 191 0 0 0;
+#X obj 23 169 set template-element y;
+#X obj 97 137 element template-toplevel bazoo;
+#X obj 96 85 setsize template-toplevel bazoo;
+#X obj 210 232 set template-toplevel x;
+#X obj 210 278 set template-toplevel y;
+#X obj 212 330 set template-toplevel z;
+#X floatatom 22 200 0 0 0;
+#X obj 22 225 set template-element x;
+#X msg 419 67 traverse pd-data \, next;
+#X floatatom 26 258 0 0 0;
+#X obj 26 283 set template-element w;
+#X connect 0 0 1 0;
+#X connect 2 0 20 0;
+#X connect 3 0 19 0;
+#X connect 4 0 17 0;
+#X connect 5 0 16 0;
+#X connect 6 0 15 0;
+#X connect 7 0 18 0;
+#X connect 11 0 12 0;
+#X connect 11 0 17 1;
+#X connect 11 0 18 1;
+#X connect 11 0 19 1;
+#X connect 11 0 20 1;
+#X connect 11 0 1 1;
+#X connect 11 0 16 1;
+#X connect 12 0 14 0;
+#X connect 12 1 10 0;
+#X connect 12 2 9 0;
+#X connect 12 3 8 0;
+#X connect 13 0 11 0;
+#X connect 16 0 15 1;
+#X connect 16 0 22 1;
+#X connect 16 0 25 1;
+#X connect 21 0 22 0;
+#X connect 23 0 11 0;
+#X connect 24 0 25 0;
+#X restore 41 218 pd traversal;
+#X msg 43 55 \; pd-data write xx.txt;
+#X msg 41 102 \; pd-data read file.txt;
diff --git a/pd/doc/7.stuff/data-structures/7.sequencer.pd b/pd/doc/7.stuff/data-structures/7.sequencer.pd
new file mode 100644
index 00000000..6b815191
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/7.sequencer.pd
@@ -0,0 +1,192 @@
+#N struct template-toplevel float x float y float voiceno array pitch
+template-pitch array amp template-amp;
+#N struct template-pitch float x float y float w;
+#N struct template-amp float x float y float w;
+#N canvas 467 44 543 446 12;
+#N canvas 565 104 524 166 template-toplevel 0;
+#X obj 25 86 plot pitch voiceno 3 10 0;
+#X obj 25 113 plot amp 0 3 10 0;
+#X obj 27 60 filledpolygon 9 9 0 0 -10 0 10 5 10 5 -10;
+#X obj 25 21 struct template-toplevel float x float y float voiceno
+array pitch template-pitch array amp template-amp;
+#X restore 64 197 pd template-toplevel;
+#N canvas 0 0 419 102 template-amp 0;
+#X obj 15 41 struct template-amp float x float y float w;
+#X restore 64 219 pd template-amp;
+#N canvas 42 221 452 87 template-pitch 0;
+#X obj 21 29 struct template-pitch float x float y float w;
+#X restore 66 242 pd template-pitch;
+#N canvas 282 38 522 569 synthesis 0;
+#X msg 125 220 next;
+#X msg 108 172 traverse pd-data \, next;
+#X obj 108 250 pointer template-toplevel;
+#X obj 108 273 t p p;
+#X obj 108 296 get template-toplevel voiceno;
+#X obj 108 325 pack 0 p;
+#X obj 108 4 inlet;
+#X obj 108 33 route start stop;
+#X msg 161 54 \; reset bang;
+#X obj 298 30 r reset;
+#X obj 152 112 s reset;
+#X obj 125 194 r next-evt;
+#X obj 108 354 route 0 9 90 900 99 909 990;
+#X obj 55 372 voice;
+#X obj 55 536 outlet~;
+#X obj 55 395 voice;
+#X obj 55 418 voice;
+#X obj 55 441 voice;
+#X msg 298 58 \; reset-stop stop \; time-of-last-evt 0 \; pd-data sort
+;
+#X obj 55 465 voice;
+#X obj 55 488 voice;
+#X obj 55 511 voice;
+#X obj 372 351 s delay-multiplier;
+#X obj 375 276 t b f;
+#X msg 372 303 1000;
+#X obj 389 327 /;
+#X obj 375 250 r tempo;
+#X obj 108 90 t b b b;
+#X msg 130 136 \; pd-data sort;
+#X connect 0 0 2 0;
+#X connect 1 0 2 0;
+#X connect 2 0 3 0;
+#X connect 3 0 4 0;
+#X connect 3 1 5 1;
+#X connect 4 0 5 0;
+#X connect 5 0 12 0;
+#X connect 6 0 7 0;
+#X connect 7 0 27 0;
+#X connect 7 1 8 0;
+#X connect 9 0 18 0;
+#X connect 11 0 0 0;
+#X connect 12 0 13 1;
+#X connect 12 1 15 1;
+#X connect 12 2 16 1;
+#X connect 12 3 17 1;
+#X connect 12 4 19 1;
+#X connect 12 5 20 1;
+#X connect 12 6 21 1;
+#X connect 13 0 15 0;
+#X connect 15 0 16 0;
+#X connect 16 0 17 0;
+#X connect 17 0 19 0;
+#X connect 19 0 20 0;
+#X connect 20 0 21 0;
+#X connect 21 0 14 0;
+#X connect 23 0 24 0;
+#X connect 23 1 25 1;
+#X connect 24 0 25 0;
+#X connect 25 0 22 0;
+#X connect 26 0 23 0;
+#X connect 27 0 1 0;
+#X connect 27 1 28 0;
+#X connect 27 2 10 0;
+#X restore 64 323 pd synthesis;
+#X floatatom 278 276 0 0 0;
+#X floatatom 92 358 0 0 0;
+#N canvas 159 26 495 270 output 0;
+#X obj 345 163 t b;
+#X obj 345 112 f;
+#X obj 345 61 inlet;
+#X text 351 30 mute;
+#X obj 345 189 f;
+#X msg 434 182 0;
+#X msg 345 87 bang;
+#X obj 345 138 moses 1;
+#X obj 434 156 t b f;
+#X obj 405 119 moses 1;
+#X obj 85 151 dbtorms;
+#X obj 405 94 r master-lvl;
+#X obj 85 43 r master-lvl;
+#X obj 345 214 s master-lvl;
+#X obj 22 185 inlet~;
+#X obj 203 42 inlet;
+#X text 203 18 level;
+#X obj 203 102 s master-lvl;
+#X msg 98 67 set \$1;
+#X obj 98 91 outlet;
+#X msg 218 65 \; pd dsp 1;
+#X obj 85 198 line~;
+#X obj 22 216 *~;
+#X obj 22 246 dac~;
+#X obj 85 175 pack 0 50;
+#X text 20 162 audio;
+#X text 95 112 show level;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 13 0;
+#X connect 5 0 13 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 9 1 4 1;
+#X connect 10 0 24 0;
+#X connect 11 0 1 1;
+#X connect 11 0 9 0;
+#X connect 12 0 10 0;
+#X connect 12 0 18 0;
+#X connect 14 0 22 0;
+#X connect 15 0 17 0;
+#X connect 15 0 20 0;
+#X connect 18 0 19 0;
+#X connect 21 0 22 1;
+#X connect 22 0 23 0;
+#X connect 22 0 23 1;
+#X connect 24 0 21 0;
+#X restore 64 380 pd output;
+#X msg 122 358 MUTE;
+#X msg 64 296 start;
+#X msg 111 296 stop;
+#N canvas 22 39 392 386 data 0;
+#X scalar template-toplevel 2 246 900 \; 0 0 50 \; 10 0 50 \; \; 0
+0 0 \; 10 0 10 \; 11 0 0 \; \;;
+#X scalar template-toplevel 13 220 990 \; 0 0 50 \; 10 0 50 \; \; 0
+0 10 \; 10 0 10 \; 11 0 0 \; \;;
+#X scalar template-toplevel 34 73 90 \; 0 250 50 \; 100 50 0 \; 100
+50 50 \; 230 200 0 \; 230 50 10 \; 240 300 1 \; 240 100 50 \; 250 250
+1 \; 250 150 50 \; 260 250 1 \; 285 250 1 \; \; 0 0 2 \; 100 0 5 \;
+200 0 6 \; 225 0 2 \; 230 0 5 \; 260 0 10 \; 261 0 5 \; 265 0 0 \;
+266 0 6 \; 270 0 0 \; 271 0 8 \; 275 0 0 \; 276 0 10 \; 280 0 0 \;
+281 0 12 \; 285 0 0 \; \;;
+#X scalar template-toplevel 67 282 900 \; 0 20 1 \; 60 20 1 \; \; 0
+0 0 \; 30 0 14 \; 60 0 0 \; \;;
+#X scalar template-toplevel 141 322 900 \; 0 0 50 \; 70 -70 50 \; \;
+0 0 0 \; 10 0 10 \; 20 0 0 \; 30 0 0 \; 40 0 10 \; 50 0 0 \; 60 0 10
+\; 70 0 0 \; \;;
+#X scalar template-toplevel 326 192 909 \; 0 0 50 \; 50 0 50 \; \;
+0 0 15 \; 10 0 10 \; 50 0 0 \; \;;
+#X restore 64 176 pd data;
+#N canvas 82 467 332 145 stuff 0;
+#X msg 1 101 \; pd-data write xx.txt;
+#X msg -3 39 \; pd-data read score.txt;
+#X obj 208 7 loadbang;
+#X msg 208 34 \; tempo 60;
+#X msg 198 101 \; pd-data sort;
+#X connect 2 0 3 0;
+#X restore 65 264 pd stuff;
+#X obj 278 229 r tempo;
+#X msg 278 252 set \$1;
+#X obj 278 300 s tempo;
+#X text 13 4 This patch shows an example of how to use data collections
+as musical sequences (with apologies to Yuasa and Stockhausen). Here
+the black traces show dynamics and the colored ones show pitch. The
+fatness of the pitch traces give bandwidth. Any of the three can change
+over the life of the event.;
+#X text 160 357 <--- volume in dB;
+#X text 13 96 To hear the result \, turn the volume up to 70 or so
+(higher if it's not loud enough the first time) and hit "start". You
+can set the tempo lower if that helps you follow the "score" the first
+couple of times.;
+#X text 311 276 <--- tempo;
+#X text 256 416 Updated for Pd version 0.32;
+#X connect 3 0 6 0;
+#X connect 4 0 14 0;
+#X connect 5 0 6 1;
+#X connect 6 0 5 0;
+#X connect 7 0 6 2;
+#X connect 8 0 3 0;
+#X connect 9 0 3 0;
+#X connect 12 0 13 0;
+#X connect 13 0 4 0;
diff --git a/pd/doc/7.stuff/data-structures/data-array.pd b/pd/doc/7.stuff/data-structures/data-array.pd
new file mode 100644
index 00000000..25cb1ec8
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/data-array.pd
@@ -0,0 +1,64 @@
+#N canvas 230 71 587 465 12;
+#X floatatom 179 207 0 0 0;
+#X obj 53 199 f;
+#X obj 89 194 + 1;
+#X obj 53 232 sel;
+#X msg 69 165 1;
+#X msg 285 213 0;
+#X obj 418 342 *;
+#X obj 418 392 del;
+#X obj 414 292 t f f;
+#X obj 418 322 -;
+#X msg 469 304 0;
+#X obj 449 346 r delay-multiplier;
+#X obj 432 369 r reset-stop;
+#X obj 238 110 inlet;
+#X obj 179 184 getsize \$1 \$2;
+#X obj 285 233 element \$1 \$2;
+#X obj 187 234 element \$1 \$2;
+#X obj 208 408 outlet;
+#X obj 349 408 outlet;
+#X obj 187 254 get \$3 y w x;
+#X obj 285 253 get \$3 y w;
+#X obj 265 408 outlet;
+#X obj 342 302 t f b;
+#X obj 372 326 0;
+#X obj 238 130 t b b p b;
+#X text 229 93 pointer in;
+#X text 20 12 This is an abstraction used in the sequencer example. Here we take a pointer and sequence an array belonging to it \, either the amplitude or the frequency \, depending on the value of argument 2 The template of the scalar is given by argument 1 and that of the array elements by argument 3;
+#X text 90 431 Outlets: new y value \, new w value \, time to ramp to new values.;
+#X connect 1 0 2 0;
+#X connect 1 0 3 0;
+#X connect 2 0 1 1;
+#X connect 3 1 16 0;
+#X connect 4 0 1 1;
+#X connect 5 0 15 0;
+#X connect 6 0 7 0;
+#X connect 6 0 18 0;
+#X connect 7 0 1 0;
+#X connect 8 0 9 1;
+#X connect 8 1 9 0;
+#X connect 9 0 6 0;
+#X connect 10 0 9 1;
+#X connect 11 0 6 1;
+#X connect 12 0 7 0;
+#X connect 13 0 24 0;
+#X connect 14 0 0 0;
+#X connect 14 0 3 1;
+#X connect 15 0 20 0;
+#X connect 16 0 19 0;
+#X connect 19 0 17 0;
+#X connect 19 1 21 0;
+#X connect 19 2 8 0;
+#X connect 20 0 17 0;
+#X connect 20 1 22 0;
+#X connect 22 0 21 0;
+#X connect 22 1 23 0;
+#X connect 23 0 18 0;
+#X connect 24 0 1 0;
+#X connect 24 1 5 0;
+#X connect 24 2 15 1;
+#X connect 24 2 14 0;
+#X connect 24 2 16 1;
+#X connect 24 3 4 0;
+#X connect 24 3 10 0;
diff --git a/pd/doc/7.stuff/data-structures/data-start.pd b/pd/doc/7.stuff/data-structures/data-start.pd
new file mode 100644
index 00000000..b0522fbf
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/data-start.pd
@@ -0,0 +1,40 @@
+#N canvas 404 0 597 385 12;
+#X obj 248 142 inlet;
+#X obj 295 250 *;
+#X obj 165 262 del;
+#X obj 130 141 r reset-stop;
+#X obj 195 339 outlet;
+#X obj 375 172 outlet;
+#X obj 310 145 get \$1 x y;
+#X obj 195 312 pointer;
+#X text 46 101 outlets: pointer (delayed) \, y-value.;
+#X obj 248 167 t b p;
+#X obj 165 288 t b b;
+#X obj 335 224 r time-of-last-evt;
+#X obj 295 223 -;
+#X obj 310 167 t f f;
+#X obj 97 339 s next-evt;
+#X obj 335 201 s time-of-last-evt;
+#X obj 329 251 r delay-multiplier;
+#X text 49 10 This is an abstraction used by the sequencer example.
+;
+#X text 46 45 Here we carry out the actual sequencing. Argument is
+template of the scalar. Note the sends and receives which must agree
+with the rest of the patch.;
+#X connect 0 0 9 0;
+#X connect 1 0 2 1;
+#X connect 2 0 10 0;
+#X connect 3 0 2 0;
+#X connect 6 0 13 0;
+#X connect 6 1 5 0;
+#X connect 7 0 4 0;
+#X connect 9 0 2 0;
+#X connect 9 1 7 1;
+#X connect 9 1 6 0;
+#X connect 10 0 14 0;
+#X connect 10 1 7 0;
+#X connect 11 0 12 1;
+#X connect 12 0 1 0;
+#X connect 13 0 15 0;
+#X connect 13 1 12 0;
+#X connect 16 0 1 1;
diff --git a/pd/doc/7.stuff/data-structures/file.txt b/pd/doc/7.stuff/data-structures/file.txt
new file mode 100644
index 00000000..62b6a167
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/file.txt
@@ -0,0 +1,39 @@
+data;
+template template-toplevel;
+float x;
+float y;
+float z;
+float q;
+array bazoo template-element;
+;
+template template-element;
+float x;
+float y;
+float w;
+;
+;
+template-toplevel 76 177 -66 85;
+0 0 0;
+30 0 0;
+0 111 8;
+-47 22 0;
+0 0 0;
+0 70 0;
+0 70 70;
+70 70 0;
+0 70 0;
+;
+template-toplevel 196 109 77 802;
+-20 77 0;
+0 0 4;
+67 59 0;
+0 76 12;
+-45 -68 0;
+;
+template-toplevel 150 250 20 80;
+0 0 0;
+40 0 4;
+60 50 0;
+100 30 3;
+200 0 0;
+;
diff --git a/pd/doc/7.stuff/data-structures/score.txt b/pd/doc/7.stuff/data-structures/score.txt
new file mode 100644
index 00000000..84ef0376
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/score.txt
@@ -0,0 +1,94 @@
+data;
+template template-toplevel;
+float x;
+float y;
+float voiceno;
+array pitch template-pitch;
+array amp template-amp;
+;
+template template-pitch;
+float x;
+float y;
+float w;
+;
+template template-amp;
+float x;
+float y;
+float w;
+;
+;
+template-toplevel 2 246 900;
+0 0 50;
+10 0 50;
+;
+0 0 0;
+10 0 10;
+11 0 0;
+;
+template-toplevel 13 220 990;
+0 0 50;
+10 0 50;
+;
+0 0 10;
+10 0 10;
+11 0 0;
+;
+template-toplevel 34 73 90;
+0 250 50;
+100 50 0;
+100 50 50;
+230 200 0;
+230 50 10;
+240 300 1;
+240 100 50;
+250 250 1;
+250 150 50;
+260 250 1;
+285 250 1;
+;
+0 0 2;
+100 0 5;
+200 0 6;
+225 0 2;
+230 0 5;
+260 0 10;
+261 0 5;
+265 0 0;
+266 0 6;
+270 0 0;
+271 0 8;
+275 0 0;
+276 0 10;
+280 0 0;
+281 0 12;
+285 0 0;
+;
+template-toplevel 67 282 900;
+0 20 1;
+60 20 1;
+;
+0 0 0;
+30 0 14;
+60 0 0;
+;
+template-toplevel 141 322 900;
+0 0 50;
+70 -70 50;
+;
+0 0 0;
+10 0 10;
+20 0 0;
+30 0 0;
+40 0 10;
+50 0 0;
+60 0 10;
+70 0 0;
+;
+template-toplevel 326 192 909;
+0 0 50;
+50 0 50;
+;
+0 0 15;
+10 0 10;
+50 0 0;
+;
diff --git a/pd/doc/7.stuff/data-structures/voice.pd b/pd/doc/7.stuff/data-structures/voice.pd
new file mode 100644
index 00000000..2d124db7
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/voice.pd
@@ -0,0 +1,127 @@
+#N canvas 0 34 918 591 12;
+#X obj 180 96 inlet;
+#X obj 169 288 pack;
+#X obj 169 395 line~;
+#X obj 169 238 sqrt;
+#X obj 169 262 sqrt;
+#X obj 169 480 *~;
+#X obj 169 419 *~;
+#X obj 169 443 *~;
+#X obj 92 478 inlet~;
+#X obj 92 526 outlet~;
+#X obj 92 502 +~;
+#X obj 436 411 line~;
+#X obj 436 435 *~;
+#X obj 436 459 *~;
+#X obj 436 283 mtof;
+#X obj 394 187 +;
+#X obj 436 307 sqrt;
+#X obj 436 331 sqrt;
+#X obj 436 387 pack;
+#X obj 169 214 / 6;
+#X obj 189 343 r reset;
+#X msg 189 367 0 20;
+#X obj 180 120 data-start template-toplevel;
+#X obj 6 150 data-array template-toplevel amp template-amp;
+#X obj 433 148 data-array template-toplevel pitch template-pitch;
+#X obj 308 437 noise~;
+#X obj 477 259 +;
+#X obj 477 282 mtof;
+#X obj 477 306 sqrt;
+#X obj 477 330 sqrt;
+#X obj 477 411 line~;
+#X obj 477 435 *~;
+#X obj 477 459 *~;
+#X obj 477 387 pack;
+#X obj 394 411 line~;
+#X obj 394 435 *~;
+#X obj 394 459 *~;
+#X obj 394 387 pack;
+#X obj 394 284 mtof;
+#X obj 394 308 sqrt;
+#X obj 394 332 sqrt;
+#X obj 394 262 -;
+#X obj 240 520 vcf~ 10;
+#X obj 315 520 vcf~ 10;
+#X obj 390 516 vcf~ 10;
+#X text 13 7 This is an abstraction used in the sequencer example.
+Here we take care of the audio synthesis \, according to timed controls
+from the the "data-start" and "data-array" subpatches.;
+#X text 505 458 calculate time-varying center frequencies;
+#X text 470 512 ... for three VCFs acting on a noise source.;
+#X text 92 394 Amplitude;
+#X text 93 410 envelope;
+#X text 117 508 summing bus;
+#X text 346 62 Pitch is in eighth-tones (because 4 pixels per half
+tone looks reasonable on the screen.) Hence the * 0.25 objects below.
+;
+#X obj 394 209 * 0.25;
+#X obj 493 233 * 0.25;
+#X obj 394 230 + 24;
+#X connect 0 0 22 0;
+#X connect 1 0 2 0;
+#X connect 2 0 6 0;
+#X connect 2 0 6 1;
+#X connect 3 0 4 0;
+#X connect 4 0 1 0;
+#X connect 5 0 10 1;
+#X connect 6 0 7 0;
+#X connect 6 0 7 1;
+#X connect 7 0 5 0;
+#X connect 8 0 10 0;
+#X connect 10 0 9 0;
+#X connect 11 0 12 0;
+#X connect 11 0 12 1;
+#X connect 12 0 13 0;
+#X connect 12 0 13 1;
+#X connect 13 0 43 1;
+#X connect 14 0 16 0;
+#X connect 15 0 52 0;
+#X connect 16 0 17 0;
+#X connect 17 0 18 0;
+#X connect 18 0 11 0;
+#X connect 19 0 3 0;
+#X connect 20 0 21 0;
+#X connect 21 0 2 0;
+#X connect 22 0 23 0;
+#X connect 22 0 24 0;
+#X connect 22 1 15 1;
+#X connect 23 1 19 0;
+#X connect 23 2 1 1;
+#X connect 24 0 15 0;
+#X connect 24 1 53 0;
+#X connect 24 2 18 1;
+#X connect 24 2 37 1;
+#X connect 24 2 33 1;
+#X connect 25 0 42 0;
+#X connect 25 0 43 0;
+#X connect 25 0 44 0;
+#X connect 26 0 27 0;
+#X connect 27 0 28 0;
+#X connect 28 0 29 0;
+#X connect 29 0 33 0;
+#X connect 30 0 31 0;
+#X connect 30 0 31 1;
+#X connect 31 0 32 0;
+#X connect 31 0 32 1;
+#X connect 32 0 44 1;
+#X connect 33 0 30 0;
+#X connect 34 0 35 0;
+#X connect 34 0 35 1;
+#X connect 35 0 36 0;
+#X connect 35 0 36 1;
+#X connect 36 0 42 1;
+#X connect 37 0 34 0;
+#X connect 38 0 39 0;
+#X connect 39 0 40 0;
+#X connect 40 0 37 0;
+#X connect 41 0 38 0;
+#X connect 42 0 5 1;
+#X connect 43 0 5 1;
+#X connect 44 0 5 1;
+#X connect 52 0 54 0;
+#X connect 53 0 26 1;
+#X connect 53 0 41 1;
+#X connect 54 0 41 0;
+#X connect 54 0 26 0;
+#X connect 54 0 14 0;
diff --git a/pd/doc/7.stuff/data-structures/z.txt b/pd/doc/7.stuff/data-structures/z.txt
new file mode 100644
index 00000000..6cdd0a4a
--- /dev/null
+++ b/pd/doc/7.stuff/data-structures/z.txt
@@ -0,0 +1,64 @@
+data;
+template template5;
+float x;
+float y;
+float z;
+float q;
+array bazoo template5-element;
+;
+template template5-element;
+float y;
+;
+;
+template5 50 150 30 9;
+0;
+0;
+0;
+0;
+0;
+3;
+0;
+0;
+0;
+7;
+-30;
+0;
+0;
+0;
+0;
+0;
+-4;
+-18;
+-26;
+-36;
+-46;
+-62;
+-74;
+-78;
+-70;
+-62;
+-52;
+-40;
+-30;
+-20;
+-4;
+1;
+7;
+11;
+13;
+0;
+0;
+0;
+0;
+0;
+0;
+0;
+0;
+0;
+0;
+43;
+0;
+0;
+0;
+0;
+;
diff --git a/pd/doc/7.stuff/soundfile-tools/1.ring-mod.pd b/pd/doc/7.stuff/soundfile-tools/1.ring-mod.pd
new file mode 100644
index 00000000..72fb9c7f
--- /dev/null
+++ b/pd/doc/7.stuff/soundfile-tools/1.ring-mod.pd
@@ -0,0 +1,189 @@
+#N canvas 73 28 687 421 12;
+#N canvas 213 187 495 352 input-sample 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array array1 91065 float 0;
+#X coords 0 1 91065 -1 400 300 1;
+#X restore 56 23 graph;
+#X text 151 393 INPUT SAMPLE;
+#X restore 179 299 pd input-sample;
+#N canvas 192 180 507 343 output-sample 0;
+#N canvas 0 0 450 300 graph2 0;
+#X array array2 95475 float 0;
+#X coords 0 1 95475 -1 400 300 1;
+#X restore 60 13 graph;
+#X text 161 388 OUTPUT SAMPLE;
+#X restore 179 323 pd output-sample;
+#N canvas 110 33 827 602 guts 0;
+#X msg 25 133 bang;
+#X obj 25 360 openpanel;
+#X obj 144 31 inlet;
+#X obj 441 403 dac~;
+#X obj 441 342 *~;
+#X obj 456 318 line~;
+#X obj 456 296 r master-amp;
+#X msg 597 130 bang;
+#X obj 597 155 savepanel;
+#X obj 231 164 spigot;
+#X msg 233 132 0;
+#X msg 265 132 1;
+#X obj 497 427 outlet;
+#X obj 299 132 r frequency;
+#X obj 219 321 tabwrite~ array2;
+#X msg 219 193 bang;
+#X obj 420 289 +~;
+#X msg 96 129 \; pd dsp 1;
+#X obj 441 370 hip~ 7;
+#X obj 231 223 tabplay~ array1;
+#X msg 439 131 bang;
+#X obj 439 160 tabplay~ array2;
+#X msg 597 180 write \$1 array2;
+#X obj 597 205 soundfiler;
+#X obj 144 53 route read run start hear save;
+#N canvas 0 0 368 263 audio-transformation 0;
+#X obj 113 95 osc~;
+#X obj 97 116 *~;
+#X obj 97 138 hip~ 7;
+#X obj 113 71 r frequency;
+#X obj 97 22 inlet~;
+#X obj 97 169 outlet~;
+#X obj 97 47 hip~ 7;
+#X connect 0 0 1 1;
+#X connect 1 0 2 0;
+#X connect 2 0 5 0;
+#X connect 3 0 0 0;
+#X connect 4 0 6 0;
+#X connect 6 0 1 0;
+#X restore 231 251 pd audio-transformation;
+#X obj 497 402 env~ 16384;
+#X obj 570 91 route normalized;
+#X msg 571 300 write -normalize \$1 array2;
+#X msg 571 250 bang;
+#X obj 571 276 savepanel;
+#X obj 571 325 soundfiler;
+#X obj 25 413 soundfiler;
+#X msg 25 390 read -resize -maxsize 1e+06 \$1 array1;
+#X msg 25 460 \; array2 resize \$1;
+#X obj 25 437 + 4410;
+#X floatatom 96 436 0 0 0;
+#X connect 0 0 1 0;
+#X connect 1 0 33 0;
+#X connect 2 0 24 0;
+#X connect 4 0 18 0;
+#X connect 5 0 4 1;
+#X connect 6 0 5 0;
+#X connect 7 0 8 0;
+#X connect 8 0 22 0;
+#X connect 9 0 10 0;
+#X connect 9 0 15 0;
+#X connect 10 0 9 1;
+#X connect 11 0 9 1;
+#X connect 13 0 9 0;
+#X connect 15 0 14 0;
+#X connect 15 0 19 0;
+#X connect 16 0 4 0;
+#X connect 18 0 3 0;
+#X connect 18 0 3 1;
+#X connect 18 0 26 0;
+#X connect 19 0 25 0;
+#X connect 20 0 21 0;
+#X connect 21 0 16 1;
+#X connect 22 0 23 0;
+#X connect 24 0 0 0;
+#X connect 24 1 15 0;
+#X connect 24 1 10 0;
+#X connect 24 1 17 0;
+#X connect 24 2 11 0;
+#X connect 24 2 17 0;
+#X connect 24 3 20 0;
+#X connect 24 4 27 0;
+#X connect 25 0 14 0;
+#X connect 25 0 16 0;
+#X connect 26 0 12 0;
+#X connect 27 0 29 0;
+#X connect 27 1 7 0;
+#X connect 28 0 31 0;
+#X connect 29 0 30 0;
+#X connect 30 0 28 0;
+#X connect 32 0 35 0;
+#X connect 32 0 36 0;
+#X connect 33 0 32 0;
+#X connect 35 0 34 0;
+#X restore 28 265 pd guts;
+#X msg 28 155 run the transformation;
+#X msg 28 199 hear the output buffer again;
+#X text 28 113 click below to:;
+#X msg 28 221 save the output buffer;
+#X floatatom 404 257 0 0 0;
+#N canvas 194 37 397 591 output 0;
+#X obj 66 203 t b;
+#X obj 66 152 f;
+#X obj 66 102 inlet;
+#X text 71 81 mute;
+#X obj 66 228 f;
+#X msg 134 244 0;
+#X msg 66 127 bang;
+#X obj 66 178 moses 1;
+#X obj 134 218 t b f;
+#X obj 96 442 outlet;
+#X msg 96 416 set \$1;
+#X obj 186 163 moses 1;
+#X obj 224 444 dbtorms;
+#X obj 224 469 pack 0 100;
+#X obj 186 138 r master-lvl;
+#X obj 96 382 r master-lvl;
+#X obj 83 286 s master-lvl;
+#X obj 224 494 s master-amp;
+#X obj 208 244 loadbang;
+#X msg 208 269 \; master-lvl 90;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X connect 18 0 19 0;
+#X restore 404 235 pd output;
+#X msg 404 213 mute;
+#X obj 404 279 s master-lvl;
+#X text 448 212 <-- mute button;
+#X msg 28 177 start transformation when I change frequency;
+#X floatatom 392 117 0 0 0;
+#X obj 392 139 s frequency;
+#X text 392 97 modulation frequency (Hz.);
+#X floatatom 28 287 0 0 0;
+#X text 10 330 100 maximum;
+#X text 10 311 output meter;
+#X text 429 115 <--set me;
+#X text 443 255 <--set me;
+#X msg 28 133 read an input file;
+#X msg 28 243 save normalized to max amplitude;
+#X text 29 8 Ring modulator. Read in a sample first \, then you can
+either set a frequency and hit the "run" button or else hit the "start..."
+button and start the transformation by dragging on the frequency control.
+The output level should be "100" for unit gain.;
+#X text 22 361 Outputs are saved as "wav" files \, although you can
+edit the patch to make "aiff" or "nextstep" instead. Any of the three
+are OK for source files.;
+#X text 397 302 LINE OUT LEVEL in dB (100 max);
+#X connect 2 0 16 0;
+#X connect 3 0 2 0;
+#X connect 4 0 2 0;
+#X connect 6 0 2 0;
+#X connect 7 0 10 0;
+#X connect 8 0 7 0;
+#X connect 9 0 8 0;
+#X connect 12 0 2 0;
+#X connect 13 0 14 0;
+#X connect 21 0 2 0;
+#X connect 22 0 2 0;
diff --git a/pd/doc/7.stuff/soundfile-tools/2.bandpass.pd b/pd/doc/7.stuff/soundfile-tools/2.bandpass.pd
new file mode 100644
index 00000000..c75335eb
--- /dev/null
+++ b/pd/doc/7.stuff/soundfile-tools/2.bandpass.pd
@@ -0,0 +1,202 @@
+#N canvas 73 28 846 432 12;
+#N canvas 213 187 495 352 input-sample 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array array1 155948 float 0;
+#X coords 0 1 155948 -1 400 300 1;
+#X restore 55 22 graph;
+#X text 149 386 INPUT SAMPLE;
+#X restore 238 282 pd input-sample;
+#N canvas 192 180 507 343 output-sample 0;
+#N canvas 0 0 450 300 graph2 0;
+#X array array2 160358 float 0;
+#X coords 0 1 160358 -1 400 300 1;
+#X restore 59 13 graph;
+#X text 158 381 OUTPUT SAMPLE;
+#X restore 237 305 pd output-sample;
+#N canvas 116 150 735 425 guts 0;
+#X msg 25 131 bang;
+#X obj 25 354 openpanel;
+#X obj 142 31 inlet;
+#X obj 458 378 dac~;
+#X obj 458 335 *~;
+#X obj 473 312 line~;
+#X obj 473 290 r master-amp;
+#X msg 726 87 bang;
+#X obj 726 109 savepanel;
+#X obj 275 165 spigot;
+#X msg 260 131 0;
+#X msg 291 131 1;
+#X obj 513 408 outlet;
+#X obj 324 131 r frequency;
+#X obj 263 320 tabwrite~ array2;
+#X msg 263 194 bang;
+#X obj 438 283 +~;
+#X msg 143 131 \; pd dsp 1;
+#X obj 458 356 hip~ 7;
+#X obj 275 223 tabplay~ array1;
+#X msg 453 127 bang;
+#X obj 453 149 tabplay~ array2;
+#X msg 726 130 write \$1 array2;
+#X obj 726 151 soundfiler;
+#X obj 142 52 route read run start hear save;
+#N canvas 0 0 368 259 audio-transformation 0;
+#X obj 111 70 r frequency;
+#X obj 96 21 inlet~;
+#X obj 96 178 outlet~;
+#X obj 179 123 r q;
+#X obj 111 94 pack 0 100;
+#X obj 110 119 line~;
+#X obj 96 153 vcf~;
+#X connect 0 0 4 0;
+#X connect 1 0 6 0;
+#X connect 3 0 6 2;
+#X connect 4 0 5 0;
+#X connect 5 0 6 1;
+#X connect 6 0 2 0;
+#X restore 275 244 pd audio-transformation;
+#X obj 513 387 env~ 16384;
+#X obj 585 88 route normalized;
+#X msg 585 184 write -normalize \$1 array2;
+#X msg 585 142 bang;
+#X obj 585 163 savepanel;
+#X obj 585 209 soundfiler;
+#X obj 25 406 soundfiler;
+#X msg 25 383 read -resize -maxsize 1e+06 \$1 array1;
+#X msg 25 452 \; array2 resize \$1;
+#X obj 25 430 + 4410;
+#X floatatom 94 428 0 0 0;
+#X obj 413 132 r q;
+#X connect 0 0 1 0;
+#X connect 1 0 33 0;
+#X connect 2 0 24 0;
+#X connect 4 0 18 0;
+#X connect 5 0 4 1;
+#X connect 6 0 5 0;
+#X connect 7 0 8 0;
+#X connect 8 0 22 0;
+#X connect 9 0 10 0;
+#X connect 9 0 15 0;
+#X connect 10 0 9 1;
+#X connect 11 0 9 1;
+#X connect 13 0 9 0;
+#X connect 15 0 14 0;
+#X connect 15 0 19 0;
+#X connect 16 0 4 0;
+#X connect 18 0 3 0;
+#X connect 18 0 3 1;
+#X connect 18 0 26 0;
+#X connect 19 0 25 0;
+#X connect 20 0 21 0;
+#X connect 21 0 16 1;
+#X connect 22 0 23 0;
+#X connect 24 0 0 0;
+#X connect 24 1 15 0;
+#X connect 24 1 10 0;
+#X connect 24 1 17 0;
+#X connect 24 2 11 0;
+#X connect 24 2 17 0;
+#X connect 24 3 20 0;
+#X connect 24 4 27 0;
+#X connect 25 0 14 0;
+#X connect 25 0 16 0;
+#X connect 26 0 12 0;
+#X connect 27 0 29 0;
+#X connect 27 1 7 0;
+#X connect 28 0 31 0;
+#X connect 29 0 30 0;
+#X connect 30 0 28 0;
+#X connect 32 0 35 0;
+#X connect 32 0 36 0;
+#X connect 33 0 32 0;
+#X connect 35 0 34 0;
+#X connect 37 0 9 0;
+#X restore 34 236 pd guts;
+#X msg 34 131 run the transformation;
+#X msg 34 173 hear the output buffer again;
+#X text 34 91 click below to:;
+#X msg 34 194 save the output buffer;
+#X floatatom 562 334 0 0 120;
+#N canvas 194 37 397 591 output 0;
+#X obj 65 199 t b;
+#X obj 65 150 f;
+#X obj 65 100 inlet;
+#X text 70 79 mute;
+#X obj 65 224 f;
+#X msg 132 240 0;
+#X msg 65 125 bang;
+#X obj 65 175 moses 1;
+#X obj 132 215 t b f;
+#X obj 94 434 outlet;
+#X msg 94 409 set \$1;
+#X obj 183 160 moses 1;
+#X obj 221 437 dbtorms;
+#X obj 221 461 pack 0 100;
+#X obj 183 136 r master-lvl;
+#X obj 94 375 r master-lvl;
+#X obj 81 281 s master-lvl;
+#X obj 221 486 s master-amp;
+#X obj 204 240 loadbang;
+#X msg 204 264 \; master-lvl 90;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X connect 18 0 19 0;
+#X restore 562 313 pd output;
+#X msg 562 292 mute;
+#X obj 562 355 s master-lvl;
+#X text 604 291 <-- mute button;
+#X floatatom 34 257 0 0 0;
+#X text 35 300 100 maximum;
+#X text 35 282 output meter;
+#X text 600 335 <--set me;
+#X msg 34 110 read an input file;
+#X msg 34 215 save normalized to max amplitude;
+#X msg 34 152 start transformation when I change f or q;
+#X floatatom 479 164 0 0 0;
+#X obj 479 185 s frequency;
+#X text 479 85 center;
+#X floatatom 482 229 0 0 10000;
+#X text 482 210 "q";
+#X obj 482 252 s q;
+#X obj 479 143 mtof;
+#X floatatom 479 122 0 0 128;
+#X text 536 163 <- set in Hz;
+#X text 533 230 <--set selectivity;
+#X text 479 103 frequency;
+#X text 534 123 <- set in MIDI units;
+#X text 31 341 Note -- you can shift-click on the controls to change
+them in hundredths. You can also click and type numbers in \, followed
+by the "enter" key.;
+#X text 16 5 Bandpass filter. Read in a sample first \, then you can
+either set a frequency and hit the "run" button or else hit the "start..."
+button and start the transformation by dragging on the frequency or
+q control.;
+#X text 552 377 OUTPUT LEVEL in dB (100 norm);
+#X connect 2 0 12 0;
+#X connect 3 0 2 0;
+#X connect 4 0 2 0;
+#X connect 6 0 2 0;
+#X connect 7 0 10 0;
+#X connect 8 0 7 0;
+#X connect 9 0 8 0;
+#X connect 16 0 2 0;
+#X connect 17 0 2 0;
+#X connect 18 0 2 0;
+#X connect 19 0 20 0;
+#X connect 22 0 24 0;
+#X connect 25 0 19 0;
+#X connect 26 0 25 0;
diff --git a/pd/doc/7.stuff/soundfile-tools/3.phase.vocoder.pd b/pd/doc/7.stuff/soundfile-tools/3.phase.vocoder.pd
new file mode 100644
index 00000000..b69d5e86
--- /dev/null
+++ b/pd/doc/7.stuff/soundfile-tools/3.phase.vocoder.pd
@@ -0,0 +1,551 @@
+#N canvas 223 113 913 495 12;
+#X floatatom 457 258 0 0 0;
+#X floatatom 793 114 0 0 0;
+#X floatatom 654 114 0 0 0;
+#N canvas 249 280 600 398 loc&precess 0;
+#X floatatom 218 312 0 0 0;
+#X msg 369 239 set \$1;
+#X obj 367 282 outlet;
+#X obj 114 364 outlet;
+#X obj 233 96 r location;
+#X msg 113 321 set \$1;
+#X obj 368 203 r speed;
+#X obj 75 95 r see-location;
+#X obj 92 139 t b f;
+#X obj 113 292 f;
+#X obj 73 173 int;
+#X obj 73 206 sel 0;
+#X msg 209 139 1;
+#X msg 275 138 0;
+#X obj 150 245 del 300;
+#X connect 1 0 2 0;
+#X connect 4 0 8 0;
+#X connect 5 0 3 0;
+#X connect 6 0 1 0;
+#X connect 7 0 8 0;
+#X connect 8 0 10 0;
+#X connect 8 1 9 1;
+#X connect 9 0 5 0;
+#X connect 10 0 11 0;
+#X connect 11 0 12 0;
+#X connect 11 0 14 0;
+#X connect 12 0 10 1;
+#X connect 13 0 10 1;
+#X connect 14 0 13 0;
+#X connect 14 0 9 0;
+#X restore 654 91 pd loc&precess;
+#N canvas 0 0 600 400 setlocprecess 0;
+#X obj 235 113 inlet;
+#X obj 100 109 inlet;
+#X obj 231 144 s speed;
+#X obj 101 144 s location;
+#X connect 0 0 2 0;
+#X connect 1 0 3 0;
+#X restore 654 138 pd setlocprecess;
+#X obj 457 279 s transpo;
+#X obj 457 216 r transpo;
+#X msg 457 237 set \$1;
+#X msg 673 387 \; location 0 \; speed 200;
+#X text 200 8 PHASE VOCODER;
+#X text 609 18 set location;
+#X text 609 36 and stop;
+#X text 609 54 precession;
+#X text 785 53 precession;
+#X text 785 71 speed in;
+#X text 785 89 hundredths;
+#X text 457 179 transposition;
+#X text 457 197 in cents;
+#X text 666 360 contraction;
+#X text 784 360 expansion;
+#X msg 776 387 \; location 0 \; speed 10;
+#X text 30 349 100 maximum;
+#X text 30 331 output meter;
+#X floatatom 610 218 0 5 60;
+#X text 645 218 <--set me to change length;
+#N canvas 0 0 265 196 length 0;
+#X obj 46 23 inlet;
+#X obj 48 101 * 44100;
+#X msg 84 125 \; array2 resize \$1;
+#X obj 46 48 min 60;
+#X obj 60 75 s output-length;
+#X obj 46 159 s maxoutsize;
+#X connect 0 0 3 0;
+#X connect 1 0 2 0;
+#X connect 1 0 5 0;
+#X connect 3 0 1 0;
+#X connect 3 0 4 0;
+#X restore 610 239 pd length;
+#N canvas 219 38 198 151 /SUBPATCH/ 0;
+#X obj 77 118 outlet;
+#X obj 77 72 loadbang;
+#X msg 77 95 10;
+#X connect 1 0 2 0;
+#X connect 2 0 0 0;
+#X restore 610 197 pd;
+#X text 610 259 length in seconds of the output;
+#X text 610 277 buffer... maximum 60;
+#N canvas 42 0 1083 546 guts 0;
+#X msg 24 129 bang;
+#X obj 24 405 openpanel;
+#X obj 139 30 inlet;
+#X obj 450 385 dac~;
+#X obj 450 329 *~;
+#X obj 465 306 line~;
+#X obj 465 283 r master-amp;
+#X msg 728 129 bang;
+#X obj 728 150 savepanel;
+#X obj 219 164 spigot;
+#X msg 206 102 0;
+#X msg 238 102 1;
+#X obj 503 444 outlet;
+#X obj 292 251 tabwrite~ array2;
+#X obj 449 246 +~;
+#X obj 450 356 hip~ 7;
+#X msg 446 97 bang;
+#X obj 446 118 tabplay~ array2;
+#X msg 728 175 write \$1 array2;
+#X obj 728 200 soundfiler;
+#X obj 505 386 env~ 16384;
+#X obj 591 101 route normalized;
+#X msg 591 284 write -normalize \$1 array2;
+#X msg 591 242 bang;
+#X obj 591 263 savepanel;
+#X obj 591 308 soundfiler;
+#X obj 24 450 soundfiler;
+#X msg 24 428 read -resize -maxsize 1e+06 \$1 array1;
+#X floatatom 24 473 0 0 0;
+#X obj 680 345 loadbang;
+#X msg 680 368 \; window-size 2048 \; transpo 0;
+#N canvas 9 7 835 599 fft-analysis 0;
+#X obj 267 304 *~;
+#X obj 235 304 *~;
+#X obj 235 326 -~;
+#X obj 333 305 *~;
+#X obj 302 305 *~;
+#X obj 302 328 +~;
+#X obj 348 131 *~;
+#X obj 317 131 *~;
+#X obj 288 131 *~;
+#X obj 256 131 *~;
+#X obj 256 153 +~;
+#X obj 225 183 *~;
+#X obj 88 570 *~;
+#X obj 375 256 rfft~;
+#X obj 396 55 rfft~;
+#X obj 488 652 *~;
+#X obj 702 295 r window-size;
+#X obj 770 211 r sample-rate;
+#X obj 624 239 f;
+#X obj 617 53 r sample-rate;
+#X obj 592 30 r window-size;
+#X obj 615 100 t b f;
+#X obj 592 124 /;
+#X obj 519 652 *~;
+#X obj 89 549 *~;
+#X obj 106 525 rifft~;
+#X obj 89 596 outlet~;
+#X obj 639 457 print~;
+#X msg 639 428 bang;
+#X text 155 526 inverse real FFT;
+#X obj 603 215 bang~;
+#X obj 528 434 line~;
+#X obj 592 146 * 1000;
+#X text 645 139 window size (msec);
+#X obj 617 78 * 4;
+#X obj 647 162 r speed;
+#X obj 726 164 r location;
+#X obj 655 240 +;
+#X obj 648 204 *;
+#X msg 726 193 0;
+#X obj 624 349 +;
+#X obj 615 305 t f f;
+#X msg 528 406 \$1 \, \$2 \$3;
+#X obj 528 378 pack 0 0 0;
+#X obj 770 234 / 1000;
+#X obj 624 276 *;
+#X text 654 276 reading location (samples);
+#X obj 652 390 / 4;
+#X text 684 395 hop size (samples);
+#X obj 578 476 sig~;
+#X obj 546 474 +~;
+#X text 653 85 (overlap times parent SR);
+#X text 653 71 local sample rate;
+#X obj 23 52 tabreceive~ phase-real;
+#X obj 194 183 *~;
+#X obj 194 205 +~;
+#X obj 194 231 rsqrt~;
+#X obj 317 153 -~;
+#X obj 237 261 *~;
+#X obj 298 261 *~;
+#X obj 203 52 tabreceive~ phase-imag;
+#X obj 108 390 sig~;
+#X obj 90 319 t b f;
+#X msg 90 340 1;
+#X obj 104 364 /;
+#X obj 232 447 tabsend~ phase-real;
+#X obj 262 418 tabsend~ phase-imag;
+#X obj 107 136 sig~ 1.5e-20;
+#X obj 647 184 * 0.01;
+#X obj 708 217 s speed;
+#X obj 479 256 s see-location;
+#X floatatom 688 368 0 0 0;
+#X obj 686 347 *;
+#X obj 848 245 r transpo;
+#X obj 848 264 * 0.01;
+#X obj 849 285 + 69;
+#X obj 851 307 mtof;
+#X obj 851 327 / 440;
+#X obj 705 324 t b f;
+#X obj 90 297 r window-size;
+#X floatatom 855 361 0 0 0;
+#X obj 564 556 tabreceive~ hanning;
+#X obj 549 500 tabread4~ array1;
+#X obj 514 524 tabread4~ array1;
+#X obj 612 608 r running;
+#X obj 612 632 switch~ 2048 4;
+#X connect 0 0 2 1;
+#X connect 1 0 2 0;
+#X connect 2 0 65 0;
+#X connect 2 0 25 0;
+#X connect 3 0 5 1;
+#X connect 4 0 5 0;
+#X connect 5 0 66 0;
+#X connect 5 0 25 1;
+#X connect 6 0 57 1;
+#X connect 7 0 57 0;
+#X connect 8 0 10 1;
+#X connect 9 0 10 0;
+#X connect 10 0 58 1;
+#X connect 10 0 54 0;
+#X connect 10 0 54 1;
+#X connect 11 0 55 1;
+#X connect 12 0 26 0;
+#X connect 13 0 1 1;
+#X connect 13 0 3 1;
+#X connect 13 1 0 1;
+#X connect 13 1 4 1;
+#X connect 14 0 9 1;
+#X connect 14 0 7 1;
+#X connect 14 1 6 1;
+#X connect 14 1 8 1;
+#X connect 15 0 14 0;
+#X connect 16 0 72 0;
+#X connect 17 0 44 0;
+#X connect 18 0 37 0;
+#X connect 18 0 45 0;
+#X connect 18 0 70 0;
+#X connect 19 0 34 0;
+#X connect 20 0 22 0;
+#X connect 21 0 22 0;
+#X connect 21 1 22 1;
+#X connect 22 0 32 0;
+#X connect 23 0 13 0;
+#X connect 24 0 12 0;
+#X connect 25 0 24 1;
+#X connect 28 0 27 0;
+#X connect 30 0 18 0;
+#X connect 31 0 27 0;
+#X connect 31 0 50 0;
+#X connect 31 0 83 0;
+#X connect 32 0 43 2;
+#X connect 32 0 38 1;
+#X connect 34 0 21 0;
+#X connect 35 0 68 0;
+#X connect 36 0 39 0;
+#X connect 36 0 18 1;
+#X connect 37 0 18 1;
+#X connect 38 0 37 1;
+#X connect 39 0 69 0;
+#X connect 40 0 43 1;
+#X connect 41 0 43 0;
+#X connect 41 1 40 0;
+#X connect 42 0 31 0;
+#X connect 43 0 42 0;
+#X connect 44 0 45 1;
+#X connect 45 0 41 0;
+#X connect 47 0 49 0;
+#X connect 49 0 50 1;
+#X connect 50 0 82 0;
+#X connect 53 0 9 0;
+#X connect 53 0 6 0;
+#X connect 54 0 55 0;
+#X connect 55 0 56 0;
+#X connect 56 0 58 0;
+#X connect 56 0 59 0;
+#X connect 57 0 59 1;
+#X connect 57 0 11 0;
+#X connect 57 0 11 1;
+#X connect 58 0 1 0;
+#X connect 58 0 4 0;
+#X connect 59 0 0 0;
+#X connect 59 0 3 0;
+#X connect 60 0 7 0;
+#X connect 60 0 8 0;
+#X connect 61 0 24 0;
+#X connect 62 0 63 0;
+#X connect 62 1 64 1;
+#X connect 63 0 64 0;
+#X connect 64 0 61 0;
+#X connect 67 0 10 0;
+#X connect 68 0 38 0;
+#X connect 71 0 47 0;
+#X connect 71 0 40 1;
+#X connect 72 0 71 0;
+#X connect 73 0 74 0;
+#X connect 74 0 75 0;
+#X connect 75 0 76 0;
+#X connect 76 0 77 0;
+#X connect 77 0 78 0;
+#X connect 77 0 80 0;
+#X connect 78 0 72 0;
+#X connect 78 1 72 1;
+#X connect 79 0 62 0;
+#X connect 81 0 23 1;
+#X connect 81 0 15 1;
+#X connect 81 0 12 1;
+#X connect 82 0 23 0;
+#X connect 83 0 15 0;
+#X connect 84 0 85 0;
+#X restore 291 222 pd fft-analysis;
+#X msg 203 187 bang;
+#X obj 38 225 samplerate~;
+#X obj 38 247 s sample-rate;
+#N canvas 260 23 647 768 phase-tables 0;
+#N canvas 0 0 450 300 graph2 0;
+#X array phase-imag 4096 float 0;
+#X coords 0 1 4096 -1 400 300 1;
+#X restore 234 396 graph;
+#N canvas 0 0 450 300 graph3 0;
+#X array phase-real 4096 float 0;
+#X coords 0 1 4096 -1 400 300 1;
+#X restore 232 23 graph;
+#X msg 229 769 \; phase-real resize 4096 \; phase-imag resize 4096
+;
+#X restore 681 423 pd phase-tables;
+#N canvas 138 111 767 761 hanning-window 0;
+#X obj 125 281 phasor~;
+#X obj 125 319 cos~;
+#X obj 31 448 tabwrite~ hanning;
+#X obj 41 345 -~;
+#X obj 38 298 sig~ 1;
+#X msg 51 246 0;
+#X text 193 19 CALCULATE HANNING;
+#X text 193 37 WINDOW TABLE;
+#N canvas 0 0 450 300 graph1 0;
+#X array hanning 4096 float 0;
+#X coords 0 1 4096 -1 400 300 1;
+#X restore 375 384 graph;
+#X obj 126 233 sig~;
+#X text 239 202 sample rate / window size;
+#X msg 31 196 bang;
+#X obj 90 367 sig~ 0.5;
+#X obj 67 409 *~;
+#X obj 128 109 samplerate~;
+#X obj 34 31 r window-size;
+#X obj 34 72 t b f;
+#X msg 375 746 \; hanning resize 4096;
+#X obj 126 180 /;
+#X connect 0 0 1 0;
+#X connect 1 0 3 1;
+#X connect 3 0 13 0;
+#X connect 4 0 3 0;
+#X connect 5 0 0 1;
+#X connect 9 0 0 0;
+#X connect 11 0 2 0;
+#X connect 11 0 5 0;
+#X connect 12 0 13 1;
+#X connect 13 0 2 0;
+#X connect 14 0 18 0;
+#X connect 15 0 16 0;
+#X connect 16 0 14 0;
+#X connect 16 0 11 0;
+#X connect 16 1 18 1;
+#X connect 18 0 9 0;
+#X restore 681 447 pd hanning-window;
+#X obj 100 335 delay;
+#X obj 160 332 + 100;
+#X obj 139 51 route read run start hear save stop;
+#X obj 895 245 timer;
+#X obj 895 269 * 44.1;
+#X obj 232 314 r maxoutsize;
+#X obj 217 338 f;
+#X msg 217 360 \; array2 resize \$1;
+#X msg 100 358 \; action stop;
+#X obj 186 30 r action;
+#X obj 910 172 r running;
+#X obj 894 195 f;
+#X obj 894 219 sel 1;
+#X obj 895 344 moses;
+#X obj 946 345 r maxoutsize;
+#X msg 895 366 \; array2 resize \$1;
+#X msg 918 293 \; running 0;
+#X msg 293 277 \; running 1;
+#X obj 160 310 / 44.1;
+#X msg 751 10 bang;
+#X obj 277 92 r location;
+#X obj 277 115 r speed;
+#X obj 278 137 r transpo;
+#X msg 43 283 \; pd dsp 1;
+#X obj 503 414 int;
+#X connect 0 0 1 0;
+#X connect 1 0 27 0;
+#X connect 2 0 39 0;
+#X connect 4 0 15 0;
+#X connect 5 0 4 1;
+#X connect 6 0 5 0;
+#X connect 7 0 8 0;
+#X connect 8 0 18 0;
+#X connect 9 0 10 0;
+#X connect 9 0 32 0;
+#X connect 10 0 9 1;
+#X connect 11 0 9 1;
+#X connect 14 0 4 0;
+#X connect 15 0 3 0;
+#X connect 15 0 3 1;
+#X connect 15 0 20 0;
+#X connect 16 0 17 0;
+#X connect 17 0 14 1;
+#X connect 18 0 19 0;
+#X connect 20 0 61 0;
+#X connect 21 0 23 0;
+#X connect 21 1 7 0;
+#X connect 22 0 25 0;
+#X connect 23 0 24 0;
+#X connect 24 0 22 0;
+#X connect 26 0 28 0;
+#X connect 27 0 26 0;
+#X connect 29 0 30 0;
+#X connect 31 0 13 0;
+#X connect 31 0 14 0;
+#X connect 32 0 33 0;
+#X connect 32 0 37 0;
+#X connect 32 0 40 0;
+#X connect 32 0 43 0;
+#X connect 32 0 13 0;
+#X connect 32 0 54 0;
+#X connect 32 0 60 0;
+#X connect 33 0 34 0;
+#X connect 37 0 45 0;
+#X connect 38 0 37 1;
+#X connect 39 0 0 0;
+#X connect 39 1 10 0;
+#X connect 39 1 32 0;
+#X connect 39 2 11 0;
+#X connect 39 3 16 0;
+#X connect 39 4 21 0;
+#X connect 39 5 56 0;
+#X connect 40 0 41 0;
+#X connect 41 0 53 0;
+#X connect 41 0 50 0;
+#X connect 42 0 43 1;
+#X connect 42 0 55 0;
+#X connect 43 0 44 0;
+#X connect 46 0 39 0;
+#X connect 47 0 48 1;
+#X connect 48 0 49 0;
+#X connect 49 0 40 1;
+#X connect 50 0 52 0;
+#X connect 51 0 50 1;
+#X connect 55 0 38 0;
+#X connect 56 0 48 0;
+#X connect 57 0 9 0;
+#X connect 58 0 9 0;
+#X connect 59 0 9 0;
+#X connect 61 0 12 0;
+#X restore 30 290 pd guts;
+#X msg 30 164 run the transformation;
+#X msg 30 227 hear the output buffer again;
+#X text 30 124 click below to:;
+#X msg 30 248 save the output buffer;
+#X floatatom 30 311 0 0 0;
+#X msg 30 143 read an input file;
+#X msg 30 269 save normalized to max amplitude;
+#N canvas 213 187 495 352 input-sample 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array array1 63024 float 0;
+#X coords 0 1 63023 -1 400 300 1;
+#X restore 55 22 graph;
+#X text 146 379 INPUT SAMPLE;
+#X restore 41 401 pd input-sample;
+#N canvas 192 180 507 343 output-sample 0;
+#N canvas 0 0 450 300 graph2 0;
+#X array array2 2.646e+06 float 0;
+#X coords 0 1 2.646e+06 -1 400 300 1;
+#X restore 58 13 graph;
+#X text 155 375 OUTPUT SAMPLE;
+#X restore 41 426 pd output-sample;
+#X floatatom 385 382 0 0 120;
+#N canvas 194 37 397 591 output 0;
+#X obj 64 196 t b;
+#X obj 64 147 f;
+#X obj 64 99 inlet;
+#X text 68 78 mute;
+#X obj 64 220 f;
+#X msg 130 235 0;
+#X msg 64 123 bang;
+#X obj 64 172 moses 1;
+#X obj 130 211 t b f;
+#X obj 93 427 outlet;
+#X msg 93 403 set \$1;
+#X obj 180 158 moses 1;
+#X obj 217 429 dbtorms;
+#X obj 217 454 pack 0 100;
+#X obj 180 133 r master-lvl;
+#X obj 93 369 r master-lvl;
+#X obj 80 276 s master-lvl;
+#X obj 217 478 s master-amp;
+#X obj 201 235 loadbang;
+#X msg 201 260 \; master-lvl 90;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X connect 18 0 19 0;
+#X restore 385 361 pd output;
+#X msg 385 340 mute;
+#X obj 385 403 s master-lvl;
+#X text 427 339 <-- mute button;
+#X text 422 381 <--set me;
+#X text 385 423 LINE OUT LEVEL in dB (100 norm);
+#X text 14 25 This is a Fourier-based analysis/resynthesis tool.;
+#X text 22 45 You can move forward or backward in the sample \, or
+"freeze" at any point using the "precession" and "location" controls.
+Transposition is in hundredths of a half-tone.;
+#X msg 30 185 start transformation when I change controls;
+#X msg 30 206 stop the transformation;
+#X text 731 329 examples:;
+#X msg 614 94 -40;
+#X connect 0 0 5 0;
+#X connect 1 0 4 1;
+#X connect 2 0 4 0;
+#X connect 3 0 2 0;
+#X connect 3 1 1 0;
+#X connect 6 0 7 0;
+#X connect 7 0 0 0;
+#X connect 23 0 25 0;
+#X connect 26 0 23 0;
+#X connect 29 0 34 0;
+#X connect 30 0 29 0;
+#X connect 31 0 29 0;
+#X connect 33 0 29 0;
+#X connect 35 0 29 0;
+#X connect 36 0 29 0;
+#X connect 39 0 42 0;
+#X connect 40 0 39 0;
+#X connect 41 0 40 0;
+#X connect 48 0 29 0;
+#X connect 49 0 29 0;
+#X connect 51 0 2 0;
diff --git a/pd/doc/7.stuff/soundfile-tools/4.looper.pd b/pd/doc/7.stuff/soundfile-tools/4.looper.pd
new file mode 100644
index 00000000..0b969a42
--- /dev/null
+++ b/pd/doc/7.stuff/soundfile-tools/4.looper.pd
@@ -0,0 +1,338 @@
+#N canvas 0 16 878 417 12;
+#N canvas 213 187 495 352 input-sample 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array array1 63024 float 0;
+#X coords 0 1 63023 -1 400 300 1;
+#X restore 56 23 graph;
+#X text 151 393 INPUT SAMPLE;
+#X restore 143 301 pd input-sample;
+#N canvas 192 180 507 343 output-sample 0;
+#N canvas 0 0 450 300 graph2 0;
+#X array array2 441000 float 0;
+#X coords 0 1 440999 -1 400 300 1;
+#X restore 60 13 graph;
+#X text 161 388 OUTPUT SAMPLE;
+#X restore 143 325 pd output-sample;
+#N canvas 41 102 912 552 guts 0;
+#X msg 25 133 bang;
+#X obj 15 468 openpanel;
+#X obj 144 28 inlet;
+#X obj 478 392 dac~;
+#X obj 478 342 *~;
+#X obj 494 301 line~;
+#X obj 494 276 r master-amp;
+#X msg 759 352 bang;
+#X obj 759 377 savepanel;
+#X obj 274 203 spigot;
+#X msg 248 133 0;
+#X msg 281 133 1;
+#X obj 555 384 outlet;
+#X obj 268 325 tabwrite~ array2;
+#X msg 262 232 bang;
+#X obj 456 312 +~;
+#X obj 478 367 hip~ 7;
+#X msg 480 228 bang;
+#X obj 480 253 tabplay~ array2;
+#X msg 759 402 write \$1 array2;
+#X obj 759 427 soundfiler;
+#N canvas 92 118 921 631 audio-transformation 0;
+#X obj 320 509 outlet~;
+#X obj 221 41 r transposition;
+#X obj 287 175 r looplength;
+#X obj 221 66 / 120;
+#X obj 235 139 pow;
+#X obj 221 91 t b f;
+#X msg 221 115 2;
+#X text 305 107 2 to the power (octaves);
+#X text 304 123 gives speed change for the;
+#X text 305 141 desired transposition;
+#X text 280 65 transposition in octaves;
+#X obj 470 264 * 441;
+#X floatatom 470 234 0 0 0;
+#X obj 426 352 samphold~;
+#X obj 545 335 -~ 0.5;
+#X obj 545 461 clip~ -0.5 0.5;
+#X obj 545 437 *~ 1;
+#X obj 545 486 cos~;
+#X obj 545 365 wrap~;
+#X obj 545 400 -~ 0.5;
+#X obj 545 532 *~ -0.5;
+#X obj 545 508 -~ 1;
+#X floatatom 617 406 0 0 0;
+#X obj 601 254 r smoothing;
+#X obj 470 208 r startpoint;
+#X obj 617 431 max 1;
+#X obj 602 308 max 0.001;
+#X obj 602 334 t b f;
+#X obj 602 358 1;
+#X obj 617 382 /;
+#X obj 320 482 *~;
+#X obj 320 456 tabread4~ array1;
+#X obj 320 430 +~;
+#X obj 227 472 print~;
+#X msg 227 443 bang;
+#X obj 601 61 loadbang;
+#X msg 598 91 \; transposition 0 \; looplength 0 \; startpoint 0 \;
+smoothing 0;
+#X obj 236 263 ../../../extra/loop~;
+#X obj 285 233 * 441;
+#X obj 284 307 *~;
+#X obj 133 41 loadbang;
+#X obj 78 174 r running;
+#X obj 78 203 sel 1;
+#X obj 602 282 * 0.01;
+#X obj 286 202 max 0.01;
+#X connect 1 0 3 0;
+#X connect 2 0 44 0;
+#X connect 3 0 5 0;
+#X connect 4 0 37 0;
+#X connect 5 0 6 0;
+#X connect 5 1 4 1;
+#X connect 6 0 4 0;
+#X connect 11 0 13 0;
+#X connect 12 0 11 0;
+#X connect 13 0 32 1;
+#X connect 14 0 18 0;
+#X connect 15 0 17 0;
+#X connect 16 0 15 0;
+#X connect 17 0 21 0;
+#X connect 18 0 19 0;
+#X connect 19 0 16 0;
+#X connect 20 0 30 1;
+#X connect 21 0 20 0;
+#X connect 22 0 25 0;
+#X connect 23 0 43 0;
+#X connect 24 0 12 0;
+#X connect 25 0 16 1;
+#X connect 26 0 27 0;
+#X connect 27 0 28 0;
+#X connect 27 1 29 1;
+#X connect 28 0 29 0;
+#X connect 29 0 22 0;
+#X connect 30 0 0 0;
+#X connect 31 0 30 0;
+#X connect 32 0 31 0;
+#X connect 32 0 33 0;
+#X connect 34 0 33 0;
+#X connect 35 0 36 0;
+#X connect 37 0 13 1;
+#X connect 37 0 39 0;
+#X connect 37 0 14 0;
+#X connect 37 1 39 1;
+#X connect 38 0 37 1;
+#X connect 39 0 32 0;
+#X connect 40 0 3 0;
+#X connect 41 0 42 0;
+#X connect 42 0 37 0;
+#X connect 43 0 26 0;
+#X connect 44 0 38 0;
+#X restore 274 283 pd audio-transformation;
+#X obj 553 331 env~ 16384;
+#X obj 665 317 route normalized;
+#X msg 667 457 write -normalize \$1 array2;
+#X msg 667 407 bang;
+#X obj 667 432 savepanel;
+#X obj 667 482 soundfiler;
+#X obj 15 518 soundfiler;
+#X msg 15 493 read -resize -maxsize 1e+06 \$1 array1;
+#X obj 330 100 r transposition;
+#X obj 330 125 r looplength;
+#X obj 330 175 r smoothing;
+#X obj 708 110 timer;
+#X obj 708 135 * 44.1;
+#X obj 724 37 r running;
+#X obj 708 60 f;
+#X obj 708 85 sel 1;
+#X obj 708 227 moses;
+#X obj 753 227 r maxoutsize;
+#X msg 708 252 \; array2 resize \$1;
+#X msg 685 37 bang;
+#X obj 144 53 route read run start hear save stop;
+#X obj 312 427 delay;
+#X obj 331 404 + 100;
+#X obj 145 329 r maxoutsize;
+#X obj 86 329 f;
+#X msg 86 354 \; array2 resize \$1;
+#X msg 312 452 \; action stop;
+#X obj 331 379 / 44.1;
+#X msg 62 271 \; pd dsp 1;
+#X msg 334 240 \; running 1;
+#X obj 202 28 r action;
+#X msg 733 160 \; running 0 \; pd dsp 0;
+#X obj 330 150 r startpoint;
+#X obj 553 357 int;
+#X connect 0 0 1 0;
+#X connect 1 0 29 0;
+#X connect 2 0 42 0;
+#X connect 4 0 16 0;
+#X connect 5 0 4 1;
+#X connect 6 0 5 0;
+#X connect 7 0 8 0;
+#X connect 8 0 19 0;
+#X connect 9 0 10 0;
+#X connect 9 0 14 0;
+#X connect 10 0 9 1;
+#X connect 11 0 9 1;
+#X connect 14 0 13 0;
+#X connect 14 0 46 0;
+#X connect 14 0 43 0;
+#X connect 14 0 50 0;
+#X connect 14 0 51 0;
+#X connect 14 0 33 0;
+#X connect 15 0 4 0;
+#X connect 15 0 22 0;
+#X connect 16 0 3 0;
+#X connect 16 0 3 1;
+#X connect 17 0 18 0;
+#X connect 18 0 15 1;
+#X connect 19 0 20 0;
+#X connect 21 0 13 0;
+#X connect 21 0 15 0;
+#X connect 22 0 55 0;
+#X connect 23 0 25 0;
+#X connect 23 1 7 0;
+#X connect 24 0 27 0;
+#X connect 25 0 26 0;
+#X connect 26 0 24 0;
+#X connect 29 0 28 0;
+#X connect 30 0 9 0;
+#X connect 31 0 9 0;
+#X connect 32 0 9 0;
+#X connect 33 0 34 0;
+#X connect 34 0 53 0;
+#X connect 34 0 38 0;
+#X connect 35 0 36 1;
+#X connect 36 0 37 0;
+#X connect 37 0 33 1;
+#X connect 38 0 40 0;
+#X connect 39 0 38 1;
+#X connect 41 0 36 0;
+#X connect 42 0 0 0;
+#X connect 42 1 14 0;
+#X connect 42 1 10 0;
+#X connect 42 2 11 0;
+#X connect 42 3 17 0;
+#X connect 42 4 23 0;
+#X connect 42 5 41 0;
+#X connect 43 0 48 0;
+#X connect 44 0 43 1;
+#X connect 45 0 46 1;
+#X connect 45 0 49 0;
+#X connect 46 0 47 0;
+#X connect 49 0 44 0;
+#X connect 52 0 42 0;
+#X connect 54 0 9 0;
+#X connect 55 0 12 0;
+#X restore 19 228 pd guts;
+#X msg 19 96 run the transformation;
+#X msg 19 163 hear the output buffer again;
+#X text 19 55 click below to:;
+#X msg 19 185 save the output buffer;
+#X floatatom 367 337 0 0 120;
+#N canvas 194 37 397 591 output 0;
+#X obj 66 203 t b;
+#X obj 66 152 f;
+#X obj 66 102 inlet;
+#X text 71 81 mute;
+#X obj 66 228 f;
+#X msg 134 244 0;
+#X msg 66 127 bang;
+#X obj 66 178 moses 1;
+#X obj 134 218 t b f;
+#X obj 96 442 outlet;
+#X msg 96 416 set \$1;
+#X obj 186 163 moses 1;
+#X obj 224 444 dbtorms;
+#X obj 224 469 pack 0 100;
+#X obj 186 138 r master-lvl;
+#X obj 96 382 r master-lvl;
+#X obj 83 286 s master-lvl;
+#X obj 224 494 s master-amp;
+#X obj 208 244 loadbang;
+#X msg 208 269 \; master-lvl 90;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X connect 18 0 19 0;
+#X restore 367 316 pd output;
+#X msg 367 294 mute;
+#X obj 367 359 s master-lvl;
+#X text 410 293 <-- mute button;
+#X floatatom 19 250 0 0 0;
+#X text 20 294 100 maximum;
+#X text 20 276 output meter;
+#X text 405 336 <--set me;
+#X msg 19 74 read an input file;
+#X text 367 379 LINE OUT LEVEL in dB (100 norm);
+#X msg 19 206 save normalized to max amplitude;
+#X floatatom 368 73 0 0 1000;
+#X floatatom 369 19 0 0 0;
+#X obj 369 47 s transposition;
+#X floatatom 369 170 0 0 100;
+#X obj 369 192 s smoothing;
+#X obj 368 95 s looplength;
+#X text 418 73 <- loop length \, hundredths of a second;
+#X floatatom 369 122 0 0 60000;
+#X obj 369 144 s startpoint;
+#X text 420 123 <- start point \, hundredths of a second;
+#X text 419 171 <- envelope smoothing \, 0-100;
+#X text 38 9 looping sample player.;
+#X msg 19 118 start looping when I change something;
+#X msg 19 140 stop looping;
+#X floatatom 368 239 0 0 60;
+#N canvas 0 0 265 196 length 0;
+#X obj 48 24 inlet;
+#X obj 49 104 * 44100;
+#X msg 86 130 \; array2 resize \$1;
+#X obj 48 49 min 60;
+#X obj 62 78 s output-length;
+#X obj 48 164 s maxoutsize;
+#X connect 0 0 3 0;
+#X connect 1 0 2 0;
+#X connect 1 0 5 0;
+#X connect 3 0 1 0;
+#X connect 3 0 4 0;
+#X restore 368 260 pd length;
+#N canvas 219 38 198 151 /SUBPATCH/ 0;
+#X obj 79 122 outlet;
+#X obj 79 74 loadbang;
+#X msg 79 98 10;
+#X connect 1 0 2 0;
+#X connect 2 0 0 0;
+#X restore 368 217 pd;
+#X text 455 241 length in seconds of the output;
+#X text 453 259 buffer... maximum 60;
+#X text 404 241 <- set;
+#X text 408 20 <- transposition up or down \, 10ths of a half step
+;
+#X connect 2 0 12 0;
+#X connect 3 0 2 0;
+#X connect 4 0 2 0;
+#X connect 6 0 2 0;
+#X connect 7 0 10 0;
+#X connect 8 0 7 0;
+#X connect 9 0 8 0;
+#X connect 16 0 2 0;
+#X connect 18 0 2 0;
+#X connect 19 0 24 0;
+#X connect 20 0 21 0;
+#X connect 22 0 23 0;
+#X connect 26 0 27 0;
+#X connect 31 0 2 0;
+#X connect 32 0 2 0;
+#X connect 33 0 34 0;
+#X connect 35 0 33 0;
diff --git a/pd/doc/7.stuff/soundfile-tools/5.reverb.pd b/pd/doc/7.stuff/soundfile-tools/5.reverb.pd
new file mode 100644
index 00000000..0b0cdb11
--- /dev/null
+++ b/pd/doc/7.stuff/soundfile-tools/5.reverb.pd
@@ -0,0 +1,214 @@
+#N canvas 186 43 739 364 12;
+#N canvas 213 187 495 352 input-sample 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array array1 63024 float 0;
+#X coords 0 1 63023 -1 400 300 1;
+#X restore 54 22 graph;
+#X text 145 376 INPUT SAMPLE;
+#X restore 154 226 pd input-sample;
+#N canvas 192 180 507 343 output-sample 0;
+#N canvas 0 0 450 300 graph2 0;
+#X array array2 504024 float 0;
+#X coords 0 1 504023 -1 400 300 1;
+#X restore 57 13 graph;
+#X text 154 372 OUTPUT SAMPLE;
+#X restore 155 249 pd output-sample;
+#N canvas 116 150 735 421 guts 0;
+#X msg 24 128 bang;
+#X obj 24 345 openpanel;
+#X obj 138 30 inlet;
+#X obj 446 368 dac~;
+#X obj 446 327 *~;
+#X obj 461 304 line~;
+#X obj 461 283 r master-amp;
+#X msg 707 85 bang;
+#X obj 707 106 savepanel;
+#X obj 268 161 spigot;
+#X msg 253 128 0;
+#X msg 284 128 1;
+#X obj 500 398 outlet;
+#X obj 316 128 r frequency;
+#X obj 256 312 tabwrite~ array2;
+#X msg 256 189 bang;
+#X obj 427 276 +~;
+#X msg 139 128 \; pd dsp 1;
+#X obj 446 347 hip~ 7;
+#X obj 268 217 tabplay~ array1;
+#X msg 442 124 bang;
+#X obj 442 145 tabplay~ array2;
+#X msg 707 126 write \$1 array2;
+#X obj 707 147 soundfiler;
+#X obj 138 51 route read run start hear save;
+#N canvas 0 0 632 395 audio-transformation 0;
+#X obj 101 49 inlet~;
+#X obj 105 268 outlet~;
+#X obj 101 148 ../../../extra/rev1~ xxx;
+#X obj 339 79 r revgain;
+#X obj 338 102 dbtorms;
+#X obj 338 130 pack 0 50;
+#X obj 338 154 line~;
+#X obj 103 204 *~;
+#X obj 181 51 r revtime;
+#X obj 213 236 *~;
+#X obj 340 209 dbtorms;
+#X obj 340 237 pack 0 50;
+#X obj 340 261 line~;
+#X obj 294 37 inlet;
+#X msg 293 61 bang;
+#X obj 342 186 r drygain;
+#X connect 0 0 2 0;
+#X connect 0 0 9 0;
+#X connect 2 0 7 0;
+#X connect 3 0 4 0;
+#X connect 4 0 5 0;
+#X connect 5 0 6 0;
+#X connect 6 0 7 1;
+#X connect 7 0 1 0;
+#X connect 8 0 2 1;
+#X connect 9 0 1 0;
+#X connect 10 0 11 0;
+#X connect 11 0 12 0;
+#X connect 12 0 9 1;
+#X connect 13 0 14 0;
+#X connect 14 0 2 2;
+#X connect 15 0 10 0;
+#X restore 268 238 pd audio-transformation;
+#X obj 500 377 env~ 16384;
+#X obj 570 86 route normalized;
+#X msg 570 179 write -normalize \$1 array2;
+#X msg 570 138 bang;
+#X obj 570 159 savepanel;
+#X obj 570 204 soundfiler;
+#X obj 24 396 soundfiler;
+#X msg 24 374 read -resize -maxsize 1e+06 \$1 array1;
+#X msg 24 440 \; array2 resize \$1;
+#X obj 402 129 r q;
+#X obj 24 419 + 441000;
+#X connect 0 0 1 0;
+#X connect 1 0 33 0;
+#X connect 2 0 24 0;
+#X connect 4 0 18 0;
+#X connect 5 0 4 1;
+#X connect 6 0 5 0;
+#X connect 7 0 8 0;
+#X connect 8 0 22 0;
+#X connect 9 0 10 0;
+#X connect 9 0 15 0;
+#X connect 10 0 9 1;
+#X connect 11 0 9 1;
+#X connect 13 0 9 0;
+#X connect 15 0 14 0;
+#X connect 15 0 19 0;
+#X connect 15 0 25 1;
+#X connect 16 0 4 0;
+#X connect 18 0 3 0;
+#X connect 18 0 3 1;
+#X connect 18 0 26 0;
+#X connect 19 0 25 0;
+#X connect 20 0 21 0;
+#X connect 21 0 16 1;
+#X connect 22 0 23 0;
+#X connect 24 0 0 0;
+#X connect 24 1 15 0;
+#X connect 24 1 10 0;
+#X connect 24 1 17 0;
+#X connect 24 2 11 0;
+#X connect 24 2 17 0;
+#X connect 24 3 20 0;
+#X connect 24 4 27 0;
+#X connect 25 0 14 0;
+#X connect 25 0 16 0;
+#X connect 26 0 12 0;
+#X connect 27 0 29 0;
+#X connect 27 1 7 0;
+#X connect 28 0 31 0;
+#X connect 29 0 30 0;
+#X connect 30 0 28 0;
+#X connect 32 0 36 0;
+#X connect 33 0 32 0;
+#X connect 35 0 9 0;
+#X connect 36 0 34 0;
+#X restore 35 190 pd guts;
+#X msg 35 85 run the transformation;
+#X msg 35 127 hear the output buffer again;
+#X text 35 45 click below to:;
+#X msg 35 148 save the output buffer;
+#X floatatom 445 285 0 0 120;
+#N canvas 194 37 397 591 output 0;
+#X obj 63 194 t b;
+#X obj 63 146 f;
+#X obj 63 98 inlet;
+#X text 68 77 mute;
+#X obj 63 218 f;
+#X msg 129 233 0;
+#X msg 63 122 bang;
+#X obj 63 170 moses 1;
+#X obj 129 209 t b f;
+#X obj 92 423 outlet;
+#X msg 92 399 set \$1;
+#X obj 178 156 moses 1;
+#X obj 215 425 dbtorms;
+#X obj 215 450 pack 0 100;
+#X obj 178 132 r master-lvl;
+#X obj 92 366 r master-lvl;
+#X obj 79 274 s master-lvl;
+#X obj 215 474 s master-amp;
+#X obj 199 233 loadbang;
+#X msg 199 258 \; master-lvl 90;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X connect 18 0 19 0;
+#X restore 445 264 pd output;
+#X msg 445 243 mute;
+#X obj 445 306 s master-lvl;
+#X text 486 242 <-- mute button;
+#X floatatom 35 211 0 0 0;
+#X text 13 251 100 maximum;
+#X text 13 233 output meter;
+#X text 482 284 <--set me;
+#X msg 35 64 read an input file;
+#X text 445 326 LINE OUT LEVEL in dB (100 norm);
+#X msg 35 169 save normalized to max amplitude;
+#X msg 35 106 start transformation when I change f or q;
+#X floatatom 445 82 0 0 120;
+#X floatatom 445 40 0 0 100;
+#X obj 445 61 s revtime;
+#X obj 445 103 s revgain;
+#X floatatom 446 184 0 0 120;
+#X text 494 84 <-- reverb gain;
+#X text 482 185 <-- dry gain;
+#X obj 446 205 s drygain;
+#X obj 446 142 loadbang;
+#X msg 446 163 100;
+#X text 486 39 <- reverb time 0-100;
+#X text 23 15 Reverberator. Read in a sample first.;
+#X connect 2 0 12 0;
+#X connect 3 0 2 0;
+#X connect 4 0 2 0;
+#X connect 6 0 2 0;
+#X connect 7 0 10 0;
+#X connect 8 0 7 0;
+#X connect 9 0 8 0;
+#X connect 16 0 2 0;
+#X connect 18 0 2 0;
+#X connect 19 0 2 0;
+#X connect 20 0 23 0;
+#X connect 21 0 22 0;
+#X connect 24 0 27 0;
+#X connect 28 0 29 0;
+#X connect 29 0 24 0;
diff --git a/pd/doc/7.stuff/soundfile-tools/6.vocoder.pd b/pd/doc/7.stuff/soundfile-tools/6.vocoder.pd
new file mode 100644
index 00000000..ce822ef4
--- /dev/null
+++ b/pd/doc/7.stuff/soundfile-tools/6.vocoder.pd
@@ -0,0 +1,314 @@
+#N canvas 73 102 706 428 12;
+#X floatatom 462 162 0 0 100;
+#X msg 462 137 set \$1;
+#X text 29 322 100 maximum;
+#X text 29 304 output meter;
+#N canvas 145 136 937 540 guts 0;
+#X msg 5 296 bang;
+#X obj 5 321 openpanel;
+#X obj 136 29 inlet;
+#X obj 452 375 dac~;
+#X obj 452 323 *~;
+#X obj 467 300 line~;
+#X obj 467 280 r master-amp;
+#X msg 689 157 bang;
+#X obj 689 177 savepanel;
+#X obj 506 393 outlet;
+#X obj 297 238 tabwrite~ array2;
+#X obj 454 238 +~;
+#X obj 452 343 hip~ 7;
+#X msg 446 108 bang;
+#X obj 446 133 tabplay~ array2;
+#X msg 689 197 write \$1 array2;
+#X obj 689 218 soundfiler;
+#X obj 506 373 env~ 16384;
+#X obj 587 129 route normalized;
+#X msg 587 250 write -normalize \$1 array2;
+#X msg 587 209 bang;
+#X obj 587 230 savepanel;
+#X obj 587 274 soundfiler;
+#X obj 5 365 soundfiler;
+#X msg 5 343 read -resize -maxsize 1e+06 \$1 array1;
+#X obj 676 338 loadbang;
+#N canvas 0 0 690 470 fft-analysis 0;
+#X obj 275 314 *~;
+#X obj 257 348 *~;
+#X obj 218 348 *~;
+#X obj 105 163 *~;
+#X obj 66 156 *~;
+#X obj 66 182 +~;
+#X obj 216 104 *~;
+#X obj 253 104 inlet~;
+#X obj 216 130 rfft~;
+#X obj 42 371 *~;
+#X floatatom 458 208 0 0 0;
+#X obj 334 177 *~;
+#X obj 66 104 *~;
+#X obj 103 104 inlet~;
+#X obj 45 65 tabreceive~ hanning;
+#X obj 66 130 rfft~;
+#X obj 218 374 rifft~;
+#X obj 42 397 outlet~;
+#X obj 297 177 *~;
+#X obj 297 203 +~;
+#X obj 307 314 sig~ 0.001;
+#X text 122 214 modulus;
+#X obj 66 208 sqrt~;
+#X obj 275 288 *~;
+#X obj 457 58 r squelch;
+#X obj 329 418 block~ 1024 4;
+#X obj 297 229 rsqrt~;
+#X obj 341 203 sig~ 1e-20;
+#X obj 297 255 clip~;
+#X obj 458 130 t f f;
+#X obj 458 156 *;
+#X obj 458 182 * 0.01;
+#X obj 456 94 max 1;
+#X obj 559 58 loadbang;
+#X obj 458 234 max 0;
+#X connect 0 0 1 1;
+#X connect 0 0 2 1;
+#X connect 1 0 16 1;
+#X connect 2 0 16 0;
+#X connect 3 0 5 1;
+#X connect 4 0 5 0;
+#X connect 5 0 22 0;
+#X connect 6 0 8 0;
+#X connect 7 0 6 1;
+#X connect 8 0 18 0;
+#X connect 8 0 18 1;
+#X connect 8 0 2 0;
+#X connect 8 1 11 0;
+#X connect 8 1 11 1;
+#X connect 8 1 1 0;
+#X connect 9 0 17 0;
+#X connect 10 0 34 0;
+#X connect 11 0 19 1;
+#X connect 12 0 15 0;
+#X connect 13 0 12 1;
+#X connect 14 0 12 0;
+#X connect 14 0 6 0;
+#X connect 14 0 9 0;
+#X connect 15 0 4 0;
+#X connect 15 0 4 1;
+#X connect 15 1 3 0;
+#X connect 15 1 3 1;
+#X connect 16 0 9 1;
+#X connect 18 0 19 0;
+#X connect 19 0 26 0;
+#X connect 20 0 0 1;
+#X connect 22 0 23 0;
+#X connect 23 0 0 0;
+#X connect 24 0 32 0;
+#X connect 26 0 28 0;
+#X connect 27 0 26 0;
+#X connect 28 0 23 1;
+#X connect 29 0 30 0;
+#X connect 29 0 30 1;
+#X connect 30 0 31 0;
+#X connect 31 0 10 0;
+#X connect 32 0 29 0;
+#X connect 33 0 32 0;
+#X connect 34 0 28 2;
+#X restore 296 194 pd fft-analysis;
+#X msg 202 86 bang;
+#N canvas 46 0 723 534 hanning-window 0;
+#X obj 122 273 phasor~;
+#X obj 122 311 cos~;
+#X obj 31 436 tabwrite~ hanning;
+#X obj 40 336 -~;
+#X obj 37 290 sig~ 1;
+#X msg 50 240 0;
+#X text 188 18 CALCULATE HANNING;
+#X text 188 36 WINDOW TABLE;
+#N canvas 0 0 450 300 graph1 0;
+#X array hanning 1024 float 0;
+#X coords 0 1 1023 -1 400 300 1;
+#X restore 342 235 graph;
+#X obj 123 227 sig~;
+#X text 156 173 sample rate / window size;
+#X msg 31 191 bang;
+#X obj 88 357 sig~ 0.5;
+#X obj 66 399 *~;
+#X obj 124 106 samplerate~;
+#X obj 33 31 r window-size;
+#X obj 123 175 /;
+#X msg 262 106 \; hanning resize \$1;
+#X obj 31 70 t b f f;
+#X connect 0 0 1 0;
+#X connect 1 0 3 1;
+#X connect 3 0 13 0;
+#X connect 4 0 3 0;
+#X connect 5 0 0 1;
+#X connect 9 0 0 0;
+#X connect 11 0 2 0;
+#X connect 11 0 5 0;
+#X connect 12 0 13 1;
+#X connect 13 0 2 0;
+#X connect 14 0 16 0;
+#X connect 15 0 18 0;
+#X connect 16 0 9 0;
+#X connect 18 0 14 0;
+#X connect 18 0 11 0;
+#X connect 18 1 16 1;
+#X connect 18 2 17 0;
+#X restore 673 403 pd hanning-window;
+#X obj 181 29 r action;
+#X msg 150 163 \; pd dsp 1;
+#X msg 389 106 stop;
+#X msg 58 455 \; array2 resize \$1;
+#X obj 58 434 + 4410;
+#X obj 136 50 route read AND run hear save stop;
+#X obj 58 412 min;
+#X obj 107 380 t b f;
+#X obj 107 270 openpanel;
+#X obj 107 314 soundfiler;
+#X msg 107 293 read -resize -maxsize 1e+06 \$1 array3;
+#X msg 106 239 bang;
+#X msg 676 360 \; window-size 1024 \;;
+#X obj 296 145 tabplay~ array1;
+#X obj 314 167 tabplay~ array3;
+#X connect 0 0 1 0;
+#X connect 1 0 24 0;
+#X connect 2 0 34 0;
+#X connect 4 0 12 0;
+#X connect 5 0 4 1;
+#X connect 6 0 5 0;
+#X connect 7 0 8 0;
+#X connect 8 0 15 0;
+#X connect 11 0 4 0;
+#X connect 12 0 3 0;
+#X connect 12 0 3 1;
+#X connect 12 0 17 0;
+#X connect 13 0 14 0;
+#X connect 14 0 11 1;
+#X connect 15 0 16 0;
+#X connect 17 0 9 0;
+#X connect 18 0 20 0;
+#X connect 18 1 7 0;
+#X connect 19 0 22 0;
+#X connect 20 0 21 0;
+#X connect 21 0 19 0;
+#X connect 23 0 35 0;
+#X connect 24 0 23 0;
+#X connect 25 0 41 0;
+#X connect 26 0 10 0;
+#X connect 26 0 11 0;
+#X connect 27 0 10 0;
+#X connect 27 0 30 0;
+#X connect 27 0 42 0;
+#X connect 27 0 43 0;
+#X connect 29 0 34 0;
+#X connect 31 0 14 0;
+#X connect 33 0 32 0;
+#X connect 34 0 0 0;
+#X connect 34 1 40 0;
+#X connect 34 2 27 0;
+#X connect 34 3 13 0;
+#X connect 34 4 18 0;
+#X connect 34 5 31 0;
+#X connect 35 0 33 0;
+#X connect 36 0 35 0;
+#X connect 36 1 35 1;
+#X connect 37 0 39 0;
+#X connect 38 0 36 0;
+#X connect 39 0 38 0;
+#X connect 40 0 37 0;
+#X connect 42 0 26 0;
+#X connect 43 0 26 1;
+#X restore 29 263 pd guts;
+#X msg 29 158 run the transformation;
+#X msg 29 200 hear the output buffer again;
+#X text 29 97 click below to:;
+#X msg 29 221 save the output buffer;
+#X floatatom 29 284 0 0 0;
+#X msg 29 242 save normalized to max amplitude;
+#N canvas 130 10 488 287 input-sample 0;
+#N canvas 0 0 450 300 graph1 0;
+#X array array1 188955 float 0;
+#X coords 0 1 188954 -1 400 100 1;
+#X restore 53 21 graph;
+#N canvas 0 0 450 300 graph3 0;
+#X array array3 225280 float 0;
+#X coords 0 1 225279 -1 400 100 1;
+#X restore 54 146 graph;
+#X text 227 279 INPUT SAMPLES;
+#X restore 169 368 pd input-sample;
+#N canvas 192 180 507 343 output-sample 0;
+#N canvas 0 0 450 300 graph2 0;
+#X array array2 193365 float 0;
+#X coords 0 1 193364 -1 400 300 1;
+#X restore 56 12 graph;
+#X text 151 365 OUTPUT SAMPLE;
+#X restore 168 393 pd output-sample;
+#X floatatom 408 365 0 0 0;
+#N canvas 194 37 397 591 output 0;
+#X obj 62 191 t b;
+#X obj 62 144 f;
+#X obj 62 96 inlet;
+#X text 67 76 mute;
+#X obj 62 215 f;
+#X msg 127 229 0;
+#X msg 62 120 bang;
+#X obj 62 167 moses 1;
+#X obj 127 206 t b f;
+#X obj 90 416 outlet;
+#X msg 90 392 set \$1;
+#X obj 175 154 moses 1;
+#X obj 211 418 dbtorms;
+#X obj 211 442 pack 0 100;
+#X obj 175 130 r master-lvl;
+#X obj 90 359 r master-lvl;
+#X obj 78 269 s master-lvl;
+#X obj 211 466 s master-amp;
+#X obj 195 229 loadbang;
+#X msg 195 253 \; master-lvl 90;
+#X connect 0 0 4 0;
+#X connect 1 0 7 0;
+#X connect 2 0 6 0;
+#X connect 4 0 16 0;
+#X connect 5 0 16 0;
+#X connect 6 0 1 0;
+#X connect 7 0 0 0;
+#X connect 7 1 8 0;
+#X connect 8 0 5 0;
+#X connect 10 0 9 0;
+#X connect 11 1 4 1;
+#X connect 12 0 13 0;
+#X connect 13 0 17 0;
+#X connect 14 0 1 1;
+#X connect 14 0 11 0;
+#X connect 15 0 10 0;
+#X connect 15 0 12 0;
+#X connect 18 0 19 0;
+#X restore 408 344 pd output;
+#X msg 408 323 mute;
+#X obj 408 386 s master-lvl;
+#X text 449 322 <-- mute button;
+#X text 444 363 <--set me;
+#X text 408 406 LINE OUT LEVEL in dB (100 norm);
+#X msg 29 179 stop the transformation;
+#X text 193 9 (old-fashioned) VOCODER;
+#X text 28 31 This takes in two soundfiles and uses the first to "vocode"
+the second. THe resulting sound is as long as the shorter of the two
+inputs.;
+#X msg 29 116 read the analysis sound from file;
+#X msg 29 137 AND read the sound to be processed from file;
+#X text 462 97 SQUELCH;
+#X obj 462 116 r squelch;
+#X obj 462 187 s squelch;
+#X text 526 161 1-100 or so;
+#X connect 0 0 27 0;
+#X connect 1 0 0 0;
+#X connect 4 0 9 0;
+#X connect 5 0 4 0;
+#X connect 6 0 4 0;
+#X connect 8 0 4 0;
+#X connect 10 0 4 0;
+#X connect 13 0 16 0;
+#X connect 14 0 13 0;
+#X connect 15 0 14 0;
+#X connect 20 0 4 0;
+#X connect 23 0 4 0;
+#X connect 24 0 4 0;
+#X connect 26 0 1 0;
diff --git a/pd/doc/7.stuff/soundfile-tools/README.txt b/pd/doc/7.stuff/soundfile-tools/README.txt
new file mode 100644
index 00000000..1670af3f
--- /dev/null
+++ b/pd/doc/7.stuff/soundfile-tools/README.txt
@@ -0,0 +1,2 @@
+Here are some standard audio transformations packaged to work with mono
+soundfiles.
diff --git a/pd/doc/7.stuff/synth/1.poly.synth.pd b/pd/doc/7.stuff/synth/1.poly.synth.pd
new file mode 100644
index 00000000..bd2a95a8
--- /dev/null
+++ b/pd/doc/7.stuff/synth/1.poly.synth.pd
@@ -0,0 +1,311 @@
+#N canvas 232 162 657 719 12;
+#X floatatom 424 666 0 0 100;
+#N canvas 269 205 698 344 output 0;
+#X obj 388 156 t b;
+#X obj 388 105 f;
+#X obj 388 54 inlet;
+#X obj 388 181 f;
+#X msg 482 174 0;
+#X msg 388 79 bang;
+#X obj 388 130 moses 1;
+#X obj 482 149 t b f;
+#X obj 444 111 moses 1;
+#X obj 91 148 dbtorms;
+#X obj 444 86 r master-lvl;
+#X obj 91 48 r master-lvl;
+#X obj 388 207 s master-lvl;
+#X obj 28 169 inlet~;
+#X obj 213 195 inlet;
+#X obj 229 218 s master-lvl;
+#X msg 101 72 set \$1;
+#X obj 101 98 outlet;
+#X msg 213 241 \; pd dsp 1;
+#X obj 91 201 line~;
+#X obj 31 219 *~;
+#X obj 31 247 dac~;
+#X obj 91 173 pack 0 50;
+#X text 17 149 audio in;
+#X obj 28 194 hip~ 1;
+#X connect 0 0 3 0;
+#X connect 1 0 6 0;
+#X connect 2 0 5 0;
+#X connect 3 0 12 0;
+#X connect 4 0 12 0;
+#X connect 5 0 1 0;
+#X connect 6 0 0 0;
+#X connect 6 1 7 0;
+#X connect 7 0 4 0;
+#X connect 8 1 3 1;
+#X connect 9 0 22 0;
+#X connect 10 0 1 1;
+#X connect 10 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 16 0;
+#X connect 13 0 24 0;
+#X connect 14 0 15 0;
+#X connect 14 0 18 0;
+#X connect 16 0 17 0;
+#X connect 19 0 20 1;
+#X connect 20 0 21 0;
+#X connect 20 0 21 1;
+#X connect 22 0 19 0;
+#X connect 24 0 20 0;
+#X restore 386 690 pd output;
+#X msg 462 666 MUTE;
+#X obj 16 382 unpack;
+#X obj 16 299 notein;
+#X obj 16 327 pack;
+#X obj 329 213 numset amp x;
+#X obj 329 242 numset aa x;
+#X obj 329 329 numset ar x;
+#X obj 329 271 numset ad x;
+#X obj 329 300 numset as x;
+#N canvas 248 85 884 761 synth 0;
+#X obj 114 588 synthvoice;
+#X obj 114 561 synthvoice;
+#X obj 114 534 synthvoice;
+#X obj 114 507 synthvoice;
+#X obj 114 480 synthvoice;
+#X obj 114 453 synthvoice;
+#X obj 114 426 synthvoice;
+#X obj 114 399 synthvoice;
+#X obj 40 91 t b f;
+#X obj 22 185 f;
+#X obj 44 130 + 1;
+#X obj 44 158 mod 1e+06;
+#X obj 317 670 outlet~;
+#X obj 45 24 r syn-note;
+#X obj 454 25 r syn-noteon;
+#X text 445 652 todo: field to stamp note for later messages;
+#X obj 22 212 + 1e+06;
+#X obj 55 239 makenote;
+#X obj 109 309 moses 1e+06;
+#X obj 26 352 r all-off;
+#X msg 26 377 stop;
+#X obj 196 368 route 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16;
+#X obj 317 644 synthvoice;
+#X obj 317 617 synthvoice;
+#X obj 317 590 synthvoice;
+#X obj 317 563 synthvoice;
+#X obj 317 536 synthvoice;
+#X obj 317 509 synthvoice;
+#X obj 317 482 synthvoice;
+#X obj 317 455 synthvoice;
+#X obj 55 279 poly 16 1;
+#X obj 45 54 unpack 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
+#X obj 455 51 unpack 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
+#X obj 196 339 pack 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
+#X connect 0 0 29 0;
+#X connect 1 0 0 0;
+#X connect 2 0 1 0;
+#X connect 3 0 2 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 5 0;
+#X connect 7 0 6 0;
+#X connect 8 0 9 0;
+#X connect 8 1 33 2;
+#X connect 9 0 10 0;
+#X connect 9 0 16 0;
+#X connect 10 0 11 0;
+#X connect 11 0 9 1;
+#X connect 13 0 31 0;
+#X connect 14 0 32 0;
+#X connect 16 0 17 0;
+#X connect 17 0 30 0;
+#X connect 17 1 30 1;
+#X connect 18 0 33 2;
+#X connect 19 0 20 0;
+#X connect 20 0 30 0;
+#X connect 21 0 7 1;
+#X connect 21 1 6 1;
+#X connect 21 2 5 1;
+#X connect 21 3 4 1;
+#X connect 21 4 3 1;
+#X connect 21 5 2 1;
+#X connect 21 6 1 1;
+#X connect 21 7 0 1;
+#X connect 21 8 29 1;
+#X connect 21 9 28 1;
+#X connect 21 10 27 1;
+#X connect 21 11 26 1;
+#X connect 21 12 25 1;
+#X connect 21 13 24 1;
+#X connect 21 14 23 1;
+#X connect 21 15 22 1;
+#X connect 22 0 12 0;
+#X connect 23 0 22 0;
+#X connect 24 0 23 0;
+#X connect 25 0 24 0;
+#X connect 26 0 25 0;
+#X connect 27 0 26 0;
+#X connect 28 0 27 0;
+#X connect 29 0 28 0;
+#X connect 30 0 33 0;
+#X connect 30 1 18 0;
+#X connect 30 2 33 1;
+#X connect 31 0 8 0;
+#X connect 31 1 17 1;
+#X connect 31 2 17 2;
+#X connect 31 3 33 3;
+#X connect 31 4 33 4;
+#X connect 31 5 33 5;
+#X connect 31 6 33 6;
+#X connect 31 7 33 7;
+#X connect 31 8 33 8;
+#X connect 31 9 33 9;
+#X connect 31 10 33 10;
+#X connect 31 11 33 11;
+#X connect 31 12 33 12;
+#X connect 31 13 33 13;
+#X connect 31 14 33 14;
+#X connect 31 15 33 15;
+#X connect 31 16 33 16;
+#X connect 32 0 30 0;
+#X connect 32 1 30 1;
+#X connect 32 2 33 3;
+#X connect 32 3 33 4;
+#X connect 32 4 33 5;
+#X connect 32 5 33 6;
+#X connect 32 6 33 7;
+#X connect 32 7 33 8;
+#X connect 32 8 33 9;
+#X connect 32 9 33 10;
+#X connect 32 10 33 11;
+#X connect 32 11 33 12;
+#X connect 32 12 33 13;
+#X connect 32 13 33 14;
+#X connect 32 14 33 15;
+#X connect 32 15 33 16;
+#X connect 33 0 21 0;
+#X restore 386 640 pd synth;
+#X obj 24 351 r syn-midinoteon;
+#X obj 16 649 s syn-noteon;
+#N canvas 0 0 690 415 tables 0;
+#X msg 107 49 bang;
+#X obj 107 78 t b b;
+#X obj 159 142 f;
+#X obj 197 142 + 1;
+#X msg 181 115 0;
+#X obj 107 107 until;
+#X obj 161 177 t f f;
+#X obj 109 210 mtof;
+#X obj 90 177 sel 129;
+#X obj 109 237 tabwrite mtof;
+#X text 48 15 patch to regenerate the mtof table;
+#N canvas 0 0 450 300 graph2 0;
+#X array mtof 130 float 1;
+#A 0 8.1758 8.66196 9.17702 9.72272 10.3009 10.9134 11.5623 12.2499
+12.9783 13.75 14.5676 15.4339 16.3516 17.3239 18.354 19.4454 20.6017
+21.8268 23.1247 24.4997 25.9565 27.5 29.1352 30.8677 32.7032 34.6478
+36.7081 38.8909 41.2034 43.6535 46.2493 48.9994 51.9131 55 58.2705
+61.7354 65.4064 69.2957 73.4162 77.7817 82.4069 87.3071 92.4986 97.9989
+103.826 110 116.541 123.471 130.813 138.591 146.832 155.563 164.814
+174.614 184.997 195.998 207.652 220 233.082 246.942 261.626 277.183
+293.665 311.127 329.628 349.228 369.994 391.995 415.305 440 466.164
+493.883 523.251 554.365 587.33 622.254 659.255 698.456 739.989 783.991
+830.609 880 932.328 987.767 1046.5 1108.73 1174.66 1244.51 1318.51
+1396.91 1479.98 1567.98 1661.22 1760 1864.66 1975.53 2093 2217.46 2349.32
+2489.02 2637.02 2793.83 2959.96 3135.96 3322.44 3520 3729.31 3951.07
+4186.01 4434.92 4698.64 4978.03 5274.04 5587.65 5919.91 6271.93 6644.88
+7040 7458.62 7902.13 8372.02 8869.84 9397.27 9956.06 10548.1 11175.3
+11839.8 12543.9 13289.8 14080;
+#X coords 0 12000 130 0 200 100 1;
+#X restore 381 116 graph;
+#X text 391 224 ------ 130 samples ------;
+#X text 590 209 0;
+#X text 592 109 12000;
+#X connect 0 0 1 0;
+#X connect 1 0 5 0;
+#X connect 1 1 4 0;
+#X connect 2 0 3 0;
+#X connect 2 0 6 0;
+#X connect 2 0 8 0;
+#X connect 3 0 2 1;
+#X connect 4 0 2 1;
+#X connect 5 0 2 0;
+#X connect 6 0 7 0;
+#X connect 6 1 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 5 1;
+#X restore 186 132 pd tables;
+#X obj 25 117 metro 500;
+#X floatatom 67 178 5 0 0;
+#X obj 25 203 makenote 64 250;
+#X obj 27 229 pack;
+#X obj 27 253 s syn-midinoteon;
+#X obj 25 98 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
+;
+#X obj 329 358 numset fil x;
+#X obj 329 387 numset fb x;
+#X obj 329 416 numset fa x;
+#X obj 329 445 numset fd x;
+#X obj 329 474 numset fs x;
+#X obj 329 503 numset fr x;
+#X obj 329 532 numset q x;
+#X floatatom 101 96 5 0 0;
+#X floatatom 155 176 5 0 0;
+#X floatatom 115 149 5 0 0;
+#X obj 25 178 + 24;
+#X obj 25 149 random 48;
+#X obj 329 15 preset preset1 x;
+#X obj 329 62 preset preset2 x;
+#X obj 329 108 preset preset3 x;
+#X obj 329 155 preset preset4 x;
+#X obj 330 561 numset 2nd x;
+#X obj 330 590 numset 2pc;
+#X obj 16 626 pack 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
+#X text 451 219 amplitude;
+#X text 440 246 amp attack time;
+#X text 445 279 amp decay time;
+#X text 448 305 amp sustain level (%);
+#X text 448 331 amp release time;
+#X text 454 362 filter sweep;
+#X text 445 392 filter base pitch;
+#X text 444 424 filter attack time;
+#X text 444 450 filter decay time;
+#X text 448 480 filter sustain;
+#X text 448 507 filter release time;
+#X text 453 535 q;
+#X text 450 566 2nd osc detune;
+#X text 432 596 2nd osc amp (%);
+#X text 508 666 OUTPUT LEVEL;
+#X text 41 14 polyphonic synth with;
+#X text 40 37 voice presets;
+#X text 21 74 random-note tester;
+#X connect 0 0 1 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 2;
+#X connect 3 0 39 0;
+#X connect 3 1 39 1;
+#X connect 4 0 5 0;
+#X connect 4 1 5 1;
+#X connect 5 0 3 0;
+#X connect 6 0 39 2;
+#X connect 7 0 39 3;
+#X connect 8 0 39 6;
+#X connect 9 0 39 4;
+#X connect 10 0 39 5;
+#X connect 11 0 1 0;
+#X connect 12 0 3 0;
+#X connect 15 0 32 0;
+#X connect 16 0 31 1;
+#X connect 17 0 18 0;
+#X connect 17 1 18 1;
+#X connect 18 0 19 0;
+#X connect 20 0 15 0;
+#X connect 21 0 39 7;
+#X connect 22 0 39 8;
+#X connect 23 0 39 9;
+#X connect 24 0 39 10;
+#X connect 25 0 39 11;
+#X connect 26 0 39 12;
+#X connect 27 0 39 13;
+#X connect 28 0 15 1;
+#X connect 29 0 17 2;
+#X connect 30 0 32 1;
+#X connect 31 0 17 0;
+#X connect 32 0 31 0;
+#X connect 37 0 39 14;
+#X connect 38 0 39 15;
+#X connect 39 0 13 0;
diff --git a/pd/doc/7.stuff/synth/README.txt b/pd/doc/7.stuff/synth/README.txt
new file mode 100644
index 00000000..b6e6c994
--- /dev/null
+++ b/pd/doc/7.stuff/synth/README.txt
@@ -0,0 +1,7 @@
+This patch (1.poly.synth) is a polyphonic subtractive synthesizer with
+a dozen or so voice parameters you can control live or via presets. Four
+presets are defined. To test, recall a preset (you should see numbers pop up
+in all the parameter controls) start the metronome and turn up the output
+volume to around 100.
+
+
diff --git a/pd/doc/7.stuff/synth/gadsr.pd b/pd/doc/7.stuff/synth/gadsr.pd
new file mode 100644
index 00000000..242ec877
--- /dev/null
+++ b/pd/doc/7.stuff/synth/gadsr.pd
@@ -0,0 +1,146 @@
+#N canvas 71 56 698 578 12;
+#X obj 9 412 inlet;
+#X obj 24 314 inlet;
+#X text 21 437 trigger;
+#X obj 154 314 inlet;
+#X obj 156 477 line~;
+#X obj 289 316 inlet;
+#X obj 426 321 inlet;
+#X obj 566 321 inlet;
+#X text 26 366 level;
+#X obj 156 512 outlet~;
+#X text 148 365 attack;
+#X text 284 366 decay;
+#X text 390 366 sustain;
+#X text 536 374 release;
+#X obj 460 480 snapshot~;
+#X obj 460 452 metro 200;
+#X msg 460 508 set \$1;
+#X floatatom 475 13 5 0 0;
+#X obj 15 286 f \$1;
+#X obj 460 426 loadbang;
+#X obj 13 233 loadbang;
+#X obj 273 58 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 346 63 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X msg 346 85 0;
+#X obj 206 60 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X msg 206 81 1;
+#X msg 273 83 -1;
+#X obj 143 288 f \$2;
+#X floatatom 143 342 4 0 0;
+#N canvas 110 169 840 732 control 0;
+#X obj 368 662 outlet;
+#X obj 37 46 inlet;
+#X obj 170 120 inlet;
+#X obj 256 121 inlet;
+#X text 74 12 trigger;
+#X text 167 82 level;
+#X text 392 640 to line~;
+#X obj 42 332 pack;
+#X obj 22 152 moses;
+#X text 76 168 from;
+#X text 78 180 here;
+#X obj 13 199 t b b;
+#X text 21 170 from;
+#X text 23 182 zero;
+#X msg 206 336 0;
+#X obj 36 302 f;
+#X text 245 84 attack time;
+#X obj 373 120 inlet;
+#X text 362 83 decay time;
+#X obj 494 119 inlet;
+#X text 478 85 sustain level;
+#X obj 617 117 inlet;
+#X text 610 86 release time;
+#X text 471 472 DECAY;
+#X obj 401 423 * 0.01;
+#X obj 415 329 del;
+#X obj 401 397 *;
+#X obj 420 358 f;
+#X obj 464 454 pack;
+#X obj 624 346 pack;
+#X obj 50 81 sel 0;
+#X msg 80 120 stop;
+#X obj 68 197 t b;
+#X connect 1 0 30 0;
+#X connect 2 0 15 1;
+#X connect 2 0 26 1;
+#X connect 3 0 7 1;
+#X connect 3 0 25 0;
+#X connect 7 0 0 0;
+#X connect 8 0 11 0;
+#X connect 8 1 32 0;
+#X connect 11 0 15 0;
+#X connect 11 1 14 0;
+#X connect 14 0 0 0;
+#X connect 15 0 7 0;
+#X connect 17 0 28 1;
+#X connect 19 0 27 1;
+#X connect 21 0 29 1;
+#X connect 24 0 28 0;
+#X connect 25 0 27 0;
+#X connect 26 0 24 0;
+#X connect 27 0 26 0;
+#X connect 28 0 0 0;
+#X connect 29 0 0 0;
+#X connect 30 0 31 0;
+#X connect 30 0 29 0;
+#X connect 30 1 8 0;
+#X connect 31 0 25 0;
+#X connect 32 0 15 0;
+#X restore 135 395 pd control;
+#X obj 280 288 f \$2;
+#X obj 410 286 f \$2;
+#X obj 543 286 f \$2;
+#X floatatom 280 345 4 0 0;
+#X floatatom 410 345 4 0 0;
+#X floatatom 543 346 4 0 0;
+#X obj 489 74 pack 0 20;
+#X msg 495 110 0;
+#X obj 489 47 t f b;
+#X text 104 541 gadsr - arguments: level \, attack time \, decay time
+\, sustain percentage \, release time;
+#X floatatom 15 341 4 0 0;
+#X connect 0 0 29 0;
+#X connect 1 0 40 0;
+#X connect 3 0 28 0;
+#X connect 4 0 9 0;
+#X connect 4 0 14 0;
+#X connect 5 0 33 0;
+#X connect 6 0 34 0;
+#X connect 7 0 35 0;
+#X connect 14 0 16 0;
+#X connect 15 0 14 0;
+#X connect 16 0 17 0;
+#X connect 17 0 38 0;
+#X connect 18 0 40 0;
+#X connect 19 0 15 0;
+#X connect 20 0 18 0;
+#X connect 20 0 27 0;
+#X connect 20 0 30 0;
+#X connect 20 0 31 0;
+#X connect 20 0 32 0;
+#X connect 21 0 26 0;
+#X connect 22 0 23 0;
+#X connect 23 0 29 0;
+#X connect 24 0 25 0;
+#X connect 25 0 29 0;
+#X connect 26 0 29 0;
+#X connect 27 0 28 0;
+#X connect 28 0 29 2;
+#X connect 29 0 4 0;
+#X connect 30 0 33 0;
+#X connect 31 0 34 0;
+#X connect 32 0 35 0;
+#X connect 33 0 29 3;
+#X connect 34 0 29 4;
+#X connect 35 0 29 5;
+#X connect 36 0 4 0;
+#X connect 37 0 29 0;
+#X connect 38 0 36 0;
+#X connect 38 1 37 0;
+#X connect 40 0 29 1;
+#X coords 0 0 1 1 200 40 1;
diff --git a/pd/doc/7.stuff/synth/numset.pd b/pd/doc/7.stuff/synth/numset.pd
new file mode 100644
index 00000000..fcbeb159
--- /dev/null
+++ b/pd/doc/7.stuff/synth/numset.pd
@@ -0,0 +1,27 @@
+#N canvas 672 25 448 396 10;
+#X obj 11 240 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 0 0 10
+-262144 -1 -1 0 256;
+#X obj 11 262 outlet;
+#X obj 78 267 s \$1-out;
+#X obj 189 49 r \$1-in;
+#X obj 191 130 r \$2-in;
+#X obj 191 177 route \$1 -record-;
+#X obj 191 205 f;
+#X obj 245 289 pack s 0;
+#X obj 248 203 b;
+#X obj 245 340 s \$2-out;
+#X msg 245 315 add list \$1 \$2;
+#X obj 248 228 symbol \$1-in;
+#X connect 0 0 1 0;
+#X connect 0 0 2 0;
+#X connect 0 0 7 1;
+#X connect 3 0 0 0;
+#X connect 4 0 5 0;
+#X connect 5 0 6 0;
+#X connect 5 1 8 0;
+#X connect 6 0 0 0;
+#X connect 7 0 10 0;
+#X connect 8 0 11 0;
+#X connect 10 0 9 0;
+#X connect 11 0 7 0;
+#X coords 0 0 1 1 80 24 1;
diff --git a/pd/doc/7.stuff/synth/preset.pd b/pd/doc/7.stuff/synth/preset.pd
new file mode 100644
index 00000000..8148f3b0
--- /dev/null
+++ b/pd/doc/7.stuff/synth/preset.pd
@@ -0,0 +1,54 @@
+#N canvas 69 59 443 360 12;
+#X obj 210 286 textfile;
+#X obj 8 174 bng 15 250 50 0 empty empty store 16 7 0 10 -262144 -1
+-1;
+#X obj 8 320 bng 15 250 50 0 empty empty recall 15 7 0 10 -262144 -1
+-1;
+#X obj 408 330 tgl 10 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 104 59 t b b b;
+#X msg 219 121 clear;
+#X msg 114 149 -record-;
+#X obj 61 207 symbol \$1.txt;
+#X obj 363 115 1;
+#X obj 310 191 symbol \$1.txt;
+#X obj 312 165 loadbang;
+#X msg 310 216 read \$1 cr;
+#X msg 62 234 write \$1 cr;
+#X obj 75 299 until;
+#X msg 210 322 \; \$1 \$2;
+#X obj 112 175 s \$2-in;
+#X obj 224 187 r \$2-out;
+#X obj 77 265 t b b;
+#X msg 140 268 rewind;
+#X obj 308 295 print;
+#X obj 224 220 spigot;
+#X obj 311 126 1;
+#X obj 278 124 0;
+#X connect 0 0 14 0;
+#X connect 0 1 13 1;
+#X connect 1 0 4 0;
+#X connect 2 0 17 0;
+#X connect 4 0 7 0;
+#X connect 4 0 8 0;
+#X connect 4 0 22 0;
+#X connect 4 1 6 0;
+#X connect 4 2 5 0;
+#X connect 4 2 21 0;
+#X connect 5 0 0 0;
+#X connect 6 0 15 0;
+#X connect 7 0 12 0;
+#X connect 8 0 3 0;
+#X connect 9 0 11 0;
+#X connect 10 0 9 0;
+#X connect 11 0 0 0;
+#X connect 12 0 0 0;
+#X connect 13 0 0 0;
+#X connect 16 0 20 0;
+#X connect 17 0 13 0;
+#X connect 17 1 18 0;
+#X connect 18 0 0 0;
+#X connect 20 0 0 0;
+#X connect 21 0 20 1;
+#X connect 22 0 20 1;
+#X coords 0 0 1 1 90 35 1;
diff --git a/pd/doc/7.stuff/synth/preset1.txt b/pd/doc/7.stuff/synth/preset1.txt
new file mode 100644
index 00000000..38e342fc
--- /dev/null
+++ b/pd/doc/7.stuff/synth/preset1.txt
@@ -0,0 +1,13 @@
+list 2nd-in 1200
+list q-in 0.48
+list fr-in 200
+list fs-in 37
+list fd-in 509
+list fa-in 2
+list fb-in 15
+list fil-in 51
+list as-in 81
+list ad-in 11
+list ar-in 207
+list aa-in 6
+list amp-in 94
diff --git a/pd/doc/7.stuff/synth/preset2.txt b/pd/doc/7.stuff/synth/preset2.txt
new file mode 100644
index 00000000..ce0352fb
--- /dev/null
+++ b/pd/doc/7.stuff/synth/preset2.txt
@@ -0,0 +1,13 @@
+list 2nd-in 1200
+list q-in 0.87
+list fr-in 200
+list fs-in 37
+list fd-in 509
+list fa-in 2
+list fb-in 39
+list fil-in 51
+list as-in 63
+list ad-in 1631
+list ar-in 207
+list aa-in 6
+list amp-in 90
diff --git a/pd/doc/7.stuff/synth/preset3.txt b/pd/doc/7.stuff/synth/preset3.txt
new file mode 100644
index 00000000..a53f57df
--- /dev/null
+++ b/pd/doc/7.stuff/synth/preset3.txt
@@ -0,0 +1,13 @@
+list 2nd-in 1200
+list q-in 10
+list fr-in 200
+list fs-in 46
+list fd-in 360
+list fa-in 2
+list fb-in 30
+list fil-in 73
+list as-in 63
+list ad-in 1631
+list ar-in 207
+list aa-in 6
+list amp-in 95
diff --git a/pd/doc/7.stuff/synth/preset4.txt b/pd/doc/7.stuff/synth/preset4.txt
new file mode 100644
index 00000000..f9ce0b38
--- /dev/null
+++ b/pd/doc/7.stuff/synth/preset4.txt
@@ -0,0 +1,13 @@
+list 2nd-in 0
+list q-in 3.01
+list fr-in 200
+list fs-in 18
+list fd-in 323
+list fa-in 2
+list fb-in 6
+list fil-in 50
+list as-in 88
+list ad-in 72
+list ar-in 207
+list aa-in 6
+list amp-in 100
diff --git a/pd/doc/7.stuff/synth/synthvoice.pd b/pd/doc/7.stuff/synth/synthvoice.pd
new file mode 100644
index 00000000..e43fe42d
--- /dev/null
+++ b/pd/doc/7.stuff/synth/synthvoice.pd
@@ -0,0 +1,73 @@
+#N canvas 218 43 728 639 12;
+#X obj 61 616 outlet~;
+#X obj 363 475 *~;
+#X obj 166 328 *~;
+#X obj 166 351 *~;
+#X obj 40 325 mtof;
+#X obj 180 52 inlet;
+#X obj 61 565 inlet~;
+#X obj 61 592 +~;
+#X obj 473 266 gadsr;
+#X obj 166 265 gadsr;
+#X obj 106 115 dbtorms;
+#X obj 106 141 sqrt;
+#X obj 106 166 sqrt;
+#X obj 461 322 +~;
+#X obj 461 348 tabread4~ mtof;
+#X obj 405 464 vcf~;
+#X obj 42 416 phasor~;
+#X obj 106 349 + 0.3;
+#X obj 106 377 phasor~;
+#X obj 42 455 +~;
+#X obj 180 75 unpack 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
+#X text 173 19 on/off pitch amp a d s r filt filt-bias a d s r filt-q
+2nd-interval 2nd-percentage;
+#X obj 106 281 +;
+#X obj 106 319 mtof;
+#X obj 120 414 *~ 1;
+#X obj 345 391 * 0.01;
+#X obj 133 241 * 0.01;
+#X connect 1 0 7 1;
+#X connect 2 0 3 0;
+#X connect 2 0 3 1;
+#X connect 3 0 1 0;
+#X connect 4 0 16 0;
+#X connect 5 0 20 0;
+#X connect 6 0 7 0;
+#X connect 7 0 0 0;
+#X connect 8 0 13 1;
+#X connect 9 0 2 0;
+#X connect 9 0 2 1;
+#X connect 10 0 11 0;
+#X connect 11 0 12 0;
+#X connect 12 0 9 1;
+#X connect 13 0 14 0;
+#X connect 14 0 15 1;
+#X connect 15 0 1 1;
+#X connect 16 0 19 0;
+#X connect 17 0 18 0;
+#X connect 18 0 24 0;
+#X connect 19 0 15 0;
+#X connect 20 0 9 0;
+#X connect 20 0 8 0;
+#X connect 20 1 4 0;
+#X connect 20 1 22 0;
+#X connect 20 2 10 0;
+#X connect 20 3 9 2;
+#X connect 20 4 9 3;
+#X connect 20 5 9 4;
+#X connect 20 6 9 5;
+#X connect 20 7 8 1;
+#X connect 20 8 13 0;
+#X connect 20 9 8 2;
+#X connect 20 10 8 3;
+#X connect 20 11 8 4;
+#X connect 20 12 8 5;
+#X connect 20 13 15 2;
+#X connect 20 14 26 0;
+#X connect 20 15 25 0;
+#X connect 22 0 23 0;
+#X connect 23 0 17 0;
+#X connect 24 0 19 1;
+#X connect 25 0 24 1;
+#X connect 26 0 22 1;
diff --git a/pd/doc/7.stuff/synth/test-gadsr.pd b/pd/doc/7.stuff/synth/test-gadsr.pd
new file mode 100644
index 00000000..ea26fe46
--- /dev/null
+++ b/pd/doc/7.stuff/synth/test-gadsr.pd
@@ -0,0 +1,2 @@
+#N canvas 45 88 785 525 10;
+#X obj 208 171 gadsr;
diff --git a/pd/doc/7.stuff/tools/latency.pd b/pd/doc/7.stuff/tools/latency.pd
new file mode 100644
index 00000000..ce6db34d
--- /dev/null
+++ b/pd/doc/7.stuff/tools/latency.pd
@@ -0,0 +1,97 @@
+#N canvas 37 -7 825 630 12;
+#X obj 98 184 metro 500;
+#X obj 60 296 sig~;
+#X msg 60 267 0.5;
+#X obj 96 245 del 3;
+#X msg 96 270 0;
+#X obj 33 325 dac~;
+#X obj 286 216 adc~;
+#X obj 290 312 timer;
+#X obj 92 335 env~ 65536;
+#X floatatom 92 362 4 0 0;
+#X floatatom 339 170 4 0 0;
+#X obj 339 193 + 100;
+#X obj 339 218 dbtorms;
+#X obj 180 335 env~ 65536;
+#X floatatom 180 364 4 0 0;
+#X floatatom 290 349 4 0 0;
+#X obj 312 245 *~ 1;
+#X obj 312 282 threshold~ 0.1 5 0.05 5;
+#X obj 634 160 bonk~;
+#X obj 634 188 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 508 188 osc~ 440;
+#X obj 530 214 *~ 0;
+#X obj 582 189 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 571 211 * 0.1;
+#X obj 80 25 vdl 15 1 0 3 empty empty empty 0 -6 0 8 -262144 -1 -1
+0;
+#X msg 17 138 \; pd dsp 1;
+#X obj 634 132 *~;
+#X obj 16 113 sel 0;
+#X text 100 20 off;
+#X obj 96 156 == 1;
+#X obj 80 78 t f;
+#X text 102 36 measure latency;
+#X text 100 53 test continuity;
+#X obj 582 97 == 2;
+#X text 89 381 level out;
+#X text 181 382 level in;
+#X obj 530 244 dac~;
+#X text 291 370 latency in;
+#X text 292 386 msec;
+#X text 656 188 this flashes when;
+#X text 656 207 a discontinuity is;
+#X text 657 226 detected;
+#X obj 642 105 adc~;
+#X text 328 131 you can;
+#X text 324 147 adjust gain here;
+#X text 538 66 --- continuity check ---;
+#X text 169 105 --- latency measurement ---;
+#X text 63 410 To use this patch \, connect your audio output back
+to the audio input (channel 1 should suffice.) The latency measurement
+assumes the feedback gain is at least about -14 dB - you can increase
+the input sensitivity if need be.;
+#X text 66 481 If you select "measure latency" a series of pulses are
+timed using the "threshold~" object - note that it has an uncertainty
+of 1.45 msec (at 44K1) \, so you might see the number jitter even if
+the latency is constant.;
+#X text 66 554 Select "test continuity' to see if there are interruptions
+in the sound \, either at the input or output stage. If there are \,
+the button will flash.;
+#X connect 0 0 2 0;
+#X connect 0 0 3 0;
+#X connect 0 0 7 0;
+#X connect 1 0 8 0;
+#X connect 1 0 5 0;
+#X connect 1 0 5 1;
+#X connect 2 0 1 0;
+#X connect 3 0 4 0;
+#X connect 4 0 1 0;
+#X connect 6 0 16 0;
+#X connect 7 0 15 0;
+#X connect 8 0 9 0;
+#X connect 10 0 11 0;
+#X connect 11 0 12 0;
+#X connect 12 0 16 1;
+#X connect 13 0 14 0;
+#X connect 16 0 13 0;
+#X connect 16 0 17 0;
+#X connect 17 0 7 1;
+#X connect 18 0 19 0;
+#X connect 20 0 21 0;
+#X connect 21 0 36 0;
+#X connect 21 0 36 1;
+#X connect 22 0 23 0;
+#X connect 23 0 21 1;
+#X connect 24 0 30 0;
+#X connect 26 0 18 0;
+#X connect 27 1 25 0;
+#X connect 29 0 0 0;
+#X connect 30 0 27 0;
+#X connect 30 0 29 0;
+#X connect 30 0 33 0;
+#X connect 33 0 22 0;
+#X connect 33 0 26 1;
+#X connect 42 0 26 0;
diff --git a/pd/doc/7.stuff/tools/load-meter.pd b/pd/doc/7.stuff/tools/load-meter.pd
new file mode 100644
index 00000000..6a73b5a4
--- /dev/null
+++ b/pd/doc/7.stuff/tools/load-meter.pd
@@ -0,0 +1,21 @@
+#N canvas 161 261 299 317 12;
+#X floatatom 118 256;
+#X obj 118 168 cputime;
+#X obj 118 28 loadbang;
+#X obj 118 112 metro 1000;
+#X msg 118 56 1;
+#X floatatom 118 84;
+#X obj 118 140 t b b;
+#X obj 118 228 * 0.1;
+#X obj 118 197 int;
+#X text 163 84 <-- on/off;
+#X text 51 284 CPU load in percent;
+#X connect 1 0 8 0;
+#X connect 2 0 4 0;
+#X connect 3 0 6 0;
+#X connect 4 0 5 0;
+#X connect 5 0 3 0;
+#X connect 6 0 1 0;
+#X connect 6 1 1 1;
+#X connect 7 0 0 0;
+#X connect 8 0 7 0;
diff --git a/pd/doc/7.stuff/tools/testtone.pd b/pd/doc/7.stuff/tools/testtone.pd
new file mode 100644
index 00000000..965859c7
--- /dev/null
+++ b/pd/doc/7.stuff/tools/testtone.pd
@@ -0,0 +1,366 @@
+#N canvas 36 16 581 402 12;
+#X floatatom 83 307 3 0 0;
+#X obj 33 257 notein;
+#X obj 33 283 stripnote;
+#X floatatom 32 308 3 0 0;
+#X text 35 5 Welcome to Pd ("Pure Data"). This window can test your
+audio and MIDI connections. To see Pd's DOCUMENTATION select "getting
+started" in the Help menu.;
+#X text 236 258 MIDI OUT;
+#X text 33 233 MIDI IN;
+#X floatatom 175 305 3 0 0;
+#X floatatom 136 304 3 0 0;
+#X obj 136 279 ctlin;
+#N canvas 0 0 484 446 midi 0;
+#X obj 95 61 inlet;
+#X obj 96 262 noteout;
+#X floatatom 96 92 0 0 0;
+#X obj 107 120 outlet;
+#X obj 338 113 loadbang;
+#X obj 96 184 metro;
+#X obj 96 236 makenote;
+#X floatatom 189 166 0 0 0;
+#X obj 96 210 f;
+#X floatatom 145 166 0 0 0;
+#X floatatom 233 166 0 0 0;
+#X floatatom 276 166 0 0 0;
+#X msg 338 148 500;
+#X msg 370 148 60;
+#X msg 399 148 64;
+#X msg 427 148 250;
+#X text 144 145 rate;
+#X text 187 145 pitch;
+#X text 232 145 vel;
+#X text 268 146 length;
+#X obj 230 257 ctlout 1;
+#X floatatom 231 228 0 0 0;
+#X connect 0 0 2 0;
+#X connect 2 0 3 0;
+#X connect 2 0 5 0;
+#X connect 4 0 12 0;
+#X connect 4 0 13 0;
+#X connect 4 0 14 0;
+#X connect 4 0 15 0;
+#X connect 5 0 8 0;
+#X connect 6 0 1 0;
+#X connect 6 1 1 1;
+#X connect 7 0 8 1;
+#X connect 8 0 6 0;
+#X connect 9 0 5 1;
+#X connect 10 0 6 1;
+#X connect 11 0 6 2;
+#X connect 12 0 9 0;
+#X connect 13 0 7 0;
+#X connect 14 0 10 0;
+#X connect 15 0 11 0;
+#X connect 21 0 20 0;
+#X restore 236 308 pd midi;
+#X floatatom 149 183 3 0 0;
+#X floatatom 182 183 3 0 0;
+#X text 24 341 PD is COPYRIGHT 1997-2002 by Miller Puckette and others
+but is free for you to use for any reasonable purpose. See the file
+\, LICENSE.txt in the distribution.;
+#X obj 145 115 tgl 20 0 tone-ch1 tone-ch1 1 5 -8 192 12 -262144 -1
+-1 1 1;
+#X obj 170 115 tgl 20 0 tone-ch2 tone-ch2 2 5 -8 192 12 -262144 -1
+-1 1 1;
+#X obj 236 282 tgl 20 0 empty empty empty 20 8 0 8 -262144 -1 -1 0
+1;
+#X obj 394 110 tgl 20 0 tone-monitor set-tone-monitor monitor 25 10
+192 12 -262144 -1 -1 0 1;
+#X obj 36 132 vdl 20 1 0 3 tone-radio set-tone-radio empty 20 8 192
+8 -262144 -1 -1 2;
+#X text 62 177 OFF;
+#X text 62 154 -40;
+#X text 62 131 -20;
+#N canvas 0 0 536 251 more 0;
+#X floatatom 42 209 0 0 0;
+#X obj 42 183 f;
+#X obj 79 183 + 1;
+#X obj 42 150 metro 1000;
+#X obj 42 123 tgl 20 0 empty empty empty 20 8 0 8 -262144 -1 -1 0 1
+;
+#X msg 264 142 \; pd restart-audio;
+#X text 24 30 this window has various wierd debugging stuff...;
+#X text 218 97 ALSA gets twisted after a few;
+#X text 216 118 hours sometimes... use this to fix:;
+#X text 22 74 see if Pd's time;
+#X text 22 93 measurement works:;
+#X connect 1 0 2 0;
+#X connect 1 0 0 0;
+#X connect 2 0 1 1;
+#X connect 3 0 1 0;
+#X connect 4 0 3 0;
+#X restore 459 298 pd more;
+#X obj 393 148 tgl 20 0 tone-hipass set-tone-hipass input-hipass 25
+10 192 12 -262144 -1 -1 0 1;
+#X obj 195 115 tgl 20 0 tone-ch3 tone-ch3 3 5 -8 192 12 -262144 -1
+-1 1 1;
+#X obj 220 115 tgl 20 0 tone-ch4 tone-ch4 4 5 -8 192 12 -262144 -1
+-1 1 1;
+#X obj 245 115 tgl 20 0 tone-ch5 tone-ch5 5 5 -8 192 12 -262144 -1
+-1 1 1;
+#X obj 270 115 tgl 20 0 tone-ch6 tone-ch6 6 5 -8 192 12 -262144 -1
+-1 1 1;
+#X obj 304 106 bng 15 250 50 0 tone-all empty ALL 20 8 128 12 -262144
+-1 -1;
+#X obj 304 127 bng 15 250 50 0 tone-none empty NONE 20 8 128 12 -262144
+-1 -1;
+#X floatatom 215 183 3 0 0;
+#X floatatom 248 183 3 0 0;
+#X floatatom 281 184 3 0 0;
+#X floatatom 315 184 3 0 0;
+#X text 164 208 AUDIO INPUT (RMS dB);
+#X text 41 83 TEST;
+#X text 40 103 TONE;
+#X text 152 76 test tone channels:;
+#N canvas 29 216 908 548 -------audio---- 0;
+#X obj 182 92 hip~ 5;
+#X obj 23 351 *~;
+#X obj 45 324 line~;
+#X obj 190 169 outlet;
+#X obj 45 297 pack 0 50;
+#X obj 22 134 osc~;
+#X floatatom 45 245 0 0 0;
+#X obj 45 270 dbtorms;
+#X obj 190 143 int;
+#X obj 22 66 mtof;
+#X floatatom 22 43 0 0 0;
+#X floatatom 22 88 0 0 0;
+#X obj 22 110 sig~;
+#X obj 190 117 env~ 8192;
+#X obj 182 39 adc~ 1;
+#X obj 23 377 s~ tone-osc;
+#X obj 45 162 r tone-radio;
+#X obj 22 18 r tone-pitch;
+#X obj 45 190 sel 0 1 2;
+#X msg 114 213 0;
+#X msg 45 215 80;
+#X msg 78 215 60;
+#X obj 192 192 r~ tone-mon;
+#X obj 201 327 line~;
+#X obj 182 217 *~;
+#X obj 201 300 pack 0 50;
+#X obj 201 274 r tone-ch1;
+#X obj 192 247 r~ tone-osc;
+#X obj 192 352 *~;
+#X obj 182 407 dac~ 1;
+#X obj 182 382 +~;
+#X obj 190 65 r tone-hip;
+#X obj 16 487 s~ tone-mon;
+#X obj 16 461 line~;
+#X obj 17 433 pack 0 50;
+#X obj 17 407 r tone-monitor;
+#X obj 302 91 hip~ 5;
+#X obj 310 168 outlet;
+#X obj 310 142 int;
+#X obj 310 116 env~ 8192;
+#X obj 312 191 r~ tone-mon;
+#X obj 321 326 line~;
+#X obj 302 216 *~;
+#X obj 321 299 pack 0 50;
+#X obj 312 246 r~ tone-osc;
+#X obj 312 351 *~;
+#X obj 302 381 +~;
+#X obj 310 64 r tone-hip;
+#X obj 302 38 adc~ 2;
+#X obj 302 406 dac~ 2;
+#X obj 321 273 r tone-ch2;
+#X obj 177 485 * 5;
+#X obj 177 510 s tone-hip;
+#X obj 179 459 r tone-hipass;
+#N canvas 499 63 548 519 glue+loadbang 0;
+#X obj 171 21 loadbang;
+#X msg 171 49 \; pd dsp 1 \; tone-pitch 69 \; tone-radio 2 \; set-tone-radio
+2 \; tone-all 1 \;;
+#X obj 171 176 r tone-all;
+#X msg 171 204 \; tone-ch1 1 \; tone-ch2 1 \; tone-ch3 1 \; tone-ch4
+1 \; tone-ch5 1 \; tone-ch6 1;
+#X obj 168 323 r tone-none;
+#X msg 168 352 \; tone-ch1 0 \; tone-ch2 0 \; tone-ch3 0 \; tone-ch4
+0 \; tone-ch5 0 \; tone-ch6 0;
+#X connect 0 0 1 0;
+#X connect 2 0 3 0;
+#X connect 4 0 5 0;
+#X restore 14 516 pd glue+loadbang;
+#X obj 424 93 hip~ 5;
+#X obj 432 170 outlet;
+#X obj 432 144 int;
+#X obj 432 118 env~ 8192;
+#X obj 434 193 r~ tone-mon;
+#X obj 443 328 line~;
+#X obj 424 218 *~;
+#X obj 443 301 pack 0 50;
+#X obj 434 248 r~ tone-osc;
+#X obj 434 353 *~;
+#X obj 424 383 +~;
+#X obj 432 66 r tone-hip;
+#X obj 544 92 hip~ 5;
+#X obj 552 169 outlet;
+#X obj 552 143 int;
+#X obj 552 117 env~ 8192;
+#X obj 554 192 r~ tone-mon;
+#X obj 563 327 line~;
+#X obj 544 217 *~;
+#X obj 563 300 pack 0 50;
+#X obj 554 247 r~ tone-osc;
+#X obj 554 352 *~;
+#X obj 544 382 +~;
+#X obj 552 65 r tone-hip;
+#X obj 661 92 hip~ 5;
+#X obj 669 169 outlet;
+#X obj 669 143 int;
+#X obj 669 117 env~ 8192;
+#X obj 671 192 r~ tone-mon;
+#X obj 680 327 line~;
+#X obj 661 217 *~;
+#X obj 680 300 pack 0 50;
+#X obj 671 247 r~ tone-osc;
+#X obj 671 352 *~;
+#X obj 661 382 +~;
+#X obj 669 65 r tone-hip;
+#X obj 781 91 hip~ 5;
+#X obj 789 168 outlet;
+#X obj 789 142 int;
+#X obj 789 116 env~ 8192;
+#X obj 791 191 r~ tone-mon;
+#X obj 800 326 line~;
+#X obj 781 216 *~;
+#X obj 800 299 pack 0 50;
+#X obj 791 246 r~ tone-osc;
+#X obj 791 351 *~;
+#X obj 781 381 +~;
+#X obj 789 64 r tone-hip;
+#X obj 424 40 adc~ 3;
+#X obj 544 39 adc~ 4;
+#X obj 661 39 adc~ 5;
+#X obj 781 38 adc~ 6;
+#X obj 443 275 r tone-ch3;
+#X obj 563 273 r tone-ch4;
+#X obj 680 274 r tone-ch5;
+#X obj 800 273 r tone-ch6;
+#X obj 424 408 dac~ 3;
+#X obj 543 406 dac~ 4;
+#X obj 661 407 dac~ 5;
+#X obj 781 406 dac~ 6;
+#X connect 0 0 24 0;
+#X connect 0 0 13 0;
+#X connect 1 0 15 0;
+#X connect 2 0 1 1;
+#X connect 4 0 2 0;
+#X connect 5 0 1 0;
+#X connect 6 0 7 0;
+#X connect 7 0 4 0;
+#X connect 8 0 3 0;
+#X connect 9 0 11 0;
+#X connect 10 0 9 0;
+#X connect 11 0 12 0;
+#X connect 12 0 5 0;
+#X connect 13 0 8 0;
+#X connect 14 0 0 0;
+#X connect 16 0 18 0;
+#X connect 17 0 10 0;
+#X connect 18 0 20 0;
+#X connect 18 1 21 0;
+#X connect 18 2 19 0;
+#X connect 19 0 6 0;
+#X connect 20 0 6 0;
+#X connect 21 0 6 0;
+#X connect 22 0 24 1;
+#X connect 23 0 28 1;
+#X connect 24 0 30 0;
+#X connect 25 0 23 0;
+#X connect 26 0 25 0;
+#X connect 27 0 28 0;
+#X connect 28 0 30 1;
+#X connect 30 0 29 0;
+#X connect 31 0 0 1;
+#X connect 33 0 32 0;
+#X connect 34 0 33 0;
+#X connect 35 0 34 0;
+#X connect 36 0 42 0;
+#X connect 36 0 39 0;
+#X connect 38 0 37 0;
+#X connect 39 0 38 0;
+#X connect 40 0 42 1;
+#X connect 41 0 45 1;
+#X connect 42 0 46 0;
+#X connect 43 0 41 0;
+#X connect 44 0 45 0;
+#X connect 45 0 46 1;
+#X connect 46 0 49 0;
+#X connect 47 0 36 1;
+#X connect 48 0 36 0;
+#X connect 50 0 43 0;
+#X connect 51 0 52 0;
+#X connect 53 0 51 0;
+#X connect 55 0 61 0;
+#X connect 55 0 58 0;
+#X connect 57 0 56 0;
+#X connect 58 0 57 0;
+#X connect 59 0 61 1;
+#X connect 60 0 64 1;
+#X connect 61 0 65 0;
+#X connect 62 0 60 0;
+#X connect 63 0 64 0;
+#X connect 64 0 65 1;
+#X connect 65 0 111 0;
+#X connect 66 0 55 1;
+#X connect 67 0 73 0;
+#X connect 67 0 70 0;
+#X connect 69 0 68 0;
+#X connect 70 0 69 0;
+#X connect 71 0 73 1;
+#X connect 72 0 76 1;
+#X connect 73 0 77 0;
+#X connect 74 0 72 0;
+#X connect 75 0 76 0;
+#X connect 76 0 77 1;
+#X connect 77 0 112 0;
+#X connect 78 0 67 1;
+#X connect 79 0 85 0;
+#X connect 79 0 82 0;
+#X connect 81 0 80 0;
+#X connect 82 0 81 0;
+#X connect 83 0 85 1;
+#X connect 84 0 88 1;
+#X connect 85 0 89 0;
+#X connect 86 0 84 0;
+#X connect 87 0 88 0;
+#X connect 88 0 89 1;
+#X connect 89 0 113 0;
+#X connect 90 0 79 1;
+#X connect 91 0 97 0;
+#X connect 91 0 94 0;
+#X connect 93 0 92 0;
+#X connect 94 0 93 0;
+#X connect 95 0 97 1;
+#X connect 96 0 100 1;
+#X connect 97 0 101 0;
+#X connect 98 0 96 0;
+#X connect 99 0 100 0;
+#X connect 100 0 101 1;
+#X connect 101 0 114 0;
+#X connect 102 0 91 1;
+#X connect 103 0 55 0;
+#X connect 104 0 67 0;
+#X connect 105 0 79 0;
+#X connect 106 0 91 0;
+#X connect 107 0 62 0;
+#X connect 108 0 74 0;
+#X connect 109 0 86 0;
+#X connect 110 0 98 0;
+#X restore 149 157 pd -------audio----;
+#X connect 1 0 2 0;
+#X connect 1 1 2 1;
+#X connect 2 0 3 0;
+#X connect 2 1 0 0;
+#X connect 9 0 8 0;
+#X connect 9 1 7 0;
+#X connect 16 0 10 0;
+#X connect 38 0 11 0;
+#X connect 38 1 12 0;
+#X connect 38 2 30 0;
+#X connect 38 3 31 0;
+#X connect 38 4 32 0;
+#X connect 38 5 33 0;
diff --git a/pd/doc/sound/bell.aiff b/pd/doc/sound/bell.aiff
new file mode 100644
index 00000000..4b2a49ae
--- /dev/null
+++ b/pd/doc/sound/bell.aiff
Binary files differ
diff --git a/pd/doc/sound/voice.wav b/pd/doc/sound/voice.wav
new file mode 100644
index 00000000..efb6f45e
--- /dev/null
+++ b/pd/doc/sound/voice.wav
Binary files differ
diff --git a/pd/doc/sound/voice2.wav b/pd/doc/sound/voice2.wav
new file mode 100644
index 00000000..9bd9d484
--- /dev/null
+++ b/pd/doc/sound/voice2.wav
Binary files differ
diff --git a/pd/extra/README.txt b/pd/extra/README.txt
new file mode 100644
index 00000000..2ff84f68
--- /dev/null
+++ b/pd/extra/README.txt
@@ -0,0 +1,37 @@
+This is the README file for the "extras" library, consisting of Pd
+objects which are too specialized or otherwise non-canonical for
+inclusion into Pd proper. Except as noted otherwise, all
+included materiels are Copyright 1999 Miller Puckette and others.
+Permission is granted to use this software for any purpose, commercial
+or noncommercial, as long as this notice is included with all copies.
+
+NEITHER THE AUTHORS NOR THEIR EMPLOYERS MAKE ANY WARRANTY, EXPRESS
+OR IMPLIED, IN CONNECTION WITH THIS SOFTWARE!
+
+Note that "expr" is under the GPL, which is more restrictive than Pd's own
+license agreement.
+
+This package should run under linux, NT, or IRIX, except for
+"bilge" which is for NT only. You can additionally compile fiddle~. bonk~,
+and paf~ for Max/MSP.
+
+contents:
+
+externs:
+fiddle~ -- pitch tracker
+bonk~ - percussion detector
+choose - find the "best fit" of incoming vector with stored profiles
+paf~ -- phase aligned formant generator
+loop~ -- sample looper
+expr -- arithmetic expression evaluation (Shahrokh Yadegari)
+bilge (NT only) - play audio CDs through the regular PC sound system
+
+abstractions:
+hilbert~ - Hilbert transform for SSB modulation
+complex-mod~ - ring modulation for complex (real+imaginary) audio signals
+rev1~ - reverberator
+
+These objects are part of the regular Pd distribution as of Pd version
+0.30. Macintosh versions of fiddle~, bonk~, and paf~ are available
+from http://www.crca.ucsd.edu/~tapel
+- msp@ucsd.edu
diff --git a/pd/extra/choice/README.txt b/pd/extra/choice/README.txt
new file mode 100644
index 00000000..3c3ed132
--- /dev/null
+++ b/pd/extra/choice/README.txt
@@ -0,0 +1,12 @@
+Choice is copyright (C) 1999 Miller Puckette.
+Permission is granted to use this software for any purpose, commercial
+or noncommercial, as long as this notice is included with all copies.
+
+NEITHER THE AUTHORS NOR THEIR EMPLOYERS MAKE ANY WARRANTY, EXPRESS OR IMPLIED,
+IN CONNECTION WITH THIS SOFTWARE!
+
+----------------------------------------------------------------------------
+
+This is the README file for the "choice" object. This software
+is available from http://www.crca.ucsd.edu/~msp as part of the "toys"
+library. - msp@ucsd.edu
diff --git a/pd/extra/choice/choice.c b/pd/extra/choice/choice.c
new file mode 100644
index 00000000..002dac40
--- /dev/null
+++ b/pd/extra/choice/choice.c
@@ -0,0 +1,128 @@
+/* choice -- match incoming list against a collection of stored templates. */
+
+/* Copyright 1999 Miller Puckette.
+Permission is granted to use this software for any purpose provided you
+keep this copyright notice intact.
+
+THE AUTHOR AND HIS EMPLOYERS MAKE NO WARRANTY, EXPRESS OR IMPLIED,
+IN CONNECTION WITH THIS SOFTWARE.
+
+This file is downloadable from http://www.crca.ucsd.edu/~msp .
+*/
+
+#include "m_pd.h"
+#include <math.h>
+static t_class *choice_class;
+#define DIMENSION 10
+
+typedef struct _elem
+{
+ float e_age;
+ float e_weight[DIMENSION];
+} t_elem;
+
+typedef struct _choice
+{
+ t_object x_obj;
+ t_elem *x_vec;
+ int x_n;
+ int x_nonrepeat;
+} t_choice;
+
+static void *choice_new(t_float fnonrepeat)
+{
+ t_choice *x = (t_choice *)pd_new(choice_class);
+ outlet_new(&x->x_obj, gensym("float"));
+ x->x_vec = (t_elem *)getbytes(0);
+ x->x_n = 0;
+ x->x_nonrepeat = (fnonrepeat != 0);
+ return (x);
+}
+
+static void choice_clear(t_choice *x)
+{
+ x->x_vec = (t_elem *)resizebytes(x->x_vec, x->x_n * sizeof(t_elem), 0);
+ x->x_n = 0;
+}
+
+static void choice_print(t_choice *x)
+{
+ int j;
+ for (j = 0; j < x->x_n; j++)
+ {
+ t_elem *e = x->x_vec + j;
+ t_float *w = e->e_weight;
+ post("%2d age %2d \
+w %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f",
+ j, (int)(e->e_age), w[0], w[1], w[2], w[3], w[4], w[5],
+ w[6], w[7], w[8], w[9]);
+ }
+}
+
+static void choice_add(t_choice *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int oldn = x->x_n, newn = oldn + 1, i;
+ t_elem *e;
+ float sum, normal;
+ x->x_vec = (t_elem *)resizebytes(x->x_vec, oldn * sizeof(t_elem),
+ newn * sizeof(t_elem));
+ x->x_n = newn;
+ e = x->x_vec + oldn;
+ e->e_age = 2;
+
+ for (i = 0, sum = 0; i < DIMENSION; i++)
+ {
+ float f = atom_getfloatarg(i, argc, argv);
+ e->e_weight[i] = f;
+ sum += f*f;
+ }
+ normal = (float)(sum > 0 ? 1./sqrt(sum) : 1);
+ for (i = 0; i < DIMENSION; i++)
+ e->e_weight[i] *= normal;
+}
+
+static void choice_list(t_choice *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int i, j;
+ float bestsum = 0;
+ int bestindex = -1;
+ t_float invec[DIMENSION];
+ for (i = 0; i < DIMENSION; i++)
+ invec[i] = atom_getfloatarg(i, argc, argv);
+ for (j = 0; j < x->x_n; j++)
+ {
+ t_elem *e = x->x_vec + j;
+ float sum;
+ for (i = 0, sum = 0; i < DIMENSION; i++)
+ sum += e->e_weight[i] * invec[i];
+ if (x->x_nonrepeat) sum *= (float)(log(e->e_age));
+ if (sum > bestsum)
+ {
+ bestsum = sum;
+ sum = 1;
+ bestindex = j;
+ }
+ }
+ if (bestindex >= 0)
+ {
+ for (j = 0; j < x->x_n; j++)
+ x->x_vec[j].e_age += 1.;
+ x->x_vec[bestindex].e_age = 1;
+ }
+ outlet_float(x->x_obj.ob_outlet, (float)bestindex);
+}
+
+static void choice_free(t_choice *x)
+{
+ freebytes(x->x_vec, x->x_n * sizeof(t_elem));
+}
+
+void choice_setup(void)
+{
+ choice_class = class_new(gensym("choice"), (t_newmethod)choice_new,
+ (t_method)choice_free, sizeof(t_choice), 0, A_DEFFLOAT, 0);
+ class_addmethod(choice_class, (t_method)choice_add, gensym("add"), A_GIMME, 0);
+ class_addmethod(choice_class, (t_method)choice_clear, gensym("clear"), 0);
+ class_addmethod(choice_class, (t_method)choice_print, gensym("print"), 0);
+ class_addlist(choice_class, choice_list);
+}
diff --git a/pd/extra/choice/makefile b/pd/extra/choice/makefile
new file mode 100644
index 00000000..61866ffc
--- /dev/null
+++ b/pd/extra/choice/makefile
@@ -0,0 +1,94 @@
+NAME=choice
+CSYM=choice
+
+current: pd_linux
+
+# ----------------------- NT -----------------------
+
+pd_nt: $(NAME).dll
+
+.SUFFIXES: .dll
+
+PDNTCFLAGS = /W3 /WX /DNT /DPD /nologo
+VC="C:\Program Files\Microsoft Visual Studio\Vc98"
+
+PDNTINCLUDE = /I. /I\tcl\include /I\ftp\pd\src /I$(VC)\include
+
+PDNTLDIR = $(VC)\lib
+PDNTLIB = $(PDNTLDIR)\libc.lib \
+ $(PDNTLDIR)\oldnames.lib \
+ $(PDNTLDIR)\kernel32.lib \
+ \ftp\pd\bin\pd.lib
+
+.c.dll:
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c
+ link /dll /export:$(CSYM)_setup $*.obj $(PDNTLIB)
+
+# ----------------------- IRIX 5.x -----------------------
+
+pd_irix5: $(NAME).pd_irix5
+
+.SUFFIXES: .pd_irix5
+
+SGICFLAGS5 = -o32 -DPD -DUNIX -DIRIX -O2
+
+SGIINCLUDE = -I../../src
+
+.c.pd_irix5:
+ cc $(SGICFLAGS5) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -elf -shared -rdata_shared -o $*.pd_irix5 $*.o
+ rm $*.o
+
+# ----------------------- IRIX 6.x -----------------------
+
+pd_irix6: $(NAME).pd_irix6
+
+.SUFFIXES: .pd_irix6
+
+SGICFLAGS6 = -n32 -DPD -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \
+ -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \
+ -Ofast=ip32
+
+.c.pd_irix6:
+ cc $(SGICFLAGS6) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -n32 -IPA -shared -rdata_shared -o $*.pd_irix6 $*.o
+ rm $*.o
+
+# ----------------------- LINUX i386 -----------------------
+
+pd_linux: $(NAME).pd_linux
+
+.SUFFIXES: .pd_linux
+
+LINUXCFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer \
+ -Wall -W -Wshadow -Wstrict-prototypes -Werror \
+ -Wno-unused -Wno-parentheses -Wno-switch
+
+LINUXINCLUDE = -I../../src
+
+.c.pd_linux:
+ cc $(LINUXCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c
+ ld -export_dynamic -shared -o $*.pd_linux $*.o -lc -lm
+ strip --strip-unneeded $*.pd_linux
+ rm -f $*.o ../$*.pd_linux
+ ln -s $*/$*.pd_linux ..
+
+# ----------------------- Mac OSX -----------------------
+
+pd_darwin: $(NAME).pd_darwin
+
+.SUFFIXES: .pd_darwin
+
+DARWINCFLAGS = -DPD -O2 -Wall -W -Wshadow -Wstrict-prototypes \
+ -Wno-unused -Wno-parentheses -Wno-switch
+
+.c.pd_darwin:
+ cc $(DARWINCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c
+ cc -bundle -undefined suppress -flat_namespace -o $*.pd_darwin $*.o
+ rm -f $*.o ../$*.pd_darwin
+ ln -s $*/$*.pd_darwin ..
+
+# ----------------------------------------------------------
+
+clean:
+ rm -f *.o *.pd_* so_locations
diff --git a/pd/extra/complex-mod~.pd b/pd/extra/complex-mod~.pd
new file mode 100644
index 00000000..14f9b955
--- /dev/null
+++ b/pd/extra/complex-mod~.pd
@@ -0,0 +1,30 @@
+#N canvas 206 108 428 341 12;
+#X obj 142 87 inlet~;
+#X obj 315 88 inlet;
+#X obj 315 166 cos~;
+#X obj 351 144 +~ -0.25;
+#X obj 351 166 cos~;
+#X obj 225 87 inlet~;
+#X obj 142 215 *~;
+#X obj 225 216 *~;
+#X obj 142 251 -~;
+#X obj 142 284 outlet~;
+#X obj 212 285 outlet~;
+#X obj 212 252 +~;
+#X text 140 310 positive;
+#X text 213 311 negative;
+#X obj 315 114 phasor~;
+#X connect 0 0 6 0;
+#X connect 1 0 14 0;
+#X connect 2 0 6 1;
+#X connect 3 0 4 0;
+#X connect 4 0 7 1;
+#X connect 5 0 7 0;
+#X connect 6 0 8 0;
+#X connect 6 0 11 0;
+#X connect 7 0 8 1;
+#X connect 7 0 11 1;
+#X connect 8 0 9 0;
+#X connect 11 0 10 0;
+#X connect 14 0 3 0;
+#X connect 14 0 2 0;
diff --git a/pd/extra/help-bonk~.pd b/pd/extra/help-bonk~.pd
new file mode 100644
index 00000000..5102e860
--- /dev/null
+++ b/pd/extra/help-bonk~.pd
@@ -0,0 +1,162 @@
+#N canvas 107 94 958 626 10;
+#X obj 320 579 print;
+#X floatatom 314 501 0 0 0;
+#X obj 320 549 spigot;
+#X msg 314 471 0;
+#X msg 351 471 1;
+#X msg 442 427 bang;
+#X obj 429 518 bonk~;
+#X msg 442 244 learn 1;
+#X msg 442 304 learn 0;
+#X msg 437 486 print;
+#X obj 390 467 adc~;
+#X text 320 597 cooked;
+#X msg 565 76 \; pd dsp 1;
+#X obj 257 579 print;
+#X floatatom 251 501 0 0 0;
+#X obj 257 549 spigot;
+#X msg 251 471 0;
+#X msg 282 471 1;
+#X text 257 597 raw;
+#N canvas 366 126 600 400 synth 0;
+#X obj 112 24 r bonk-cooked;
+#X obj 112 49 unpack;
+#X obj 112 99 * 12;
+#X obj 112 124 div 7;
+#X obj 112 74 + 1;
+#X obj 112 174 mtof;
+#X obj 112 224 osc~;
+#X obj 112 249 cos~;
+#X obj 112 149 + 47;
+#X obj 209 247 line~;
+#X obj 209 272 *~;
+#X obj 209 297 lop~ 500;
+#X obj 112 274 *~;
+#X obj 103 361 dac~;
+#X obj 253 165 dbtorms;
+#X obj 253 115 * 0.5;
+#X obj 253 140 + 50;
+#X obj 211 189 f;
+#X msg 173 159 bang;
+#X obj 258 83 inlet;
+#X obj 111 307 hip~ 5;
+#X msg 34 24 0 60;
+#X obj 112 199 sig~;
+#X msg 209 222 \$1 \, 0 200;
+#X connect 0 0 1 0;
+#X connect 1 0 4 0;
+#X connect 2 0 3 0;
+#X connect 3 0 8 0;
+#X connect 4 0 2 0;
+#X connect 5 0 18 0;
+#X connect 5 0 22 0;
+#X connect 6 0 7 0;
+#X connect 7 0 12 0;
+#X connect 8 0 5 0;
+#X connect 9 0 10 0;
+#X connect 9 0 10 1;
+#X connect 10 0 11 0;
+#X connect 11 0 12 1;
+#X connect 12 0 20 0;
+#X connect 14 0 17 1;
+#X connect 15 0 16 0;
+#X connect 16 0 14 0;
+#X connect 17 0 23 0;
+#X connect 18 0 17 0;
+#X connect 19 0 15 0;
+#X connect 20 0 13 1;
+#X connect 20 0 13 0;
+#X connect 21 0 1 0;
+#X connect 22 0 6 0;
+#X connect 23 0 9 0;
+#X restore 804 86 pd synth;
+#X obj 454 549 s bonk-cooked;
+#X floatatom 804 63 0 0 0;
+#X msg 804 33 0;
+#X msg 442 274 learn 10;
+#X msg 442 334 forget;
+#X msg 442 364 write templates.txt;
+#X msg 442 394 read templates.txt;
+#X msg 835 33 90;
+#X msg 442 120 thresh 6 50;
+#X text 8 70 The Bonk object takes an audio signal input and looks
+for "attacks" defined as sharp changes in the spectral envelope of
+the incoming sound. Optionally \, and less reliably \, you can have
+Bonk check the attack against a collection of stored templates to try
+to guess which of two or more instruments was hit. Bonk is described
+theoretically in the 1998 ICMC proceedings \, reprinted on http://man104nfs.ucsd.edu/~mpuckett.
+;
+#X text 470 70 click here;
+#X text 471 83 to start DSP;
+#X text 8 191 Bonk's two outputs are the raw spectrum of the attack
+(provided as a list of 11 numbers giving the signal "loudness" in the
+11 frequency bands used) \, and the "cooked" output which gives only
+an instrument number (counting up from zero) and a "velocity". The
+instrumnent number is significant only if Bonk has a "template set"
+in memory.;
+#X text 8 368 In this patch \, after starting DSP \, you can print
+out the raw or cooked output using the two "spigots" or listen to a
+synthesizer output by raising its volume.;
+#X text 259 448 enable printout;
+#X text 705 32 output volume;
+#X text 719 50 (0-100);
+#X text 533 118 Set low and high thresholds. Signal growth must exceed
+the high one and then fall to the low one to make an attack.;
+#X text 533 151 Minimum velocity to output (quieter notes are ignored.)
+;
+#X msg 442 180 mask 4 0.7;
+#X msg 442 214 debounce 0;
+#X text 8 299 Bonk's analysis is carried out on a 256-point window
+(6 msec at 44.1 kHz) and by default the analysis period is 128 samples.
+The analysis period can be specified as Bonk's creation argument but
+must be a multiple of 64;
+#X text 532 219 Minimum time (msec) between attacks;
+#X text 532 170 Describes how energy in each frequency band masks later
+energy in the band. Here the masking is total for 4 analysis periods
+and then drops by 0.7 each period.;
+#X text 530 244 Forget all templates and start learning new ones. The
+argument gives the number of times you will hit each instrument (10
+recommended.) Turn on the output volume above for audible feedback
+as you train Bonk. "Learn 0" exits learn mode.;
+#X text 530 328 Forget the last template. In Learn mode \, use "forget"
+to erase and record over a template.;
+#X text 595 368 Write templates to a file in text-editable format.
+;
+#X text 596 398 Read templates from a file.;
+#X text 538 493 Print out all settings and templates.;
+#X msg 442 150 minvel 10;
+#X text 538 426 Poll the current spectrum via "raw" outlet \, You can
+set a very high threshold if you don't want attacks mixed in.;
+#X text 218 12 BONK - an attack detector for small percussion instruments
+;
+#X msg 634 517 print;
+#X msg 437 456 debug 0;
+#X text 538 466 turn debugging on or off.;
+#X connect 1 0 2 1;
+#X connect 2 0 0 0;
+#X connect 3 0 1 0;
+#X connect 4 0 1 0;
+#X connect 5 0 6 0;
+#X connect 6 0 15 0;
+#X connect 6 1 2 0;
+#X connect 6 1 20 0;
+#X connect 7 0 6 0;
+#X connect 8 0 6 0;
+#X connect 9 0 6 0;
+#X connect 10 0 6 0;
+#X connect 14 0 15 1;
+#X connect 15 0 13 0;
+#X connect 16 0 14 0;
+#X connect 17 0 14 0;
+#X connect 21 0 19 0;
+#X connect 22 0 21 0;
+#X connect 23 0 6 0;
+#X connect 24 0 6 0;
+#X connect 25 0 6 0;
+#X connect 26 0 6 0;
+#X connect 27 0 21 0;
+#X connect 28 0 6 0;
+#X connect 39 0 6 0;
+#X connect 40 0 6 0;
+#X connect 49 0 6 0;
+#X connect 53 0 6 0;
diff --git a/pd/extra/help-choice.pd b/pd/extra/help-choice.pd
new file mode 100644
index 00000000..df46ddc0
--- /dev/null
+++ b/pd/extra/help-choice.pd
@@ -0,0 +1,41 @@
+#N canvas 16 5 488 531 12;
+#X obj 8 186 choice;
+#X msg 41 86 print;
+#X msg 29 63 clear;
+#X msg 8 34 add 1 0 0 \, add 0 1 0 \, add 0 0 1 \, add 1 1 1 \, add 1 1 0;
+#X obj 77 162 pack 0 0 0;
+#X floatatom 182 120;
+#X floatatom 148 120;
+#X floatatom 115 120;
+#X obj 77 142 f;
+#X msg 77 120 bang;
+#X floatatom 8 207;
+#X obj 53 187 choice 1;
+#X floatatom 53 208;
+#X obj 76 4 choice;
+#X text 122 5 - search for a best match to an incoming list;
+#X text 19 234 The choice object holds a list of vectors \, each having up to ten elements. When sent a list of numbers \, it outputs the index of the known vector that matches most closely. The quality of the match is the dot product of the two vectors after normalizing them \, i.e. \, the vector whose direction is closest to that of the input wins.;
+#X text 19 316 If given a nonzero creation argument \, choice tries to avoid repetitious outputs by weighting less recently output vectors preferentially.;
+#X text 18 354 You can use this to choose interactively between a number of behaviors depending on their attributes. For example \, you might have stored a number of melodies \, of which some are syncopated \, some chromatic \, some are more than 100 years old \, some are bugle calls \, and some are Christmas carols. You could then ask to find a syncopated bugle call (1 \, 0 \, 0 \, 1 \, 0) and you'll get the thing most closely matching the request.;
+#X text 17 461 You can use numbers other than 0 and 1 to indicate relative strengths of the attributes \, or even use negative numbers to indicate opposites \, either in the incoming lists or in the stored ones.;
+#X text 273 513 updated for Pd version-0.30;
+#X text 72 63 delete all stored vectors;
+#X text 394 36 add vectors;
+#X text 81 85 debugging printout;
+#X text 69 104 tweak the numbers and hit "bang" to input a list;
+#X text 115 187 creation argument to avoid repeated outout;
+#X text 85 208 output is the index of best match \, counting from zero;
+#X connect 0 0 10 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 0;
+#X connect 2 0 11 0;
+#X connect 3 0 0 0;
+#X connect 3 0 11 0;
+#X connect 4 0 0 0;
+#X connect 4 0 11 0;
+#X connect 5 0 4 2;
+#X connect 6 0 4 1;
+#X connect 7 0 8 1;
+#X connect 8 0 4 0;
+#X connect 9 0 8 0;
+#X connect 11 0 12 0;
diff --git a/pd/extra/help-complex-mod~.pd b/pd/extra/help-complex-mod~.pd
new file mode 100644
index 00000000..9e14904d
--- /dev/null
+++ b/pd/extra/help-complex-mod~.pd
@@ -0,0 +1,26 @@
+#N canvas 136 85 600 480 12;
+#X graph graph1 0 -1 882 1 279 209 579 39;
+#X array mod-output 882 float;
+#X pop;
+#X msg 84 256 bang \; pd dsp 1;
+#X floatatom 67 56;
+#X obj 67 186 complex-mod~;
+#X obj 67 84 phasor~ 440;
+#X obj 67 115 cos~;
+#X obj 106 138 cos~;
+#X obj 106 114 -~ 0.25;
+#X floatatom 145 152;
+#X text 93 351 The complex modulator takes two signals in which it considers to be the real and imaginary part of a complex-valued signal. It then does a complex multiplication by a sinusoud to shift all frequencies up or down by any frequency shift in Hz. See also hilbert~.;
+#X obj 69 298 tabwrite~ mod-output;
+#X text 149 204 right outlet;
+#X text 151 220 gives the other;
+#X text 149 236 sideband;
+#X connect 1 0 10 0;
+#X connect 2 0 4 0;
+#X connect 3 0 10 0;
+#X connect 4 0 5 0;
+#X connect 4 0 7 0;
+#X connect 5 0 3 0;
+#X connect 6 0 3 1;
+#X connect 7 0 6 0;
+#X connect 8 0 3 2;
diff --git a/pd/extra/help-fiddle~.pd b/pd/extra/help-fiddle~.pd
new file mode 100644
index 00000000..a7feb4f7
--- /dev/null
+++ b/pd/extra/help-fiddle~.pd
@@ -0,0 +1,107 @@
+#N canvas 93 26 980 745 10;
+#X obj 262 522 phasor~;
+#X obj 531 616 unpack;
+#X floatatom 531 666;
+#X msg 437 449 print;
+#X obj 262 500 sig~;
+#X floatatom 262 478;
+#X obj 262 456 mtof;
+#X floatatom 262 434;
+#X floatatom 545 643;
+#X obj 531 576 route 1 2 3 4;
+#X obj 614 616 unpack;
+#X floatatom 614 666;
+#X floatatom 628 643;
+#X obj 698 616 unpack;
+#X floatatom 698 666;
+#X floatatom 712 643;
+#X obj 389 616 unpack;
+#X floatatom 389 666;
+#X floatatom 403 643;
+#X obj 334 545 *~;
+#X obj 322 394 loadbang;
+#X obj 353 522 sig~;
+#X floatatom 353 500;
+#X msg 322 478 1;
+#X msg 353 478 0;
+#X floatatom 466 666;
+#X obj 281 666 print attack;
+#X obj 190 666 print pitch;
+#X msg 555 45 \; pd dsp 1;
+#X text 460 39 click here;
+#X text 460 61 to start DSP;
+#X text 226 4 FIDDLE - pitch estimator and sinusoidal peak finder;
+#X text 8 70 The Fiddle object estimates the pitch and amplitude of an incoming sound \, both continuously and as a stream of discrete "note" events. Fiddle optionally outputs a list of detected sinusoidal peaks used to make the pitch determination. Fiddle is described theoretically in the 1998 ICMC proceedings \, reprinted on http://man104nfs.ucsd.edu/~mpuckett.;
+#X text 8 170 Fiddle's creation arguments specify an analysis window size \, the maximum polyphony (i.e. \, the number of simultaneous "pitches" to try to find) \, the number of peaks in the spectrum to consider \, and the number of peaks \, if any \, to output "raw." The outlets give discrete pitch (a number) \, detected attacks in the amplitude envelope (a bang) \, one or more voices of continuous pitch and amplitude \, overall amplitude \, and optionally a sequence of messages with the peaks.;
+#X text 8 296 The analysis hop size is half the window size so in the example shown here \, one analysis is done every 512 samples (11.6 msec at 44K1) \, and the analysis uses the most recent 1024 samples (23.2 msec at 44K1). The minimum frequency that Fiddle will report is 2-1/2 cycles per analysis windows \, or about 108 Hz. (just below MIDI 45.);
+#X text 669 535 number of pitch outlets (1-3 \, default 1);
+#X text 669 557 number of peaks to find (1-100 \, default 20);
+#X text 669 579 number of peaks to output (default 0.);
+#X msg 441 107 amp-range 40 50;
+#X msg 439 227 reattack 100 10;
+#X msg 438 282 npartial 7;
+#X msg 438 170 vibrato 50 0.5;
+#X text 560 91 a low and high amplitude threshold: if signal amplitude is below the low threshold \, no pitches or peaks are output. The high threshold is a minimum at which "cooked" outputs may appear.;
+#X text 560 152 A period in milliseconds (50) over which the raw pitch may not deviate more than an interval in half-tones (0.5) from the average pitch to report it as a note to the "cooked" pitch outlet.;
+#X text 560 213 A period in milliseconds (100) over which a re-attack is reported if the amplitude rises more than (1) dB. The re-attack will result in a "bang" in the attack outlet and may give rise to repeated notes in the cooked pitch output.;
+#X text 142 432 test input pitch;
+#X text 330 444 test input;
+#X text 330 457 amplitude;
+#X obj 410 545 fiddle~ 1024 1 20 3;
+#X text 538 690 individual sinusoidal components;
+#X text 466 688 amplitude;
+#X text 476 703 (dB);
+#X text 389 688 raw pitch;
+#X text 376 712 and amplitude;
+#X text 364 729 (up to 3 outputs);
+#X text 287 686 bang on;
+#X text 287 708 attack;
+#X text 185 686 cooked pitch;
+#X text 202 703 output;
+#X text 545 545 ------ arguments:;
+#X msg 262 412 57;
+#X msg 440 340 auto 1;
+#X msg 440 362 auto 0;
+#X msg 440 407 bang;
+#X text 561 405 poll current values --- useful if not in auto mode \,;
+#X text 560 274 Higher partials are weighed less strongly than lower ones in determining the pitch. This specifies the number of the partial (7) which will be weighted half as strongly as the fundamental.;
+#X text 560 335 start and stop "auto" mode (on by default.) If off \, output only appears on "bang" (poll mode).;
+#X text 561 448 print out all settings;
+#X text 669 513 window size (128-2048 \, default 1024);
+#X connect 0 0 19 0;
+#X connect 1 0 2 0;
+#X connect 1 1 8 0;
+#X connect 3 0 48 0;
+#X connect 4 0 0 0;
+#X connect 5 0 4 0;
+#X connect 6 0 5 0;
+#X connect 7 0 6 0;
+#X connect 9 0 1 0;
+#X connect 9 1 10 0;
+#X connect 9 2 13 0;
+#X connect 10 0 11 0;
+#X connect 10 1 12 0;
+#X connect 13 0 14 0;
+#X connect 13 1 15 0;
+#X connect 16 0 17 0;
+#X connect 16 1 18 0;
+#X connect 19 0 48 0;
+#X connect 20 0 60 0;
+#X connect 20 0 23 0;
+#X connect 21 0 19 1;
+#X connect 22 0 21 0;
+#X connect 23 0 22 0;
+#X connect 24 0 22 0;
+#X connect 38 0 48 0;
+#X connect 39 0 48 0;
+#X connect 40 0 48 0;
+#X connect 41 0 48 0;
+#X connect 48 0 27 0;
+#X connect 48 1 26 0;
+#X connect 48 2 16 0;
+#X connect 48 3 25 0;
+#X connect 48 4 9 0;
+#X connect 60 0 7 0;
+#X connect 61 0 48 0;
+#X connect 62 0 48 0;
+#X connect 63 0 48 0;
diff --git a/pd/extra/help-hilbert~.pd b/pd/extra/help-hilbert~.pd
new file mode 100644
index 00000000..130ec750
--- /dev/null
+++ b/pd/extra/help-hilbert~.pd
@@ -0,0 +1,18 @@
+#N canvas 156 234 600 488 12;
+#X obj 67 124 hilbert~;
+#X obj 66 85 osc~ 440;
+#X graph graph1 0 -1 882 1 279 209 579 39;
+#X array out-left 882 float;
+#X array out-right 882 float;
+#X pop;
+#X obj 67 274 tabwrite~ out-left;
+#X obj 118 248 tabwrite~ out-right;
+#X msg 137 188 bang \; pd dsp 1;
+#X floatatom 66 57;
+#X text 71 319 The Hilbert transform (the name is abused here according to computer music tradition) puts out a phase quadrature version of the input signal suitable for signal sideband modulation via complex-mod~.;
+#X connect 0 0 3 0;
+#X connect 0 1 4 0;
+#X connect 1 0 0 0;
+#X connect 5 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 1 0;
diff --git a/pd/extra/help-loop~.pd b/pd/extra/help-loop~.pd
new file mode 100644
index 00000000..6acff93c
--- /dev/null
+++ b/pd/extra/help-loop~.pd
@@ -0,0 +1,66 @@
+#N canvas 33 0 538 640 12;
+#X floatatom 55 169;
+#X obj 273 343 print~;
+#X msg 273 305 bang;
+#X obj 55 303 loop~;
+#X floatatom 80 244;
+#X msg 69 217 bang;
+#X obj 172 350 print~;
+#X msg 170 311 bang;
+#X graph graph1 0 -1 150000 1 306 586 506 436;
+#X array array2 150000 float;
+#X pop;
+#X msg 306 594 \; array2 resize 150000;
+#X obj 29 578 soundfiler;
+#X obj 55 419 tabread4~ array2;
+#X obj 55 373 *~;
+#X obj 55 488 dac~;
+#X obj 55 465 hip~ 5;
+#X obj 101 377 samphold~;
+#X obj 55 396 +~;
+#X floatatom 102 268;
+#X obj 102 291 *~ 1000;
+#X msg 47 533 read ../doc/sound/bell.aiff array2;
+#X msg 47 556 read ../doc/sound/vocal.aiff array2;
+#X msg 61 194 set 0.5;
+#X text 100 164 left signal input is transposition (1 is normal \, 2 is up an octave \, etc);
+#X text 37 6 loop~ - phase generator for looping samplers;
+#X text 121 193 set phase (0 to 1);
+#X text 121 213 reset phase to 0;
+#X text 118 243 right signal input is window size in samples;
+#X text 140 267 here's how to handle onsets;
+#X obj 55 442 *~;
+#X floatatom 171 397;
+#X obj 171 466 line~;
+#X obj 171 420 dbtorms;
+#X obj 171 443 pack 0 50;
+#X text 205 396 output level 0-100;
+#X text 170 290 print outputs;
+#X text 21 25 loop~ takes input signals to set a window size and transposition \, and outputs a phase and a sampled window size. The window size only changes at phase zero crossings and the phase output is adjusted so that changing window size doesn't change the transposition.;
+#X text 22 95 You can send "bang" or "set" message to force the phase to zero--you should mute the output before doing so. This may be desirable if you've set a large window size but then want to decrease it without waiting for the next phase crossing.;
+#X connect 0 0 3 0;
+#X connect 2 0 1 0;
+#X connect 3 0 6 0;
+#X connect 3 0 12 0;
+#X connect 3 0 15 1;
+#X connect 3 1 1 0;
+#X connect 3 1 12 1;
+#X connect 4 0 3 1;
+#X connect 5 0 3 0;
+#X connect 7 0 6 0;
+#X connect 11 0 28 0;
+#X connect 12 0 16 0;
+#X connect 14 0 13 0;
+#X connect 14 0 13 1;
+#X connect 15 0 16 1;
+#X connect 16 0 11 0;
+#X connect 17 0 18 0;
+#X connect 18 0 15 0;
+#X connect 19 0 10 0;
+#X connect 20 0 10 0;
+#X connect 21 0 3 0;
+#X connect 28 0 14 0;
+#X connect 29 0 31 0;
+#X connect 30 0 28 1;
+#X connect 31 0 32 0;
+#X connect 32 0 30 0;
diff --git a/pd/extra/help-paf~.pd b/pd/extra/help-paf~.pd
new file mode 100644
index 00000000..b36a7597
--- /dev/null
+++ b/pd/extra/help-paf~.pd
@@ -0,0 +1,165 @@
+#N canvas 19 4 745 493 12;
+#X msg 37 311 freq \$1 100;
+#X obj 37 286 mtof;
+#X msg 127 311 amp \$1 100;
+#X obj 127 261 r amp;
+#X obj 212 262 r cf;
+#X obj 212 287 mtof;
+#X msg 212 312 cf \$1 100;
+#X obj 37 261 r pit;
+#X msg 149 426 bang;
+#X obj 80 455 dac~;
+#X obj 535 161 s vfr;
+#X obj 535 86 r vfr;
+#X obj 483 161 s vib;
+#X obj 483 86 r vib;
+#X msg 535 111 set \$1;
+#X floatatom 535 136 0 0 0;
+#X msg 483 111 set \$1;
+#X floatatom 483 136 0 0 0;
+#X obj 434 161 s bw;
+#X obj 385 161 s cf;
+#X obj 266 160 s amp;
+#X obj 325 159 s pit;
+#X obj 325 84 r pit;
+#X msg 325 109 set \$1;
+#X floatatom 325 134 0 0 0;
+#X obj 266 85 r amp;
+#X msg 266 110 set \$1;
+#X floatatom 266 135 0 0 0;
+#X msg 385 111 set \$1;
+#X floatatom 385 136 0 0 0;
+#X obj 385 86 r cf;
+#X msg 434 111 set \$1;
+#X floatatom 434 136 0 0 0;
+#X obj 434 86 r bw;
+#X msg 286 312 bw \$1 100;
+#X obj 286 262 r bw;
+#X obj 286 287 mtof;
+#X obj 365 262 r vib;
+#X msg 365 312 vib \$1 100;
+#X msg 445 312 vfr \$1 100;
+#X obj 445 287 / 8;
+#X obj 445 262 r vfr;
+#X obj 365 287 / 660;
+#X msg 589 111 set \$1;
+#X floatatom 589 136 0 0 0;
+#X obj 589 86 r shift;
+#X obj 589 161 s shift;
+#X obj 530 262 r shift;
+#X msg 530 312 shift \$1 100;
+#X obj 127 286 dbtorms;
+#X obj 94 400 paf~;
+#X obj 30 432 s~ foo;
+#X msg 627 313 phase 0 0 0;
+#X obj 149 451 print~ output;
+#N canvas 447 311 726 483 spectrum 0;
+#X graph graph1 0 -1.02 882 1.02 405 401 605 271;
+#X array pulse-output 882 float;
+#X pop;
+#X text 405 403 --------- 0.02 seconds ------;
+#X graph graph1 0 0 128 500 391 208 647 78;
+#X array spectrum 128 float;
+#X pop;
+#X obj 137 257 tabwrite~ pulse-output;
+#X msg 106 174 bang;
+#N canvas 204 17 358 238 fft 0;
+#X obj 46 48 inlet~;
+#X obj 159 181 tabwrite~ spectrum;
+#X obj 159 145 inlet;
+#X obj 46 78 rfft~;
+#X obj 46 111 *~;
+#X obj 77 111 *~;
+#X obj 46 141 sqrt~;
+#X obj 191 45 block~ 1024 1;
+#X connect 0 0 3 0;
+#X connect 2 0 1 0;
+#X connect 3 0 4 0;
+#X connect 3 0 4 1;
+#X connect 3 1 5 0;
+#X connect 3 1 5 1;
+#X connect 4 0 6 0;
+#X connect 5 0 6 0;
+#X connect 6 0 1 0;
+#X restore 46 228 pd fft;
+#X text 33 8 PULSE SPECTRUM MEASUREMENT;
+#X text 379 221 0;
+#X text 627 218 5512;
+#X obj 94 123 r~ foo;
+#X obj 41 160 *~ 1;
+#X floatatom 44 113 0 0 0;
+#X obj 179 136 metro 1000;
+#X floatatom 178 108 0 0 0;
+#X obj 56 44 r graph;
+#X obj 140 205 *~ 10;
+#X connect 4 0 3 0;
+#X connect 4 0 5 1;
+#X connect 9 0 10 0;
+#X connect 9 0 15 0;
+#X connect 10 0 5 0;
+#X connect 11 0 10 1;
+#X connect 12 0 4 0;
+#X connect 13 0 12 0;
+#X connect 14 0 11 0;
+#X connect 14 0 13 0;
+#X connect 15 0 3 0;
+#X restore 438 413 pd spectrum;
+#X msg 42 133 \; pd dsp 1 \; pit 60 \; cf 60 \; graph 20;
+#X text 48 10 The "PAF" generator \, described in a paper in JAES 43/1 pp. 40-47 \, reprinted on Miller Puckette's web page. Often used in Philippe Manoury's music. The important controls are center frequency ("cf") and bandwidth ("bw") here controlled as MIDI values.;
+#X text 37 88 clich here to test;
+#X text 34 106 (then set amplitude);
+#X text 423 390 click here to see spectrum;
+#X msg 343 383 bw 0;
+#X msg 367 418 bw 0;
+#X msg 295 418 bw 80 100;
+#X msg 295 384 bw 700;
+#X connect 0 0 50 0;
+#X connect 1 0 0 0;
+#X connect 2 0 50 0;
+#X connect 3 0 49 0;
+#X connect 4 0 5 0;
+#X connect 5 0 6 0;
+#X connect 6 0 50 0;
+#X connect 7 0 1 0;
+#X connect 8 0 53 0;
+#X connect 11 0 14 0;
+#X connect 13 0 16 0;
+#X connect 14 0 15 0;
+#X connect 15 0 10 0;
+#X connect 16 0 17 0;
+#X connect 17 0 12 0;
+#X connect 22 0 23 0;
+#X connect 23 0 24 0;
+#X connect 24 0 21 0;
+#X connect 25 0 26 0;
+#X connect 26 0 27 0;
+#X connect 27 0 20 0;
+#X connect 28 0 29 0;
+#X connect 29 0 19 0;
+#X connect 30 0 28 0;
+#X connect 31 0 32 0;
+#X connect 32 0 18 0;
+#X connect 33 0 31 0;
+#X connect 34 0 50 0;
+#X connect 35 0 36 0;
+#X connect 36 0 34 0;
+#X connect 37 0 42 0;
+#X connect 38 0 50 0;
+#X connect 39 0 50 0;
+#X connect 40 0 39 0;
+#X connect 41 0 40 0;
+#X connect 42 0 38 0;
+#X connect 43 0 44 0;
+#X connect 44 0 46 0;
+#X connect 45 0 43 0;
+#X connect 47 0 48 0;
+#X connect 48 0 50 0;
+#X connect 49 0 2 0;
+#X connect 50 0 9 0;
+#X connect 50 0 51 0;
+#X connect 50 0 9 1;
+#X connect 50 0 53 0;
+#X connect 52 0 50 0;
+#X connect 60 0 50 0;
+#X connect 62 0 50 0;
+#X connect 63 0 50 0;
diff --git a/pd/extra/help-pique.pd b/pd/extra/help-pique.pd
new file mode 100644
index 00000000..1689c95b
--- /dev/null
+++ b/pd/extra/help-pique.pd
@@ -0,0 +1,33 @@
+#N canvas 143 0 729 407 12;
+#X obj 47 11 pique;
+#X text 105 12 -- find peaks in an FFT spectrum;
+#X obj 214 174 rfft~;
+#X obj 131 129 osc~ 2000;
+#X graph graph2 0 -64 63 64 519 179 719 39;
+#X array fft-real 64 float;
+#X pop;
+#X graph graph3 0 -64 63 64 519 327 719 187;
+#X array fft-imag 64 float;
+#X pop;
+#X obj 214 215 tabwrite~ fft-real;
+#X obj 245 240 tabwrite~ fft-imag;
+#X obj 315 158 metro 1000;
+#X obj 315 116 loadbang;
+#X msg 315 138 1;
+#X obj 91 349 pique;
+#X msg 91 322 64 fft-real fft-imag 10;
+#X obj 91 376 print;
+#X obj 205 132 osc~ 5000;
+#X text 25 37 pique takes unwindowed FFT analyses as input (they should be stored in arrays) and outputs a list of peaks \, giving their peak number \, frequency \, amplitude \, and phase (as a cosine/sine pair.);
+#X text 13 289 message argumnets: number of FFT points \, fft real part \, fft imaginary part \, maximum number of peaks to report.;
+#X text 578 387 updated for Pd 0.31.;
+#X connect 2 0 6 0;
+#X connect 2 1 7 0;
+#X connect 3 0 2 0;
+#X connect 8 0 6 0;
+#X connect 8 0 7 0;
+#X connect 9 0 10 0;
+#X connect 10 0 8 0;
+#X connect 11 0 13 0;
+#X connect 12 0 11 0;
+#X connect 14 0 2 0;
diff --git a/pd/extra/help-rev1.pd b/pd/extra/help-rev1.pd
new file mode 100644
index 00000000..55580bd5
--- /dev/null
+++ b/pd/extra/help-rev1.pd
@@ -0,0 +1,119 @@
+#N canvas 55 21 1008 526 12;
+#X obj 148 439 dac~;
+#X obj 58 72 line~;
+#X msg 58 49 0 \, 10000 5;
+#X obj 58 118 cos~;
+#X msg 146 70 1;
+#X obj 146 47 loadbang;
+#X obj 58 95 clip~ 0 0.25;
+#X floatatom 173 264 0 0 0;
+#X obj 251 134 line~;
+#X obj 251 157 cos~;
+#X msg 324 54 -0.25 \, 0.25 100;
+#X obj 251 8 loadbang;
+#X msg 251 31 -0.25;
+#X obj 251 203 *~;
+#X obj 58 140 hip~ 5;
+#X floatatom 162 328 0 0 0;
+#X obj 162 373 pack 0 100;
+#X obj 162 396 line~;
+#X obj 148 416 *~;
+#X obj 162 350 dbtorms;
+#X msg 324 77 -0.25 \, 0.25 400;
+#X floatatom 324 145 0 0 0;
+#X obj 324 191 osc~ 440;
+#X obj 324 168 mtof;
+#X msg 324 31 -0.25 \, 0.25 20;
+#X obj 251 180 *~ 0.1;
+#X msg 324 100 -0.25 \, 0.25 1000;
+#X msg 324 122 -0.25 \, 0.25 2000;
+#X obj 324 226 *~;
+#X obj 342 252 *~;
+#X obj 58 439 dac~;
+#X floatatom 68 323 0 0 0;
+#X obj 68 368 pack 0 100;
+#X obj 68 391 line~;
+#X obj 58 416 *~;
+#X obj 68 346 dbtorms;
+#X msg 324 8 0;
+#X obj 308 257 *~;
+#X obj 58 26 metro 2000;
+#X floatatom 58 4 0 0 0;
+#X msg 220 265 bang;
+#X obj 284 322 env~ 32768;
+#X floatatom 284 344 0 0 0;
+#X text 166 244 1 sec;
+#X text 143 226 dB after;
+#X text 220 245 clear;
+#X text 1 51 impulse;
+#X text 362 7 tone;
+#X text 484 31 beeps;
+#X text 428 167 This is an experimental reverberator design composed
+of a series of allpass filters with exponentially growing delay times.
+Each allpass filter has a gain of 0.7. The reverb time is adjusted
+by adjusting the input gains of the allpass filters. The last unit
+is modified so that its first two "echos" mimic those of an allpass
+but its loop gain depends on reverb time.;
+#X text 430 299 Reverb time is controlled by specifying the dB gain
+(100 normal) after one second \, so that 100 corresponds to infinite
+reverb time \, 70 to two seconds \, 40 to one second \, and 0 to 0
+;
+#X text 671 499 modified for Pd version 0.30.;
+#X msg 560 34 \; pd dsp 1;
+#X text 427 475 The rev1~ module eats about 18% of my 300mHz P2 machine.
+;
+#X obj 148 289 rev1~;
+#X text 428 381 The "clear" button impolitely clears out all the delay
+lines \, You may immediately resume pumping the reverberator \, but
+the input signal should be cleanly enveloped. The output \, too \,
+must be enveloped and may not be opened until 5 msec after the "clear"
+message is sent.;
+#X connect 1 0 6 0;
+#X connect 2 0 1 0;
+#X connect 3 0 14 0;
+#X connect 4 0 1 0;
+#X connect 5 0 4 0;
+#X connect 6 0 3 0;
+#X connect 7 0 54 1;
+#X connect 8 0 9 0;
+#X connect 9 0 25 0;
+#X connect 10 0 8 0;
+#X connect 11 0 12 0;
+#X connect 12 0 8 0;
+#X connect 13 0 14 0;
+#X connect 14 0 34 0;
+#X connect 14 0 54 0;
+#X connect 15 0 19 0;
+#X connect 16 0 17 0;
+#X connect 17 0 18 1;
+#X connect 18 0 0 0;
+#X connect 19 0 16 0;
+#X connect 20 0 8 0;
+#X connect 21 0 23 0;
+#X connect 22 0 13 1;
+#X connect 22 0 28 0;
+#X connect 22 0 28 1;
+#X connect 22 0 29 0;
+#X connect 23 0 22 0;
+#X connect 24 0 8 0;
+#X connect 25 0 13 0;
+#X connect 26 0 8 0;
+#X connect 27 0 8 0;
+#X connect 28 0 29 1;
+#X connect 28 0 13 1;
+#X connect 28 0 37 0;
+#X connect 28 0 37 1;
+#X connect 29 0 13 1;
+#X connect 31 0 35 0;
+#X connect 32 0 33 0;
+#X connect 33 0 34 1;
+#X connect 34 0 30 0;
+#X connect 35 0 32 0;
+#X connect 36 0 8 0;
+#X connect 37 0 13 1;
+#X connect 38 0 2 0;
+#X connect 39 0 38 0;
+#X connect 40 0 54 2;
+#X connect 41 0 42 0;
+#X connect 54 0 18 0;
+#X connect 54 0 41 0;
diff --git a/pd/extra/help-rlshift~.pd b/pd/extra/help-rlshift~.pd
new file mode 100644
index 00000000..cdfd8830
--- /dev/null
+++ b/pd/extra/help-rlshift~.pd
@@ -0,0 +1,29 @@
+#N canvas 143 0 673 325 12;
+#X msg 268 277 bang;
+#X obj 244 303 print~;
+#X msg 185 278 bang;
+#X obj 161 304 print~;
+#X text 53 117 click here first;
+#X msg 72 270 bang;
+#X obj 48 296 print~;
+#X text 162 222 shift left;
+#X text 243 224 shift right;
+#X obj 161 252 lrshift~ 1;
+#X obj 244 251 lrshift~ -1;
+#X text 39 37 Acting at whatever vector size the window is running at \, lrshift~ shifts samples to the left (toward the beginning sample) or to the right. The argument gives the direction and the amount of the shift. The rightmost (or leftmost) samples are set to zero.;
+#X graph graph2 0 0 63 1 448 258 648 118;
+#X array shiftin 64 float;
+#X pop;
+#X obj 47 11 rlshift~;
+#X text 115 11 -- shift signal vector elements left or right;
+#X msg 54 138 \; pd dsp 1 \; shiftin 1 1;
+#X obj 48 204 tabreceive~ shiftin;
+#X text 525 308 Updated for Pd 0.31.;
+#X connect 0 0 1 0;
+#X connect 2 0 3 0;
+#X connect 5 0 6 0;
+#X connect 9 0 3 0;
+#X connect 10 0 1 0;
+#X connect 16 0 6 0;
+#X connect 16 0 9 0;
+#X connect 16 0 10 0;
diff --git a/pd/extra/hilbert~.pd b/pd/extra/hilbert~.pd
new file mode 100644
index 00000000..5bc7a550
--- /dev/null
+++ b/pd/extra/hilbert~.pd
@@ -0,0 +1,15 @@
+#N canvas 451 128 556 360 12;
+#X obj 117 129 biquad~ 0.83774 -0.06338 0.06338 -0.83774 1;
+#X obj 117 103 biquad~ 1.94632 -0.94657 0.94657 -1.94632 1;
+#X obj 98 186 biquad~ -0.02569 0.260502 -0.260502 0.02569 1;
+#X obj 98 212 biquad~ 1.8685 -0.870686 0.870686 -1.8685 1;
+#X obj 98 76 inlet~;
+#X obj 117 158 outlet~;
+#X obj 98 239 outlet~;
+#X text 105 273 This is a pair of all-pass filters whose outputs somehow manage to be about 90 degrees out of phase from each other. I don't know what phase relation they have with the original signal. I adapted this from a 4X patch by Emmanuel Favreau \, circa 1982;
+#X connect 0 0 5 0;
+#X connect 1 0 0 0;
+#X connect 2 0 3 0;
+#X connect 3 0 6 0;
+#X connect 4 0 1 0;
+#X connect 4 0 2 0;
diff --git a/pd/extra/pique/makefile b/pd/extra/pique/makefile
new file mode 100644
index 00000000..2a565566
--- /dev/null
+++ b/pd/extra/pique/makefile
@@ -0,0 +1,94 @@
+NAME=pique
+CSYM=pique
+
+current: pd_linux
+
+# ----------------------- NT -----------------------
+
+pd_nt: $(NAME).dll
+
+.SUFFIXES: .dll
+
+PDNTCFLAGS = /W3 /WX /DNT /DPD /nologo
+VC="C:\Program Files\Microsoft Visual Studio\Vc98"
+
+PDNTINCLUDE = /I. /I\tcl\include /I\ftp\pd\src /I$(VC)\include
+
+PDNTLDIR = $(VC)\lib
+PDNTLIB = $(PDNTLDIR)\libc.lib \
+ $(PDNTLDIR)\oldnames.lib \
+ $(PDNTLDIR)\kernel32.lib \
+ \ftp\pd\bin\pd.lib
+
+.c.dll:
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c
+ link /dll /export:$(CSYM)_setup $*.obj $(PDNTLIB)
+
+# ----------------------- IRIX 5.x -----------------------
+
+pd_irix5: $(NAME).pd_irix5
+
+.SUFFIXES: .pd_irix5
+
+SGICFLAGS5 = -o32 -DPD -DUNIX -DIRIX -O2
+
+SGIINCLUDE = -I../../src
+
+.c.pd_irix5:
+ cc $(SGICFLAGS5) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -elf -shared -rdata_shared -o $*.pd_irix5 $*.o
+ rm $*.o
+
+# ----------------------- IRIX 6.x -----------------------
+
+pd_irix6: $(NAME).pd_irix6
+
+.SUFFIXES: .pd_irix6
+
+SGICFLAGS6 = -n32 -DPD -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \
+ -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \
+ -Ofast=ip32
+
+.c.pd_irix6:
+ cc $(SGICFLAGS6) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -n32 -IPA -shared -rdata_shared -o $*.pd_irix6 $*.o
+ rm $*.o
+
+# ----------------------- LINUX i386 -----------------------
+
+pd_linux: $(NAME).pd_linux
+
+.SUFFIXES: .pd_linux
+
+LINUXCFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer \
+ -Wall -W -Wshadow -Wstrict-prototypes -Werror \
+ -Wno-unused -Wno-parentheses -Wno-switch
+
+LINUXINCLUDE = -I../../src
+
+.c.pd_linux:
+ cc $(LINUXCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c
+ ld -export_dynamic -shared -o $*.pd_linux $*.o -lc -lm
+ strip --strip-unneeded $*.pd_linux
+ rm -f $*.o ../$*.pd_linux
+ ln -s $*/$*.pd_linux ..
+
+# ----------------------- Mac OSX -----------------------
+
+pd_darwin: $(NAME).pd_darwin
+
+.SUFFIXES: .pd_darwin
+
+DARWINCFLAGS = -DPD -O2 -Wall -W -Wshadow -Wstrict-prototypes \
+ -Wno-unused -Wno-parentheses -Wno-switch
+
+.c.pd_darwin:
+ cc $(DARWINCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c
+ cc -bundle -undefined suppress -flat_namespace -o $*.pd_darwin $*.o
+ rm -f $*.o ../$*.pd_darwin
+ ln -s $*/$*.pd_darwin ..
+
+# ----------------------------------------------------------
+
+clean:
+ rm -f *.o *.pd_* so_locations
diff --git a/pd/extra/pique/pique.c b/pd/extra/pique/pique.c
new file mode 100644
index 00000000..aaf80350
--- /dev/null
+++ b/pd/extra/pique/pique.c
@@ -0,0 +1,238 @@
+/* Copyright (c) 1999 Miller Puckette. The
+contents of this file are free for any use, but BOTH THE AUTHOR AND UCSD
+DISCLAIM ALL WARRANTIES related to it. Although not written in Java, this
+still should not be used to control any machinery containing a sharp blade or
+combustible materiel, or as part of any life support system or weapon. */
+
+#include "m_pd.h"
+#include <math.h>
+#include <stdio.h>
+#ifdef NT
+#pragma warning( disable : 4244 )
+#pragma warning( disable : 4305 )
+#endif
+
+static t_class *pique_class;
+
+typedef struct _pique
+{
+ t_object x_obj;
+ int x_n;
+ float x_errthresh;
+ float *x_freq;
+ float *x_amp;
+ float *x_ampre;
+ float *x_ampim;
+} t_pique;
+
+static void *pique_new(t_floatarg f)
+{
+ int n = f;
+ t_pique *x = (t_pique *)pd_new(pique_class);
+ if (n < 1) n = 100;
+ x->x_n = n;
+ x->x_errthresh = 0;
+ x->x_freq = t_getbytes(n * sizeof(*x->x_freq));
+ x->x_amp = t_getbytes(n * sizeof(*x->x_amp));
+ x->x_ampre = t_getbytes(n * sizeof(*x->x_ampre));
+ x->x_ampim = t_getbytes(n * sizeof(*x->x_ampim));
+ outlet_new(&x->x_obj, &s_list);
+ return (x);
+}
+
+static float hanning(float pidetune, float sinpidetune)
+{
+ float pi = 3.14159;
+ if (pidetune < 0.01 && pidetune > -0.01) return (1);
+ else if (pidetune > 3.14 && pidetune < 3.143) return (0.5);
+ else if (pidetune < -3.14 && pidetune > -3.143) return (0.5);
+ else return (sinpidetune/pidetune - 0.5 *
+ (sinpidetune/(pidetune+pi) + sinpidetune/(pidetune-pi)));
+}
+
+static float peakerror(float *fpreal, float *fpimag, float pidetune,
+ float norm, float peakreal, float peakimag)
+{
+ float sinpidetune = sin(pidetune);
+ float cospidetune = cos(pidetune);
+ float windowshould = hanning(pidetune, sinpidetune);
+ float realshould = windowshould * (
+ peakreal * cospidetune + peakimag * sinpidetune);
+ float imagshould = windowshould * (
+ peakimag * cospidetune - peakreal * sinpidetune);
+ float realgot = norm * (fpreal[0] - 0.5 * (fpreal[1] + fpreal[-1]));
+ float imaggot = norm * (fpimag[0] - 0.5 * (fpimag[1] + fpimag[-1]));
+ float realdev = realshould - realgot, imagdev = imagshould - imaggot;
+
+ /* post("real %f->%f; imag %f->%f", realshould, realgot,
+ imagshould, imaggot); */
+ return (realdev * realdev + imagdev * imagdev);
+}
+
+static void pique_doit(int npts, t_float *fpreal, t_float *fpimag,
+ int npeak, int *nfound, t_float *fpfreq, t_float *fpamp,
+ t_float *fpampre, t_float *fpampim, float errthresh)
+{
+ float srate = sys_getsr(); /* not sure how to get this correctly */
+ float oneovern = 1.0/ (float)npts;
+ float fperbin = srate * oneovern;
+ float pow1, pow2 = 0, pow3 = 0, pow4 = 0, pow5 = 0;
+ float re1, re2 = 0, re3 = *fpreal;
+ float im1, im2 = 0, im3 = 0, powthresh, relativeerror;
+ int count, peakcount = 0, n2 = (npts >> 1);
+ float *fp1, *fp2;
+ for (count = n2, fp1 = fpreal, fp2 = fpimag, powthresh = 0;
+ count--; fp1++, fp2++)
+ powthresh += (*fp1) * (*fp1) + (*fp2) * (*fp2) ;
+ powthresh *= 0.00001;
+ for (count = 1; count < n2; count++)
+ {
+ float windreal, windimag, pi = 3.14159;
+ float detune, pidetune, sinpidetune, cospidetune,
+ ampcorrect, freqout, ampout, ampoutreal, ampoutimag;
+ float rpeak, rpeaknext, rpeakprev;
+ float ipeak, ipeaknext, ipeakprev;
+ float errleft, errright;
+ fpreal++;
+ fpimag++;
+ re1 = re2;
+ re2 = re3;
+ re3 = *fpreal;
+ im1 = im2;
+ im2 = im3;
+ im3 = *fpimag;
+ if (count < 2) continue;
+ pow1 = pow2;
+ pow2 = pow3;
+ pow3 = pow4;
+ pow4 = pow5;
+ /* get Hanning-windowed spectrum by convolution */
+ windreal = re2 - 0.5 * (re1 + re3);
+ windimag = im2 - 0.5 * (im1 + im3);
+ pow5 = windreal * windreal + windimag * windimag;
+ /* if (count < 30) post("power %f", pow5); */
+ if (count < 5) continue;
+ /* check for a peak. The actual bin is count-3. */
+ if (pow3 <= pow2 || pow3 <= pow4 || pow3 <= pow1 || pow3 <= pow5
+ || pow3 < powthresh)
+ continue;
+ /* go back for the raw FFT values around the peak. */
+ rpeak = fpreal[-3];
+ rpeaknext = fpreal[-2];
+ rpeakprev = fpreal[-4];
+ ipeak = fpimag[-3];
+ ipeaknext = fpimag[-2];
+ ipeakprev = fpimag[-4];
+ /* recalculate Hanning-windowed spectrum by convolution */
+ windreal = rpeak - 0.5 * (rpeaknext + rpeakprev);
+ windimag = ipeak - 0.5 * (ipeaknext + ipeakprev);
+
+ detune = ((rpeakprev - rpeaknext) *
+ (2.0 * rpeak - rpeakprev - rpeaknext) +
+ (ipeakprev - ipeaknext) *
+ (2.0 * ipeak - ipeakprev - ipeaknext)) /
+ (4.0 * pow3);
+ /* if (count < 30) post("detune %f", detune); */
+ if (detune > 0.7 || detune < -0.7) continue;
+ /* the frequency is the sum of the bin frequency and detuning */
+ freqout = fperbin * ((float)(count-3) + detune);
+ pidetune = pi * detune;
+ sinpidetune = sin(pidetune);
+ cospidetune = cos(pidetune);
+ ampcorrect = 1.0 / hanning(pidetune, sinpidetune);
+ /* Multiply by 2 to get real-sinusoid peak amplitude
+ and divide by N to normalize FFT */
+ ampcorrect *= 2. * oneovern;
+ /* amplitude is peak height, corrected for Hanning window shape */
+
+ ampout = ampcorrect * sqrt(pow3);
+ ampoutreal = ampcorrect *
+ (windreal * cospidetune - windimag * sinpidetune);
+ ampoutimag = ampcorrect *
+ (windreal * sinpidetune + windimag * cospidetune);
+ if (errthresh > 0)
+ {
+ /* post("peak %f %f", freqout, ampout); */
+ errleft = peakerror(fpreal-4, fpimag-4, pidetune+pi,
+ 2. * oneovern, ampoutreal, ampoutimag);
+ errright = peakerror(fpreal-2, fpimag-2, pidetune-pi,
+ 2. * oneovern, ampoutreal, ampoutimag);
+ relativeerror = (errleft + errright)/(ampout * ampout);
+ if (relativeerror > errthresh) continue;
+ }
+ /* post("power %f, error %f, relative %f",
+ pow3, errleft + errright, relativeerror); */
+ *fpfreq++ = freqout;
+ *fpamp++ = ampout;
+ *fpampre++ = ampoutreal;
+ *fpampim++ = ampoutimag;
+ if (++peakcount == npeak) break;
+ }
+ *nfound = peakcount;
+}
+
+static void pique_list(t_pique *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int npts = atom_getintarg(0, argc, argv);
+ t_symbol *symreal = atom_getsymbolarg(1, argc, argv);
+ t_symbol *symimag = atom_getsymbolarg(2, argc, argv);
+ int npeak = atom_getintarg(3, argc, argv);
+ int n;
+ t_garray *a;
+ t_float *fpreal, *fpimag;
+ if (npts < 8 || npeak < 1) error("pique: bad npoints or npeak");
+ if (npeak > x->x_n) npeak = x->x_n;
+ if (!(a = (t_garray *)pd_findbyclass(symreal, garray_class)) ||
+ !garray_getfloatarray(a, &n, &fpreal) ||
+ n < npts)
+ error("%s: missing or bad array", symreal->s_name);
+ else if (!(a = (t_garray *)pd_findbyclass(symimag, garray_class)) ||
+ !garray_getfloatarray(a, &n, &fpimag) ||
+ n < npts)
+ error("%s: missing or bad array", symimag->s_name);
+ else
+ {
+ int nfound, i;
+ float *fpfreq = x->x_freq;
+ float *fpamp = x->x_amp;
+ float *fpampre = x->x_ampre;
+ float *fpampim = x->x_ampim;
+ pique_doit(npts, fpreal, fpimag, npeak,
+ &nfound, fpfreq, fpamp, fpampre, fpampim, x->x_errthresh);
+ for (i = 0; i < nfound; i++, fpamp++, fpfreq++, fpampre++, fpampim++)
+ {
+ t_atom at[5];
+ SETFLOAT(at, (float)i);
+ SETFLOAT(at+1, *fpfreq);
+ SETFLOAT(at+2, *fpamp);
+ SETFLOAT(at+3, *fpampre);
+ SETFLOAT(at+4, *fpampim);
+ outlet_list(x->x_obj.ob_outlet, &s_list, 5, at);
+ }
+ }
+}
+
+static void pique_errthresh(t_pique *x, t_floatarg f)
+{
+ x->x_errthresh = f;
+}
+
+static void pique_free(t_pique *x)
+{
+ int n = x->x_n;
+ t_freebytes(x->x_freq, n * sizeof(*x->x_freq));
+ t_freebytes(x->x_amp, n * sizeof(*x->x_amp));
+ t_freebytes(x->x_ampre, n * sizeof(*x->x_ampre));
+ t_freebytes(x->x_ampim, n * sizeof(*x->x_ampim));
+}
+
+void pique_setup(void)
+{
+ pique_class = class_new(gensym("pique"), (t_newmethod)pique_new,
+ (t_method)pique_free, sizeof(t_pique),0, A_DEFFLOAT, 0);
+ class_addlist(pique_class, pique_list);
+ class_addmethod(pique_class, (t_method)pique_errthresh,
+ gensym("errthresh"), A_FLOAT, 0);
+ post("pique 0.1 for PD version 23");
+}
+
diff --git a/pd/extra/rev1-final.pd b/pd/extra/rev1-final.pd
new file mode 100644
index 00000000..0ed091c4
--- /dev/null
+++ b/pd/extra/rev1-final.pd
@@ -0,0 +1,106 @@
+#N canvas 133 53 729 468 10;
+#X obj 72 240 inlet~;
+#X obj 347 28 loadbang;
+#X obj 90 376 +~;
+#X obj 52 408 +~;
+#X obj 52 437 outlet~;
+#X obj 409 96 pow;
+#X obj 372 118 *;
+#X floatatom 372 159;
+#X obj 82 264 *~ 0;
+#X obj 177 175 pow;
+#X text 386 140 delay \, msec;
+#X floatatom 201 237;
+#X obj 190 150 * 0.001;
+#X text 206 220 gain for this stage;
+#X obj 103 327 *~ 0;
+#X obj 201 202 *;
+#X text 25 13 Allpass filter for mono reverberator. Arg 1 = delay name \, arg2 = stage number \, arg 3 = delay time;
+#X obj 373 76 8;
+#X obj 409 75 1.79;
+#X obj 114 175 0.7;
+#X obj 363 50 t b b b b;
+#X obj 177 108 0;
+#X obj 372 207 abs;
+#X obj 372 229 moses 0.01;
+#X obj 443 229 print wrong-delay-time;
+#X obj 233 391 inlet~;
+#X obj 219 419 +~;
+#X obj 219 443 outlet~;
+#X text 74 83 decay after;
+#X text 85 98 1 second;
+#X obj 83 119 r \$1-decay;
+#X obj 327 262 r \$1-clear;
+#X obj 240 298 0;
+#X obj 327 281 t b;
+#X obj 327 344 delay;
+#X obj 327 322 + 5;
+#X obj 158 279 delread~ \$2 \$4;
+#X obj 90 397 delwrite~ \$2 \$4;
+#X obj 327 302 f \$4;
+#X obj 371 184 - \$4;
+#X obj 446 75 float \$3;
+#X obj 241 318 1;
+#X obj 158 300 *~ 1;
+#X obj 52 298 *~ 0;
+#X obj 10 209 t b f f;
+#X obj 28 233 *;
+#X obj 10 256 -;
+#X obj 41 258 * -1;
+#X obj 11 297 *~ 0;
+#X obj 214 181 sqrt;
+#X connect 0 0 8 0;
+#X connect 1 0 20 0;
+#X connect 2 0 37 0;
+#X connect 3 0 4 0;
+#X connect 3 0 26 0;
+#X connect 5 0 6 1;
+#X connect 6 0 7 0;
+#X connect 6 0 12 0;
+#X connect 7 0 39 0;
+#X connect 8 0 43 0;
+#X connect 8 0 48 0;
+#X connect 9 0 15 0;
+#X connect 9 0 14 1;
+#X connect 12 0 9 1;
+#X connect 14 0 2 1;
+#X connect 14 0 3 1;
+#X connect 15 0 11 0;
+#X connect 15 0 8 1;
+#X connect 17 0 6 0;
+#X connect 18 0 5 0;
+#X connect 18 0 49 0;
+#X connect 19 0 44 0;
+#X connect 19 0 47 0;
+#X connect 20 0 21 0;
+#X connect 20 0 19 0;
+#X connect 20 1 17 0;
+#X connect 20 2 18 0;
+#X connect 20 3 40 0;
+#X connect 21 0 9 0;
+#X connect 22 0 23 0;
+#X connect 23 1 24 0;
+#X connect 25 0 26 1;
+#X connect 26 0 27 0;
+#X connect 30 0 9 0;
+#X connect 31 0 33 0;
+#X connect 32 0 42 1;
+#X connect 33 0 32 0;
+#X connect 33 0 38 0;
+#X connect 34 0 41 0;
+#X connect 35 0 34 0;
+#X connect 36 0 42 0;
+#X connect 38 0 35 0;
+#X connect 39 0 22 0;
+#X connect 40 0 5 1;
+#X connect 41 0 42 1;
+#X connect 42 0 14 0;
+#X connect 43 0 3 0;
+#X connect 44 0 46 0;
+#X connect 44 1 45 0;
+#X connect 44 2 45 1;
+#X connect 45 0 46 1;
+#X connect 46 0 48 1;
+#X connect 47 0 43 1;
+#X connect 48 0 2 0;
+#X connect 49 0 15 1;
diff --git a/pd/extra/rev1-stage.pd b/pd/extra/rev1-stage.pd
new file mode 100644
index 00000000..c1ee6574
--- /dev/null
+++ b/pd/extra/rev1-stage.pd
@@ -0,0 +1,99 @@
+#N canvas 86 133 729 452 10;
+#X obj 27 238 inlet~;
+#X obj 347 28 loadbang;
+#X obj 171 281 * -1;
+#X obj 36 353 +~;
+#X obj 69 395 +~;
+#X obj 69 424 outlet~;
+#X obj 409 96 pow;
+#X obj 372 118 *;
+#X floatatom 372 159;
+#X obj 37 262 *~ 0;
+#X obj 177 175 pow;
+#X text 408 162 delay \, msec;
+#X floatatom 177 238;
+#X obj 190 150 * 0.001;
+#X text 182 221 gain for this stage;
+#X obj 49 332 *~ 0;
+#X obj 47 375 *~ 0;
+#X obj 177 203 *;
+#X floatatom 409 119;
+#X text 25 13 Allpass filter for mono reverberator. Arg 1 = delay name \, arg2 = stage number \, arg 3 = delay time;
+#X obj 373 76 8;
+#X obj 409 75 1.79;
+#X obj 68 185 0.7;
+#X obj 363 50 t b b b b;
+#X obj 177 108 0;
+#X obj 372 207 abs;
+#X obj 372 229 moses 0.01;
+#X obj 443 229 print wrong-delay-time;
+#X obj 233 391 inlet~;
+#X obj 219 419 +~;
+#X obj 219 443 outlet~;
+#X text 74 83 decay after;
+#X text 85 98 1 second;
+#X obj 83 119 r \$1-decay;
+#X obj 259 256 r \$1-clear;
+#X obj 206 301 0;
+#X obj 259 275 t b;
+#X obj 259 338 delay;
+#X obj 259 316 + 5;
+#X obj 79 280 delread~ \$2 \$4;
+#X obj 36 447 delwrite~ \$2 \$4;
+#X obj 259 296 f \$4;
+#X obj 371 184 - \$4;
+#X obj 446 75 float \$3;
+#X obj 207 321 1;
+#X obj 79 301 *~ 1;
+#X obj 207 188 sqrt;
+#X floatatom 35 148;
+#X connect 0 0 9 0;
+#X connect 1 0 23 0;
+#X connect 2 0 16 1;
+#X connect 3 0 16 0;
+#X connect 3 0 40 0;
+#X connect 4 0 5 0;
+#X connect 4 0 29 0;
+#X connect 6 0 7 1;
+#X connect 6 0 18 0;
+#X connect 7 0 8 0;
+#X connect 7 0 13 0;
+#X connect 8 0 42 0;
+#X connect 9 0 3 0;
+#X connect 10 0 17 0;
+#X connect 13 0 10 1;
+#X connect 15 0 3 1;
+#X connect 16 0 4 0;
+#X connect 17 0 12 0;
+#X connect 17 0 9 1;
+#X connect 20 0 7 0;
+#X connect 21 0 6 0;
+#X connect 21 0 46 0;
+#X connect 22 0 2 0;
+#X connect 22 0 15 1;
+#X connect 23 0 24 0;
+#X connect 23 0 22 0;
+#X connect 23 1 20 0;
+#X connect 23 2 21 0;
+#X connect 23 3 43 0;
+#X connect 24 0 10 0;
+#X connect 25 0 26 0;
+#X connect 26 1 27 0;
+#X connect 28 0 29 1;
+#X connect 29 0 30 0;
+#X connect 33 0 10 0;
+#X connect 33 0 47 0;
+#X connect 34 0 36 0;
+#X connect 35 0 45 1;
+#X connect 36 0 35 0;
+#X connect 36 0 41 0;
+#X connect 37 0 44 0;
+#X connect 38 0 37 0;
+#X connect 39 0 45 0;
+#X connect 41 0 38 0;
+#X connect 42 0 25 0;
+#X connect 43 0 6 1;
+#X connect 44 0 45 1;
+#X connect 45 0 15 0;
+#X connect 45 0 4 1;
+#X connect 46 0 17 1;
diff --git a/pd/extra/rev1~.pd b/pd/extra/rev1~.pd
new file mode 100644
index 00000000..83fd6d20
--- /dev/null
+++ b/pd/extra/rev1~.pd
@@ -0,0 +1,64 @@
+#N canvas 66 116 512 312 10;
+#X obj 345 154 dbtorms;
+#X obj 316 120 min 100;
+#X obj 316 100 inlet;
+#X obj 45 16 inlet~;
+#X obj 254 298 outlet~;
+#X obj 432 106 inlet;
+#X obj 432 130 t b;
+#X obj 269 145 t b f;
+#X obj 281 185 -;
+#X obj 282 254 line~;
+#X obj 282 233 pack 0 100;
+#X obj 269 166 105;
+#X obj 256 276 *~;
+#X obj 282 210 * 0.01;
+#X text 282 65 reverb decay speed;
+#X text 278 79 (dB left after 1 sec);
+#X text 425 84 bang to clear;
+#X obj 44 41 rev1-stage \$0 \$0-del1 0 8;
+#X obj 44 64 rev1-stage \$0 \$0-del2 1 14.32;
+#X obj 44 87 rev1-stage \$0 \$0-del3 2 25.6328;
+#X obj 44 110 rev1-stage \$0 \$0-del4 3 45.8827;
+#X obj 44 133 rev1-stage \$0 \$0-del5 4 82.1301;
+#X obj 44 156 rev1-stage \$0 \$0-del6 5 147.013;
+#X obj 44 179 rev1-stage \$0 \$0-del7 6 263.153;
+#X obj 44 202 rev1-stage \$0 \$0-del8 7 471.044;
+#X obj 44 225 rev1-stage \$0 \$0-del9 8 843.168;
+#X obj 44 248 rev1-final \$0 \$0-del10 9 1509.27;
+#X obj 346 177 s \$0-decay;
+#X obj 432 153 s \$0-clear;
+#X connect 0 0 27 0;
+#X connect 1 0 0 0;
+#X connect 1 0 7 0;
+#X connect 2 0 1 0;
+#X connect 3 0 17 0;
+#X connect 5 0 6 0;
+#X connect 6 0 28 0;
+#X connect 7 0 11 0;
+#X connect 7 1 8 1;
+#X connect 8 0 13 0;
+#X connect 9 0 12 1;
+#X connect 10 0 9 0;
+#X connect 11 0 8 0;
+#X connect 12 0 4 0;
+#X connect 13 0 10 0;
+#X connect 17 0 18 0;
+#X connect 17 1 18 1;
+#X connect 18 0 19 0;
+#X connect 18 1 19 1;
+#X connect 19 0 20 0;
+#X connect 19 1 20 1;
+#X connect 20 0 21 0;
+#X connect 20 1 21 1;
+#X connect 21 0 22 0;
+#X connect 21 1 22 1;
+#X connect 22 0 23 0;
+#X connect 22 1 23 1;
+#X connect 23 0 24 0;
+#X connect 23 1 24 1;
+#X connect 24 0 25 0;
+#X connect 24 1 25 1;
+#X connect 25 0 26 0;
+#X connect 25 1 26 1;
+#X connect 26 1 12 0;
diff --git a/pd/man/pd.1 b/pd/man/pd.1
new file mode 100644
index 00000000..58755be6
--- /dev/null
+++ b/pd/man/pd.1
@@ -0,0 +1,25 @@
+.TH pd 1 "1996 Mar 20" GNU
+.SH NAME
+pd \- pure data
+.SH DESCRIPTION
+Pd is a graphical programming environment for real-time audio synthesis
+and related applications. It sports a rich set of real-time control
+and I/O features.
+.PP
+To get a list of allowable arguments to pd, type
+.IP
+.B pd -help
+.PP
+and for more documentation either start pd and get help, or consult
+.PP
+.B http://www.crca.ucsd.edu/~msp/Pd_documentation/index.htm
+.PP
+or seek a copy on your own machine, perhaps in
+.PP
+.B file:/usr/local/bin/pd/doc/1.manual/index.htm
+.PP
+or
+.PP
+.B file:/usr/bin/pd/doc/1.manual/index.htm
+.SH SEE ALSO
+pdsend(1), pdreceive(1)
diff --git a/pd/man/pdreceive.1 b/pd/man/pdreceive.1
new file mode 100644
index 00000000..fc5b5b02
--- /dev/null
+++ b/pd/man/pdreceive.1
@@ -0,0 +1,26 @@
+.TH pdreceive 1 "1996 Mar 20" GNU
+.SH NAME
+pdreceive \- receive messages from pd on this or a remote machine
+.SH SYNOPSIS
+.B pdreceive
+\fIport-number\fR [udp|tcp]
+.SH DESCRIPTION
+Pdreceive opens a socket (with the specified port number) and
+waits for messages to arrive from one or more instances of pd(1). Each
+message received is printed to the standard output with a trailing semicolon.
+The protocol used is easy to implement and is called FUDI.
+.PP
+The \fIport number\fR should agree with the port number of a "netsend" object
+within pd. The protocol is "tcp" by default; this does a handshake
+to
+guarantee that all messages arrive complete and in their correct order; if you
+are sending messages locally or point-to-point you can often get away with
+the faster udp protocol instead.
+.PP
+You can also use this to get messages from a Max "pdnetsend" object or even
+just a
+"pdsend" in another shell. If you're writing another program you're welcome
+to just grab the sources for pdsend/pdreceive and adapt them to your own ends;
+they're part of the Pd distribution.
+.SH SEE ALSO
+pd(1), pdsend(1)
diff --git a/pd/man/pdsend.1 b/pd/man/pdsend.1
new file mode 100644
index 00000000..5491c745
--- /dev/null
+++ b/pd/man/pdsend.1
@@ -0,0 +1,26 @@
+.TH pdsend 1 "1996 Mar 20" GNU
+.SH NAME
+pdsend \- send messages to pd on this or a remote machine
+.SH SYNOPSIS
+.B pdsend
+\fIport-number\fR [\fIhostname\fR] [udp|tcp]
+.SH DESCRIPTION
+Pdsend sends messages to pd(1), via a socket conection, from pdsend's
+standard input. This input can be any stream of Pd messages separated by
+semicolons. This is probably the easiest way to control pd from another
+application. The protocol used is easy to implement and is called FUDI.
+.PP
+The \fIport number\fR should agree with the port number of a "netreceive" object
+within pd. The \fIhostname\fR is "localhost" by default and can be a domain
+name or an IP address. The protocol is "tcp" by default; this does a handshake
+to
+guarantee that all messages arrive complete and in their correct order; if you
+are sending messages locally or point-to-point you can often get away with
+the faster udp protocol instead.
+.PP
+You can also use this to talk to a Max "pdnetreceive" object or even just a
+"pdreceive" in another shell. If you're writing another program you're welcome
+to just grab the sources for pdsend/pdreceive and adapt them to your own ends;
+they're part of the Pd distribution.
+.SH SEE ALSO
+pd(1), pdreceive(1)
diff --git a/pd/obj/nada b/pd/obj/nada
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pd/obj/nada
diff --git a/pd/portaudio/LICENSE.txt b/pd/portaudio/LICENSE.txt
new file mode 100644
index 00000000..105da3f7
--- /dev/null
+++ b/pd/portaudio/LICENSE.txt
@@ -0,0 +1,65 @@
+Portable header file to contain:
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.audiomulch.com/portaudio/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+
+Implementation files to contain:
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest version at: http://www.audiomulch.com/portaudio/
+ * <platform> Implementation
+ * Copyright (c) 1999-2000 <author(s)>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */ \ No newline at end of file
diff --git a/pd/portaudio/MSP-README.txt b/pd/portaudio/MSP-README.txt
new file mode 100644
index 00000000..e29de09f
--- /dev/null
+++ b/pd/portaudio/MSP-README.txt
@@ -0,0 +1,3 @@
+This is not the full distribution, just what's needed to get Pd running
+on Mac OSX and ASIO (Windows.) I changed some code in pablio.c as marked.
+-MSP
diff --git a/pd/portaudio/README.txt b/pd/portaudio/README.txt
new file mode 100644
index 00000000..d1e5d7d6
--- /dev/null
+++ b/pd/portaudio/README.txt
@@ -0,0 +1,81 @@
+README for PortAudio
+Implementations for PC DirectSound and Mac SoundManager
+
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com//
+ *
+ * Copyright (c) 1999-2000 Phil Burk and Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+PortAudio is a portable audio I/O library designed for cross-platform
+support of audio. It uses a callback mechanism to request audio processing.
+Audio can be generated in various formats, including 32 bit floating point,
+and will be converted to the native format internally.
+
+Documentation:
+ See "pa_common/portaudio.h" for API spec.
+ See docs folder for a tutorial.
+ Also see http://www.portaudio.com/docs/
+ And see "pa_tests/patest_saw.c" for an example.
+
+For information on compiling programs with PortAudio, please see the
+tutorial at:
+
+ http://www.portaudio.com/docs/pa_tutorial.html
+
+Important Files and Folders:
+ pa_common/ = platform independant code
+ pa_common/portaudio.h = header file for PortAudio API. Specifies API.
+ pa_common/pa_lib.c = host independant code for all implementations.
+
+ pablio = simple blocking read/write interface
+
+Platform Implementations
+ pa_asio = ASIO for Windows and Macintosh
+ pa_beos = BeOS
+ pa_mac = Macintosh Sound Manager for OS 8,9 and Carbon
+ pa_mac_core = Macintosh Core Audio for OS X
+ pa_sgi = Silicon Graphics AL
+ pa_unix_oss = OSS implementation for various Unixes
+ pa_win_ds = Windows Direct Sound
+ pa_win_wmme = Windows MME (most widely supported)
+
+Test Programs
+ pa_tests/pa_fuzz.c = guitar fuzz box
+ pa_tests/pa_devs.c = print a list of available devices
+ pa_tests/pa_minlat.c = determine minimum latency for your machine
+ pa_tests/paqa_devs.c = self test that opens all devices
+ pa_tests/paqa_errs.c = test error detection and reporting
+ pa_tests/patest_clip.c = hear a sine wave clipped and unclipped
+ pa_tests/patest_dither.c = hear effects of dithering (extremely subtle)
+ pa_tests/patest_pink.c = fun with pink noise
+ pa_tests/patest_record.c = record and playback some audio
+ pa_tests/patest_maxsines.c = how many sine waves can we play? Tests Pa_GetCPULoad().
+ pa_tests/patest_sine.c = output a sine wave in a simple PA app
+ pa_tests/patest_sync.c = test syncronization of audio and video
+ pa_tests/patest_wire.c = pass input to output, wire simulator
diff --git a/pd/portaudio/pa_asio/pa_asio.cpp b/pd/portaudio/pa_asio/pa_asio.cpp
new file mode 100644
index 00000000..baed8ed4
--- /dev/null
+++ b/pd/portaudio/pa_asio/pa_asio.cpp
@@ -0,0 +1,2998 @@
+/*
+ * $Id: pa_asio.cpp,v 1.7 2002/04/30 12:33:04 stephane Exp $
+ * Portable Audio I/O Library for ASIO Drivers
+ *
+ * Author: Stephane Letz
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2000-2001 Stephane Letz, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* Modification History
+
+ 08-03-01 First version : Stephane Letz
+ 08-06-01 Tweaks for PC, use C++, buffer allocation, Float32 to Int32 conversion : Phil Burk
+ 08-20-01 More conversion, PA_StreamTime, Pa_GetHostError : Stephane Letz
+ 08-21-01 PaUInt8 bug correction, implementation of ASIOSTFloat32LSB and ASIOSTFloat32MSB native formats : Stephane Letz
+ 08-24-01 MAX_INT32_FP hack, another Uint8 fix : Stephane and Phil
+ 08-27-01 Implementation of hostBufferSize < userBufferSize case, better management of the ouput buffer when
+ the stream is stopped : Stephane Letz
+ 08-28-01 Check the stream pointer for null in bufferSwitchTimeInfo, correct bug in bufferSwitchTimeInfo when
+ the stream is stopped : Stephane Letz
+ 10-12-01 Correct the PaHost_CalcNumHostBuffers function: computes FramesPerHostBuffer to be the lowest that
+ respect requested FramesPerUserBuffer and userBuffersPerHostBuffer : Stephane Letz
+ 10-26-01 Management of hostBufferSize and userBufferSize of any size : Stephane Letz
+ 10-27-01 Improve calculus of hostBufferSize to be multiple or divisor of userBufferSize if possible : Stephane and Phil
+ 10-29-01 Change MAX_INT32_FP to (2147483520.0f) to prevent roundup to 0x80000000 : Phil Burk
+ 10-31-01 Clear the ouput buffer and user buffers in PaHost_StartOutput, correct bug in GetFirstMultiple : Stephane Letz
+ 11-06-01 Rename functions : Stephane Letz
+ 11-08-01 New Pa_ASIO_Adaptor_Init function to init Callback adpatation variables, cleanup of Pa_ASIO_Callback_Input: Stephane Letz
+ 11-29-01 Break apart device loading to debug random failure in Pa_ASIO_QueryDeviceInfo ; Phil Burk
+ 01-03-02 Desallocate all resources in PaHost_Term for cases where Pa_CloseStream is not called properly : Stephane Letz
+ 02-01-02 Cleanup, test of multiple-stream opening : Stephane Letz
+ 19-02-02 New Pa_ASIO_loadDriver that calls CoInitialize on each thread on Windows : Stephane Letz
+ 09-04-02 Correct error code management in PaHost_Term, removes various compiler warning : Stephane Letz
+ 12-04-02 Add Mac includes for <Devices.h> and <Timer.h> : Phil Burk
+ 13-04-02 Removes another compiler warning : Stephane Letz
+ 30-04-02 Pa_ASIO_QueryDeviceInfo bug correction, memory allocation checking, better error handling : D Viens, P Burk, S Letz
+
+ TO DO :
+
+ - Check Pa_StopSteam and Pa_AbortStream
+ - Optimization for Input only or Ouput only (really necessary ??)
+*/
+
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+#include "asiosys.h"
+#include "asio.h"
+#include "asiodrivers.h"
+
+
+#if MAC
+#include <Devices.h>
+#include <Timer.h>
+#include <Math64.h>
+#else
+#include <math.h>
+#include <windows.h>
+#include <mmsystem.h>
+#endif
+
+enum {
+ // number of input and outputs supported by the host application
+ // you can change these to higher or lower values
+ kMaxInputChannels = 32,
+ kMaxOutputChannels = 32
+};
+
+/* ASIO specific device information. */
+typedef struct internalPortAudioDevice
+{
+ PaDeviceInfo pad_Info;
+} internalPortAudioDevice;
+
+
+/* ASIO driver internal data storage */
+typedef struct PaHostSoundControl
+{
+ // ASIOInit()
+ ASIODriverInfo pahsc_driverInfo;
+
+ // ASIOGetChannels()
+ int32 pahsc_NumInputChannels;
+ int32 pahsc_NumOutputChannels;
+
+ // ASIOGetBufferSize() - sizes in frames per buffer
+ int32 pahsc_minSize;
+ int32 pahsc_maxSize;
+ int32 pahsc_preferredSize;
+ int32 pahsc_granularity;
+
+ // ASIOGetSampleRate()
+ ASIOSampleRate pahsc_sampleRate;
+
+ // ASIOOutputReady()
+ bool pahsc_postOutput;
+
+ // ASIOGetLatencies ()
+ int32 pahsc_inputLatency;
+ int32 pahsc_outputLatency;
+
+ // ASIOCreateBuffers ()
+ ASIOBufferInfo bufferInfos[kMaxInputChannels + kMaxOutputChannels]; // buffer info's
+
+ // ASIOGetChannelInfo()
+ ASIOChannelInfo pahsc_channelInfos[kMaxInputChannels + kMaxOutputChannels]; // channel info's
+ // The above two arrays share the same indexing, as the data in them are linked together
+
+ // Information from ASIOGetSamplePosition()
+ // data is converted to double floats for easier use, however 64 bit integer can be used, too
+ double nanoSeconds;
+ double samples;
+ double tcSamples; // time code samples
+
+ // bufferSwitchTimeInfo()
+ ASIOTime tInfo; // time info state
+ unsigned long sysRefTime; // system reference time, when bufferSwitch() was called
+
+ // Signal the end of processing in this example
+ bool stopped;
+
+ ASIOCallbacks pahsc_asioCallbacks;
+
+
+ int32 pahsc_userInputBufferFrameOffset; // Position in Input user buffer
+ int32 pahsc_userOutputBufferFrameOffset; // Position in Output user buffer
+ int32 pahsc_hostOutputBufferFrameOffset; // Position in Output ASIO buffer
+
+ int32 past_FramesPerHostBuffer; // Number of frames in ASIO buffer
+
+ int32 pahsc_InputBufferOffset; // Number of null frames for input buffer alignement
+ int32 pahsc_OutputBufferOffset; // Number of null frames for ouput buffer alignement
+
+#if MAC
+ UInt64 pahsc_EntryCount;
+ UInt64 pahsc_LastExitCount;
+#elif WINDOWS
+ LARGE_INTEGER pahsc_EntryCount;
+ LARGE_INTEGER pahsc_LastExitCount;
+#endif
+
+ PaTimestamp pahsc_NumFramesDone;
+
+ internalPortAudioStream *past;
+
+} PaHostSoundControl;
+
+
+//----------------------------------------------------------
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) /**/
+
+/* We are trying to be compatible with CARBON but this has not been thoroughly tested. */
+#define CARBON_COMPATIBLE (0)
+#define PA_MAX_DEVICE_INFO (32)
+
+#define MIN_INT8 (-0x80)
+#define MAX_INT8 (0x7F)
+
+#define MIN_INT8_FP ((float)-0x80)
+#define MAX_INT8_FP ((float)0x7F)
+
+#define MIN_INT16_FP ((float)-0x8000)
+#define MAX_INT16_FP ((float)0x7FFF)
+
+#define MIN_INT16 (-0x8000)
+#define MAX_INT16 (0x7FFF)
+
+#define MAX_INT32_FP (2147483520.0f) /* 0x0x7FFFFF80 - seems safe */
+
+/************************************************************************************/
+/****************** Data ************************************************************/
+/************************************************************************************/
+static int sNumDevices = 0;
+static internalPortAudioDevice sDevices[PA_MAX_DEVICE_INFO] = { 0 };
+static int32 sPaHostError = 0;
+static int sDefaultOutputDeviceID = 0;
+static int sDefaultInputDeviceID = 0;
+
+PaHostSoundControl asioDriverInfo = {0};
+
+#ifdef MAC
+static bool swap = true;
+#elif WINDOWS
+static bool swap = false;
+#endif
+
+// Prototypes
+static void bufferSwitch(long index, ASIOBool processNow);
+static ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow);
+static void sampleRateChanged(ASIOSampleRate sRate);
+static long asioMessages(long selector, long value, void* message, double* opt);
+static void Pa_StartUsageCalculation( internalPortAudioStream *past );
+static void Pa_EndUsageCalculation( internalPortAudioStream *past );
+
+static void Pa_ASIO_Convert_Inter_Input(
+ ASIOBufferInfo* nativeBuffer,
+ void* inputBuffer,
+ long NumInputChannels,
+ long NumOuputChannels,
+ long framePerBuffer,
+ long hostFrameOffset,
+ long userFrameOffset,
+ ASIOSampleType nativeFormat,
+ PaSampleFormat paFormat,
+ PaStreamFlags flags,
+ long index);
+
+static void Pa_ASIO_Convert_Inter_Output(
+ ASIOBufferInfo* nativeBuffer,
+ void* outputBuffer,
+ long NumInputChannels,
+ long NumOuputChannels,
+ long framePerBuffer,
+ long hostFrameOffset,
+ long userFrameOffset,
+ ASIOSampleType nativeFormat,
+ PaSampleFormat paFormat,
+ PaStreamFlags flags,
+ long index);
+
+static void Pa_ASIO_Clear_Output(ASIOBufferInfo* nativeBuffer,
+ ASIOSampleType nativeFormat,
+ long NumInputChannels,
+ long NumOuputChannels,
+ long index,
+ long hostFrameOffset,
+ long frames);
+
+static void Pa_ASIO_Callback_Input(long index);
+static void Pa_ASIO_Callback_Output(long index, long framePerBuffer);
+static void Pa_ASIO_Callback_End();
+static void Pa_ASIO_Clear_User_Buffers();
+
+// Some external references
+extern AsioDrivers* asioDrivers ;
+bool loadAsioDriver(char *name);
+unsigned long get_sys_reference_time();
+
+
+/************************************************************************************/
+/****************** Macro ************************************************************/
+/************************************************************************************/
+
+#define SwapLong(v) ((((v)>>24)&0xFF)|(((v)>>8)&0xFF00)|(((v)&0xFF00)<<8)|(((v)&0xFF)<<24)) ;
+#define SwapShort(v) ((((v)>>8)&0xFF)|(((v)&0xFF)<<8)) ;
+
+#define ClipShort(v) (((v)<MIN_INT16)?MIN_INT16:(((v)>MAX_INT16)?MAX_INT16:(v)))
+#define ClipChar(v) (((v)<MIN_INT8)?MIN_INT8:(((v)>MAX_INT8)?MAX_INT8:(v)))
+#define ClipFloat(v) (((v)<-1.0f)?-1.0f:(((v)>1.0f)?1.0f:(v)))
+
+#ifndef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef max
+#define max(a,b) ((a)>=(b)?(a):(b))
+#endif
+
+
+static bool Pa_ASIO_loadAsioDriver(char *name)
+{
+ #ifdef WINDOWS
+ CoInitialize(0);
+ #endif
+ return loadAsioDriver(name);
+}
+
+
+
+// Utilities for alignement buffer size computation
+static int PGCD (int a, int b) {return (b == 0) ? a : PGCD (b,a%b);}
+static int PPCM (int a, int b) {return (a*b) / PGCD (a,b);}
+
+// Takes the size of host buffer and user buffer : returns the number of frames needed for buffer alignement
+static int Pa_ASIO_CalcFrameShift (int M, int N)
+{
+ int res = 0;
+ for (int i = M; i < PPCM (M,N) ; i+=M) { res = max (res, i%N); }
+ return res;
+}
+
+// We have the following relation :
+// Pa_ASIO_CalcFrameShift (M,N) + M = Pa_ASIO_CalcFrameShift (N,M) + N
+
+/* ASIO sample type to PortAudio sample type conversion */
+static PaSampleFormat Pa_ASIO_Convert_SampleFormat(ASIOSampleType type)
+{
+ switch (type) {
+
+ case ASIOSTInt16MSB:
+ case ASIOSTInt16LSB:
+ case ASIOSTInt32MSB16:
+ case ASIOSTInt32LSB16:
+ return paInt16;
+
+ case ASIOSTFloat32MSB:
+ case ASIOSTFloat32LSB:
+ case ASIOSTFloat64MSB:
+ case ASIOSTFloat64LSB:
+ return paFloat32;
+
+ case ASIOSTInt32MSB:
+ case ASIOSTInt32LSB:
+ case ASIOSTInt32MSB18:
+ case ASIOSTInt32MSB20:
+ case ASIOSTInt32MSB24:
+ case ASIOSTInt32LSB18:
+ case ASIOSTInt32LSB20:
+ case ASIOSTInt32LSB24:
+ return paInt32;
+
+ case ASIOSTInt24MSB:
+ case ASIOSTInt24LSB:
+ return paInt24;
+
+ default:
+ return paCustomFormat;
+ }
+}
+
+/* Allocate ASIO buffers, initialise channels */
+static ASIOError Pa_ASIO_CreateBuffers (PaHostSoundControl *asioDriverInfo, long InputChannels,
+ long OutputChannels, long framesPerBuffer)
+{
+ ASIOError err;
+ int i;
+
+ ASIOBufferInfo *info = asioDriverInfo->bufferInfos;
+
+ // Check parameters
+ if ((InputChannels > kMaxInputChannels) || (OutputChannels > kMaxInputChannels)) return ASE_InvalidParameter;
+
+ for(i = 0; i < InputChannels; i++, info++){
+ info->isInput = ASIOTrue;
+ info->channelNum = i;
+ info->buffers[0] = info->buffers[1] = 0;
+ }
+
+ for(i = 0; i < OutputChannels; i++, info++){
+ info->isInput = ASIOFalse;
+ info->channelNum = i;
+ info->buffers[0] = info->buffers[1] = 0;
+ }
+
+ // Set up the asioCallback structure and create the ASIO data buffer
+ asioDriverInfo->pahsc_asioCallbacks.bufferSwitch = &bufferSwitch;
+ asioDriverInfo->pahsc_asioCallbacks.sampleRateDidChange = &sampleRateChanged;
+ asioDriverInfo->pahsc_asioCallbacks.asioMessage = &asioMessages;
+ asioDriverInfo->pahsc_asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;
+
+ DBUG(("PortAudio : ASIOCreateBuffers with size = %ld \n", framesPerBuffer));
+
+ err = ASIOCreateBuffers( asioDriverInfo->bufferInfos, InputChannels+OutputChannels,
+ framesPerBuffer, &asioDriverInfo->pahsc_asioCallbacks);
+ if (err != ASE_OK) return err;
+
+ // Initialise buffers
+ for (i = 0; i < InputChannels + OutputChannels; i++)
+ {
+ asioDriverInfo->pahsc_channelInfos[i].channel = asioDriverInfo->bufferInfos[i].channelNum;
+ asioDriverInfo->pahsc_channelInfos[i].isInput = asioDriverInfo->bufferInfos[i].isInput;
+ err = ASIOGetChannelInfo(&asioDriverInfo->pahsc_channelInfos[i]);
+ if (err != ASE_OK) break;
+ }
+
+ err = ASIOGetLatencies(&asioDriverInfo->pahsc_inputLatency, &asioDriverInfo->pahsc_outputLatency);
+
+ DBUG(("PortAudio : InputLatency = %ld latency = %ld msec \n",
+ asioDriverInfo->pahsc_inputLatency,
+ (long)((asioDriverInfo->pahsc_inputLatency*1000)/ asioDriverInfo->past->past_SampleRate)));
+ DBUG(("PortAudio : OuputLatency = %ld latency = %ld msec \n",
+ asioDriverInfo->pahsc_outputLatency,
+ (long)((asioDriverInfo->pahsc_outputLatency*1000)/ asioDriverInfo->past->past_SampleRate)));
+
+ return err;
+}
+
+
+/*
+ Query ASIO driver info :
+
+ First we get all available ASIO drivers located in the ASIO folder,
+ then try to load each one. For each loaded driver, get all needed informations.
+*/
+static PaError Pa_ASIO_QueryDeviceInfo( internalPortAudioDevice * ipad )
+{
+
+#define NUM_STANDARDSAMPLINGRATES 3 /* 11.025, 22.05, 44.1 */
+#define NUM_CUSTOMSAMPLINGRATES 9 /* must be the same number of elements as in the array below */
+#define MAX_NUMSAMPLINGRATES (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES)
+
+ ASIOSampleRate possibleSampleRates[]
+ = {8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0};
+
+ ASIOChannelInfo channelInfos;
+ long InputChannels,OutputChannels;
+ double *sampleRates;
+ char* names[PA_MAX_DEVICE_INFO] ;
+ PaDeviceInfo *dev;
+ int i;
+ int numDrivers;
+ ASIOError asioError;
+
+ /* Allocate names */
+ for (i = 0 ; i < PA_MAX_DEVICE_INFO ; i++) {
+ names[i] = (char*)PaHost_AllocateFastMemory(32);
+ /* check memory */
+ if(!names[i]) return paInsufficientMemory;
+ }
+
+ /* MUST BE CHECKED : to force fragments loading on Mac */
+ Pa_ASIO_loadAsioDriver("dummy");
+
+ /* Get names of all available ASIO drivers */
+ asioDrivers->getDriverNames(names,PA_MAX_DEVICE_INFO);
+
+ /* Check all available ASIO drivers */
+#if MAC
+ numDrivers = asioDrivers->getNumFragments();
+#elif WINDOWS
+ numDrivers = asioDrivers->asioGetNumDev();
+#endif
+ DBUG(("PaASIO_QueryDeviceInfo: numDrivers = %d\n", numDrivers ));
+
+ for (int driver = 0 ; driver < numDrivers ; driver++)
+ {
+
+ #if WINDOWS
+ asioDriverInfo.pahsc_driverInfo.asioVersion = 2; // FIXME - is this right? PLB
+ asioDriverInfo.pahsc_driverInfo.sysRef = GetDesktopWindow(); // FIXME - is this right? PLB
+ #endif
+
+ /* If the driver can be loaded : */
+ if ( !Pa_ASIO_loadAsioDriver(names[driver]) )
+ {
+ DBUG(("PaASIO_QueryDeviceInfo could not loadAsioDriver %s\n", names[driver]));
+ }
+ else if( (asioError = ASIOInit(&asioDriverInfo.pahsc_driverInfo)) != ASE_OK )
+ {
+ DBUG(("PaASIO_QueryDeviceInfo: ASIOInit returned %d for %s\n", asioError, names[driver]));
+ }
+ else if( (ASIOGetChannels(&InputChannels, &OutputChannels) != ASE_OK))
+ {
+ DBUG(("PaASIO_QueryDeviceInfo could not ASIOGetChannels for %s\n", names[driver]));
+ }
+ else
+ {
+ /* Gets the name */
+ dev = &(ipad[sNumDevices].pad_Info);
+ dev->name = names[driver];
+ names[driver] = 0;
+
+ /* Gets Input and Output channels number */
+ dev->maxInputChannels = InputChannels;
+ dev->maxOutputChannels = OutputChannels;
+
+ DBUG(("PaASIO_QueryDeviceInfo: InputChannels = %d\n", InputChannels ));
+ DBUG(("PaASIO_QueryDeviceInfo: OutputChannels = %d\n", OutputChannels ));
+
+ /* Make room in case device supports all rates. */
+ sampleRates = (double*)PaHost_AllocateFastMemory(MAX_NUMSAMPLINGRATES * sizeof(double));
+ /* check memory */
+ if (!sampleRates) {
+ ASIOExit();
+ return paInsufficientMemory;
+ }
+ dev->sampleRates = sampleRates;
+ dev->numSampleRates = 0;
+
+ /* Loop through the possible sampling rates and check each to see if the device supports it. */
+ for (int index = 0; index < MAX_NUMSAMPLINGRATES; index++) {
+ if (ASIOCanSampleRate(possibleSampleRates[index]) != ASE_NoClock) {
+ DBUG(("PortAudio : possible sample rate = %d\n", (long)possibleSampleRates[index]));
+ dev->numSampleRates += 1;
+ *sampleRates = possibleSampleRates[index];
+ sampleRates++;
+ }
+ }
+
+ /* We assume that all channels have the same SampleType, so check the first */
+ channelInfos.channel = 0;
+ channelInfos.isInput = 1;
+ ASIOGetChannelInfo(&channelInfos);
+
+ dev->nativeSampleFormats = Pa_ASIO_Convert_SampleFormat(channelInfos.type);
+
+ /* unload the driver */
+ ASIOExit();
+ sNumDevices++;
+ }
+ }
+
+ /* free only unused names */
+ for (i = 0 ; i < PA_MAX_DEVICE_INFO ; i++) if (names[i]) PaHost_FreeFastMemory(names[i],32);
+
+ return paNoError;
+}
+
+//----------------------------------------------------------------------------------
+// TAKEN FROM THE ASIO SDK:
+static void sampleRateChanged(ASIOSampleRate sRate)
+{
+ // do whatever you need to do if the sample rate changed
+ // usually this only happens during external sync.
+ // Audio processing is not stopped by the driver, actual sample rate
+ // might not have even changed, maybe only the sample rate status of an
+ // AES/EBU or S/PDIF digital input at the audio device.
+ // You might have to update time/sample related conversion routines, etc.
+}
+
+//----------------------------------------------------------------------------------
+// TAKEN FROM THE ASIO SDK:
+long asioMessages(long selector, long value, void* message, double* opt)
+{
+ // currently the parameters "value", "message" and "opt" are not used.
+ long ret = 0;
+ switch(selector)
+ {
+ case kAsioSelectorSupported:
+ if(value == kAsioResetRequest
+ || value == kAsioEngineVersion
+ || value == kAsioResyncRequest
+ || value == kAsioLatenciesChanged
+ // the following three were added for ASIO 2.0, you don't necessarily have to support them
+ || value == kAsioSupportsTimeInfo
+ || value == kAsioSupportsTimeCode
+ || value == kAsioSupportsInputMonitor)
+ ret = 1L;
+ break;
+
+ case kAsioBufferSizeChange:
+ //printf("kAsioBufferSizeChange \n");
+ break;
+
+ case kAsioResetRequest:
+ // defer the task and perform the reset of the driver during the next "safe" situation
+ // You cannot reset the driver right now, as this code is called from the driver.
+ // Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction
+ // Afterwards you initialize the driver again.
+ asioDriverInfo.stopped; // In this sample the processing will just stop
+ ret = 1L;
+ break;
+ case kAsioResyncRequest:
+ // This informs the application, that the driver encountered some non fatal data loss.
+ // It is used for synchronization purposes of different media.
+ // Added mainly to work around the Win16Mutex problems in Windows 95/98 with the
+ // Windows Multimedia system, which could loose data because the Mutex was hold too long
+ // by another thread.
+ // However a driver can issue it in other situations, too.
+ ret = 1L;
+ break;
+ case kAsioLatenciesChanged:
+ // This will inform the host application that the drivers were latencies changed.
+ // Beware, it this does not mean that the buffer sizes have changed!
+ // You might need to update internal delay data.
+ ret = 1L;
+ //printf("kAsioLatenciesChanged \n");
+ break;
+ case kAsioEngineVersion:
+ // return the supported ASIO version of the host application
+ // If a host applications does not implement this selector, ASIO 1.0 is assumed
+ // by the driver
+ ret = 2L;
+ break;
+ case kAsioSupportsTimeInfo:
+ // informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback
+ // is supported.
+ // For compatibility with ASIO 1.0 drivers the host application should always support
+ // the "old" bufferSwitch method, too.
+ ret = 1;
+ break;
+ case kAsioSupportsTimeCode:
+ // informs the driver wether application is interested in time code info.
+ // If an application does not need to know about time code, the driver has less work
+ // to do.
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+
+//----------------------------------------------------------------------------------
+// conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
+#if NATIVE_INT64
+ #define ASIO64toDouble(a) (a)
+#else
+ const double twoRaisedTo32 = 4294967296.;
+ #define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32)
+#endif
+
+
+static ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow)
+{
+ // the actual processing callback.
+ // Beware that this is normally in a seperate thread, hence be sure that you take care
+ // about thread synchronization. This is omitted here for simplicity.
+
+ // static processedSamples = 0;
+ int result = 0;
+
+ // store the timeInfo for later use
+ asioDriverInfo.tInfo = *timeInfo;
+
+ // get the time stamp of the buffer, not necessary if no
+ // synchronization to other media is required
+
+ if (timeInfo->timeInfo.flags & kSystemTimeValid)
+ asioDriverInfo.nanoSeconds = ASIO64toDouble(timeInfo->timeInfo.systemTime);
+ else
+ asioDriverInfo.nanoSeconds = 0;
+
+ if (timeInfo->timeInfo.flags & kSamplePositionValid)
+ asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
+ else
+ asioDriverInfo.samples = 0;
+
+ if (timeInfo->timeCode.flags & kTcValid)
+ asioDriverInfo.tcSamples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
+ else
+ asioDriverInfo.tcSamples = 0;
+
+ // get the system reference time
+ asioDriverInfo.sysRefTime = get_sys_reference_time();
+
+#if 0
+ // a few debug messages for the Windows device driver developer
+ // tells you the time when driver got its interrupt and the delay until the app receives
+ // the event notification.
+ static double last_samples = 0;
+ char tmp[128];
+ sprintf (tmp, "diff: %d / %d ms / %d ms / %d samples \n", asioDriverInfo.sysRefTime - (long)(asioDriverInfo.nanoSeconds / 1000000.0), asioDriverInfo.sysRefTime, (long)(asioDriverInfo.nanoSeconds / 1000000.0), (long)(asioDriverInfo.samples - last_samples));
+ OutputDebugString (tmp);
+ last_samples = asioDriverInfo.samples;
+#endif
+
+ // To avoid the callback accessing a desallocated stream
+ if( asioDriverInfo.past == NULL) return 0L;
+
+ // Keep sample position
+ asioDriverInfo.pahsc_NumFramesDone = timeInfo->timeInfo.samplePosition.lo;
+
+ /* Has a user callback returned '1' to indicate finished at the last ASIO callback? */
+ if( asioDriverInfo.past->past_StopSoon ) {
+
+ Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ asioDriverInfo.pahsc_NumInputChannels ,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ index,
+ 0,
+ asioDriverInfo.past_FramesPerHostBuffer);
+
+ asioDriverInfo.past->past_IsActive = 0;
+
+ // Finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place
+ if (asioDriverInfo.pahsc_postOutput) ASIOOutputReady();
+
+ }else {
+
+ /* CPU usage */
+ Pa_StartUsageCalculation(asioDriverInfo.past);
+
+ Pa_ASIO_Callback_Input(index);
+
+ // Finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place
+ if (asioDriverInfo.pahsc_postOutput) ASIOOutputReady();
+
+ Pa_ASIO_Callback_End();
+
+ /* CPU usage */
+ Pa_EndUsageCalculation(asioDriverInfo.past);
+ }
+
+ return 0L;
+}
+
+
+//----------------------------------------------------------------------------------
+void bufferSwitch(long index, ASIOBool processNow)
+{
+ // the actual processing callback.
+ // Beware that this is normally in a seperate thread, hence be sure that you take care
+ // about thread synchronization. This is omitted here for simplicity.
+
+ // as this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs to be created
+ // though it will only set the timeInfo.samplePosition and timeInfo.systemTime fields and the according flags
+
+ ASIOTime timeInfo;
+ memset (&timeInfo, 0, sizeof (timeInfo));
+
+ // get the time stamp of the buffer, not necessary if no
+ // synchronization to other media is required
+ if(ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK)
+ timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
+
+ // Call the real callback
+ bufferSwitchTimeInfo (&timeInfo, index, processNow);
+}
+
+//----------------------------------------------------------------------------------
+unsigned long get_sys_reference_time()
+{
+ // get the system reference time
+ #if WINDOWS
+ return timeGetTime();
+ #elif MAC
+ static const double twoRaisedTo32 = 4294967296.;
+ UnsignedWide ys;
+ Microseconds(&ys);
+ double r = ((double)ys.hi * twoRaisedTo32 + (double)ys.lo);
+ return (unsigned long)(r / 1000.);
+ #endif
+}
+
+
+/*************************************************************
+** Calculate 2 LSB dither signal with a triangular distribution.
+** Ranged properly for adding to a 32 bit integer prior to >>15.
+*/
+#define DITHER_BITS (15)
+#define DITHER_SCALE (1.0f / ((1<<DITHER_BITS)-1))
+inline static long Pa_TriangularDither( void )
+{
+ static unsigned long previous = 0;
+ static unsigned long randSeed1 = 22222;
+ static unsigned long randSeed2 = 5555555;
+ long current, highPass;
+/* Generate two random numbers. */
+ randSeed1 = (randSeed1 * 196314165) + 907633515;
+ randSeed2 = (randSeed2 * 196314165) + 907633515;
+/* Generate triangular distribution about 0. */
+ current = (((long)randSeed1)>>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS));
+ /* High pass filter to reduce audibility. */
+ highPass = current - previous;
+ previous = current;
+ return highPass;
+}
+
+// TO BE COMPLETED WITH ALL SUPPORTED PA SAMPLE TYPES
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Input_Int16_Float32 (ASIOBufferInfo* nativeBuffer, float *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ *userBufPtr = (1.0f / MAX_INT16_FP) * temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Input_Int32_Float32 (ASIOBufferInfo* nativeBuffer, float *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (1.0f / MAX_INT32_FP) * temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+static void Input_Float32_Float32 (ASIOBufferInfo* nativeBuffer, float *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+ unsigned long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ unsigned long *asioBufPtr = &((unsigned long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (float)temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Input_Int16_Int32 (ASIOBufferInfo* nativeBuffer, long *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ *userBufPtr = temp<<16;
+ userBufPtr += NumInputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Input_Int32_Int32 (ASIOBufferInfo* nativeBuffer, long *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+static void Input_Float32_Int32 (ASIOBufferInfo* nativeBuffer, long *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+ unsigned long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ unsigned long *asioBufPtr = &((unsigned long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (long)((float)temp * MAX_INT32_FP); // Is temp a value between -1.0 and 1.0 ??
+ userBufPtr += NumInputChannels;
+ }
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Input_Int16_Int16 (ASIOBufferInfo* nativeBuffer, short *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ *userBufPtr = (short)temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+}
+
+ //-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Input_Int32_Int16 (ASIOBufferInfo* nativeBuffer, short *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (short)(temp>>16);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ temp = (temp >> 1) + Pa_TriangularDither();
+ temp = temp >> 15;
+ temp = (short) ClipShort(temp);
+ *userBufPtr = (short)temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+static void Input_Float32_Int16 (ASIOBufferInfo* nativeBuffer, short *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap)
+{
+ unsigned long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ unsigned long *asioBufPtr = &((unsigned long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (short)((float)temp * MAX_INT16_FP); // Is temp a value between -1.0 and 1.0 ??
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ unsigned long *asioBufPtr = &((unsigned long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ float dither = Pa_TriangularDither()*DITHER_SCALE;
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ temp = (short)(((float)temp * MAX_INT16_FP) + dither);
+ temp = ClipShort(temp);
+ *userBufPtr = (short)temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Input_Int16_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ *userBufPtr = (char)(temp>>8);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ temp += Pa_TriangularDither() >> 8;
+ temp = ClipShort(temp);
+ *userBufPtr = (char)(temp>>8);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Input_Int32_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset, int userFrameOffset, uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (char)(temp>>24);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ temp = temp>>16; // Shift to get a 16 bit value, then use the 16 bits to 8 bits code (MUST BE CHECHED)
+ temp += Pa_TriangularDither() >> 8;
+ temp = ClipShort(temp);
+ *userBufPtr = (char)(temp >> 8);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+
+static void Input_Float32_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap)
+{
+ unsigned long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ unsigned long *asioBufPtr = &((unsigned long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (char)((float)temp*MAX_INT8_FP); // Is temp a value between -1.0 and 1.0 ??
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ unsigned long *asioBufPtr = &((unsigned long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ float dither = Pa_TriangularDither()*DITHER_SCALE;
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ temp = (char)(((float)temp * MAX_INT8_FP) + dither);
+ temp = ClipChar(temp);
+ *userBufPtr = (char)temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Input_Int16_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ *userBufPtr = (unsigned char)((temp>>8) + 0x80);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ temp += Pa_TriangularDither() >> 8;
+ temp = ClipShort(temp);
+ *userBufPtr = (unsigned char)((temp>>8) + 0x80);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Input_Int32_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (unsigned char)((temp>>24) + 0x80);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ temp = temp>>16; // Shift to get a 16 bit value, then use the 16 bits to 8 bits code (MUST BE CHECHED)
+ temp += Pa_TriangularDither() >> 8;
+ temp = ClipShort(temp);
+ *userBufPtr = (unsigned char)((temp>>8) + 0x80);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+
+static void Input_Float32_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap)
+{
+ unsigned long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ unsigned long *asioBufPtr = &((unsigned long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (unsigned char)(((float)temp*MAX_INT8_FP) + 0x80);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ unsigned long *asioBufPtr = &((unsigned long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ float dither = Pa_TriangularDither()*DITHER_SCALE;
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ temp = (char)(((float)temp * MAX_INT8_FP) + dither);
+ temp = ClipChar(temp);
+ *userBufPtr = (unsigned char)(temp + 0x80);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+
+// OUPUT
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Output_Float32_Int16 (ASIOBufferInfo* nativeBuffer, float *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags, bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ if( flags & paClipOff ) /* NOTHING */
+ {
+ for( j=0; j<NumOuputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = (short) (*userBufPtr * MAX_INT16_FP);
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = (short)temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+ else /* CLIP */
+ {
+ for( j=0; j<NumOuputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = (long) (*userBufPtr * MAX_INT16_FP);
+ temp = ClipShort(temp);
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = (short)temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* If you dither then you have to clip because dithering could push the signal out of range! */
+ for( j=0; j<NumOuputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ float dither = Pa_TriangularDither()*DITHER_SCALE;
+ temp = (long) ((*userBufPtr * MAX_INT16_FP) + dither);
+ temp = ClipShort(temp);
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = (short)temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Output_Float32_Int32 (ASIOBufferInfo* nativeBuffer, float *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paClipOff )
+ {
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (long) (*userBufPtr * MAX_INT32_FP);
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+ else // CLIP *
+ {
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ float temp1 = *userBufPtr;
+ temp1 = ClipFloat(temp1);
+ temp = (long) (temp1*MAX_INT32_FP);
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+
+ static void Output_Float32_Float32 (ASIOBufferInfo* nativeBuffer, float *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paClipOff )
+ {
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (long) *userBufPtr;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = (float)temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+
+ }
+ else /* CLIP */
+ {
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ float temp1 = *userBufPtr;
+ temp1 = ClipFloat(temp1); // Is is necessary??
+ temp = (long) temp1;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = (float)temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Output_Int32_Int16(ASIOBufferInfo* nativeBuffer, long *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (short) ((*userBufPtr) >> 16);
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = (short)temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+ else
+ {
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (*userBufPtr >> 1) + Pa_TriangularDither();
+ temp = temp >> 15;
+ temp = (short) ClipShort(temp);
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = (short)temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Output_Int32_Int32(ASIOBufferInfo* nativeBuffer, long *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = *userBufPtr;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE CHECKED
+
+static void Output_Int32_Float32(ASIOBufferInfo* nativeBuffer, long *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = *userBufPtr;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = ((float)temp) * (1.0f / MAX_INT32_FP);
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Output_Int16_Int16(ASIOBufferInfo* nativeBuffer, short *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = *userBufPtr;
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = (short)temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Output_Int16_Int32(ASIOBufferInfo* nativeBuffer, short *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (*userBufPtr)<<16;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE CHECKED
+static void Output_Int16_Float32(ASIOBufferInfo* nativeBuffer, short *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = *userBufPtr;
+ asioBufPtr[i] = ((float)temp) * (1.0f / MAX_INT16_FP);
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Output_Int8_Int16(ASIOBufferInfo* nativeBuffer, char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (short)(*userBufPtr)<<8;
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = (short)temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Output_Int8_Int32(ASIOBufferInfo* nativeBuffer, char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (short)(*userBufPtr)<<24;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE CHECKED
+static void Output_Int8_Float32(ASIOBufferInfo* nativeBuffer, char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = *userBufPtr;
+ asioBufPtr[i] = (long)(((float)temp) * (1.0f / MAX_INT8_FP));
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Output_IntU8_Int16(ASIOBufferInfo* nativeBuffer, unsigned char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = ((short)((*userBufPtr) - 0x80)) << 8;
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = (short)temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Output_IntU8_Int32(ASIOBufferInfo* nativeBuffer, unsigned char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = ((short)((*userBufPtr) - 0x80)) << 24;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE CHECKED
+
+static void Output_IntU8_Float32(ASIOBufferInfo* nativeBuffer, unsigned char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = ((short)((*userBufPtr) - 0x80)) << 24;
+ asioBufPtr[i] = ((float)temp) * (1.0f / MAX_INT32_FP);
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Pa_ASIO_Clear_Output_16 (ASIOBufferInfo* nativeBuffer, long frames, long NumInputChannels, long NumOuputChannels, long index, long hostFrameOffset)
+{
+ int i,j;
+
+ for( j=0; j<NumOuputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ for (i= 0; i < frames; i++) {asioBufPtr[i] = 0; }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Pa_ASIO_Clear_Output_32 (ASIOBufferInfo* nativeBuffer, long frames, long NumInputChannels, long NumOuputChannels, long index, long hostFrameOffset)
+{
+ int i,j;
+
+ for( j=0; j<NumOuputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ for (i= 0; i < frames; i++) {asioBufPtr[i] = 0; }
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Pa_ASIO_Adaptor_Init()
+{
+ if (asioDriverInfo.past->past_FramesPerUserBuffer <= asioDriverInfo.past_FramesPerHostBuffer) {
+ asioDriverInfo.pahsc_hostOutputBufferFrameOffset = asioDriverInfo.pahsc_OutputBufferOffset;
+ asioDriverInfo.pahsc_userInputBufferFrameOffset = 0; // empty
+ asioDriverInfo.pahsc_userOutputBufferFrameOffset = asioDriverInfo.past->past_FramesPerUserBuffer; // empty
+ }else {
+ asioDriverInfo.pahsc_hostOutputBufferFrameOffset = 0; // empty
+ asioDriverInfo.pahsc_userInputBufferFrameOffset = asioDriverInfo.pahsc_InputBufferOffset;
+ asioDriverInfo.pahsc_userOutputBufferFrameOffset = asioDriverInfo.past->past_FramesPerUserBuffer; // empty
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// FIXME : optimization for Input only or output only modes (really necessary ??)
+static void Pa_ASIO_Callback_Input( long index)
+{
+ internalPortAudioStream *past = asioDriverInfo.past;
+ long framesInputHostBuffer = asioDriverInfo.past_FramesPerHostBuffer; // number of frames available into the host input buffer
+ long framesInputUserBuffer; // number of frames needed to complete the user input buffer
+ long framesOutputHostBuffer; // number of frames needed to complete the host output buffer
+ long framesOuputUserBuffer; // number of frames available into the user output buffer
+ long userResult;
+ long tmp;
+
+ /* Fill host ASIO output with remaining frames in user output */
+ framesOutputHostBuffer = asioDriverInfo.past_FramesPerHostBuffer;
+ framesOuputUserBuffer = asioDriverInfo.past->past_FramesPerUserBuffer - asioDriverInfo.pahsc_userOutputBufferFrameOffset;
+ tmp = min(framesOutputHostBuffer, framesOuputUserBuffer);
+ framesOutputHostBuffer -= tmp;
+ Pa_ASIO_Callback_Output(index,tmp);
+
+ /* Available frames in hostInputBuffer */
+ while (framesInputHostBuffer > 0) {
+
+ /* Number of frames needed to complete an user input buffer */
+ framesInputUserBuffer = asioDriverInfo.past->past_FramesPerUserBuffer - asioDriverInfo.pahsc_userInputBufferFrameOffset;
+
+ if (framesInputHostBuffer >= framesInputUserBuffer) {
+
+ /* Convert ASIO input to user input */
+ Pa_ASIO_Convert_Inter_Input (asioDriverInfo.bufferInfos,
+ past->past_InputBuffer,
+ asioDriverInfo.pahsc_NumInputChannels ,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ framesInputUserBuffer,
+ asioDriverInfo.past_FramesPerHostBuffer - framesInputHostBuffer,
+ asioDriverInfo.pahsc_userInputBufferFrameOffset,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ past->past_InputSampleFormat,
+ past->past_Flags,
+ index);
+
+ /* Call PortAudio callback */
+ userResult = asioDriverInfo.past->past_Callback(past->past_InputBuffer, past->past_OutputBuffer,
+ past->past_FramesPerUserBuffer,past->past_FrameCount,past->past_UserData );
+
+ /* User callback has asked us to stop in the middle of the host buffer */
+ if( userResult != 0) {
+
+ /* Put 0 in the end of the output buffer */
+ Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ asioDriverInfo.pahsc_NumInputChannels ,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ index,
+ asioDriverInfo.pahsc_hostOutputBufferFrameOffset,
+ asioDriverInfo.past_FramesPerHostBuffer - asioDriverInfo.pahsc_hostOutputBufferFrameOffset);
+
+ past->past_StopSoon = 1;
+ return;
+ }
+
+
+ /* Full user ouput buffer : write offset */
+ asioDriverInfo.pahsc_userOutputBufferFrameOffset = 0;
+
+ /* Empty user input buffer : read offset */
+ asioDriverInfo.pahsc_userInputBufferFrameOffset = 0;
+
+ /* Fill host ASIO output */
+ tmp = min (past->past_FramesPerUserBuffer,framesOutputHostBuffer);
+ Pa_ASIO_Callback_Output(index,tmp);
+
+ framesOutputHostBuffer -= tmp;
+ framesInputHostBuffer -= framesInputUserBuffer;
+
+ }else {
+
+ /* Convert ASIO input to user input */
+ Pa_ASIO_Convert_Inter_Input (asioDriverInfo.bufferInfos,
+ past->past_InputBuffer,
+ asioDriverInfo.pahsc_NumInputChannels ,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ framesInputHostBuffer,
+ asioDriverInfo.past_FramesPerHostBuffer - framesInputHostBuffer,
+ asioDriverInfo.pahsc_userInputBufferFrameOffset,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ past->past_InputSampleFormat,
+ past->past_Flags,
+ index);
+
+ /* Update pahsc_userInputBufferFrameOffset */
+ asioDriverInfo.pahsc_userInputBufferFrameOffset += framesInputHostBuffer;
+
+ /* Update framesInputHostBuffer */
+ framesInputHostBuffer = 0;
+ }
+ }
+
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Pa_ASIO_Callback_Output(long index, long framePerBuffer)
+{
+ internalPortAudioStream *past = asioDriverInfo.past;
+
+ if (framePerBuffer > 0) {
+
+ /* Convert user output to ASIO ouput */
+ Pa_ASIO_Convert_Inter_Output (asioDriverInfo.bufferInfos,
+ past->past_OutputBuffer,
+ asioDriverInfo.pahsc_NumInputChannels,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ framePerBuffer,
+ asioDriverInfo.pahsc_hostOutputBufferFrameOffset,
+ asioDriverInfo.pahsc_userOutputBufferFrameOffset,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ past->past_InputSampleFormat,
+ past->past_Flags,
+ index);
+
+ /* Update hostOuputFrameOffset */
+ asioDriverInfo.pahsc_hostOutputBufferFrameOffset += framePerBuffer;
+
+ /* Update userOutputFrameOffset */
+ asioDriverInfo.pahsc_userOutputBufferFrameOffset += framePerBuffer;
+ }
+}
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Pa_ASIO_Callback_End()
+ {
+ /* Empty ASIO ouput : write offset */
+ asioDriverInfo.pahsc_hostOutputBufferFrameOffset = 0;
+ }
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+static void Pa_ASIO_Clear_User_Buffers()
+{
+ if( asioDriverInfo.past->past_InputBuffer != NULL )
+ {
+ memset( asioDriverInfo.past->past_InputBuffer, 0, asioDriverInfo.past->past_InputBufferSize );
+ }
+ if( asioDriverInfo.past->past_OutputBuffer != NULL )
+ {
+ memset( asioDriverInfo.past->past_OutputBuffer, 0, asioDriverInfo.past->past_OutputBufferSize );
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ static void Pa_ASIO_Clear_Output(ASIOBufferInfo* nativeBuffer,
+ ASIOSampleType nativeFormat,
+ long NumInputChannels,
+ long NumOuputChannels,
+ long index,
+ long hostFrameOffset,
+ long frames)
+{
+
+ switch (nativeFormat) {
+
+ case ASIOSTInt16MSB:
+ case ASIOSTInt16LSB:
+ case ASIOSTInt32MSB16:
+ case ASIOSTInt32LSB16:
+ Pa_ASIO_Clear_Output_16(nativeBuffer, frames, NumInputChannels, NumOuputChannels, index, hostFrameOffset);
+ break;
+
+ case ASIOSTFloat64MSB:
+ case ASIOSTFloat64LSB:
+ break;
+
+ case ASIOSTFloat32MSB:
+ case ASIOSTFloat32LSB:
+ case ASIOSTInt32MSB:
+ case ASIOSTInt32LSB:
+ case ASIOSTInt32MSB18:
+ case ASIOSTInt32MSB20:
+ case ASIOSTInt32MSB24:
+ case ASIOSTInt32LSB18:
+ case ASIOSTInt32LSB20:
+ case ASIOSTInt32LSB24:
+ Pa_ASIO_Clear_Output_32(nativeBuffer, frames, NumInputChannels, NumOuputChannels, index, hostFrameOffset);
+ break;
+
+ case ASIOSTInt24MSB:
+ case ASIOSTInt24LSB:
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+//---------------------------------------------------------------------------------------
+static void Pa_ASIO_Convert_Inter_Input(
+ ASIOBufferInfo* nativeBuffer,
+ void* inputBuffer,
+ long NumInputChannels,
+ long NumOuputChannels,
+ long framePerBuffer,
+ long hostFrameOffset,
+ long userFrameOffset,
+ ASIOSampleType nativeFormat,
+ PaSampleFormat paFormat,
+ PaStreamFlags flags,
+ long index)
+{
+
+ if((NumInputChannels > 0) && (nativeBuffer != NULL))
+ {
+ /* Convert from native format to PA format. */
+ switch(paFormat)
+ {
+ case paFloat32:
+ {
+ float *inBufPtr = (float *) inputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Input_Int16_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Input_Int16_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap);
+ break;
+ case ASIOSTInt32LSB:
+ Input_Int32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,swap);
+ break;
+ case ASIOSTInt32MSB:
+ Input_Int32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap);
+ break;
+ case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,swap);
+ break;
+ case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+ }
+
+ break;
+ }
+
+ case paInt32:
+ {
+ long *inBufPtr = (long *)inputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Input_Int16_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Input_Int16_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTInt32LSB:
+ Input_Int32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt32MSB:
+ Input_Int32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+
+ }
+ break;
+ }
+
+ case paInt16:
+ {
+ short *inBufPtr = (short *) inputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Input_Int16_Int16(nativeBuffer, inBufPtr, framePerBuffer , NumInputChannels, index , hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Input_Int16_Int16(nativeBuffer, inBufPtr, framePerBuffer , NumInputChannels, index , hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTInt32LSB:
+ Input_Int32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTInt32MSB:
+ Input_Int32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+
+ }
+ break;
+ }
+
+ case paInt8:
+ {
+ /* Convert 16 bit data to 8 bit chars */
+
+ char *inBufPtr = (char *) inputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Input_Int16_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap);
+ break;
+ case ASIOSTInt16MSB:
+ Input_Int16_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTInt32LSB:
+ Input_Int32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTInt32MSB:
+ Input_Int32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+ }
+ break;
+ }
+
+ case paUInt8:
+ {
+ /* Convert 16 bit data to 8 bit unsigned chars */
+
+ unsigned char *inBufPtr = (unsigned char *)inputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Input_Int16_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTInt16MSB:
+ Input_Int16_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTInt32LSB:
+ Input_Int32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap);
+ break;
+ case ASIOSTInt32MSB:
+ Input_Int32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap);
+ break;
+ case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,!swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+}
+
+
+//---------------------------------------------------------------------------------------
+static void Pa_ASIO_Convert_Inter_Output(ASIOBufferInfo* nativeBuffer,
+ void* outputBuffer,
+ long NumInputChannels,
+ long NumOuputChannels,
+ long framePerBuffer,
+ long hostFrameOffset,
+ long userFrameOffset,
+ ASIOSampleType nativeFormat,
+ PaSampleFormat paFormat,
+ PaStreamFlags flags,
+ long index)
+{
+
+ if((NumOuputChannels > 0) && (nativeBuffer != NULL))
+ {
+ /* Convert from PA format to native format */
+
+ switch(paFormat)
+ {
+ case paFloat32:
+ {
+ float *outBufPtr = (float *) outputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Output_Float32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Output_Float32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTInt32LSB:
+ Output_Float32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags,swap);
+ break;
+ case ASIOSTInt32MSB:
+ Output_Float32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTFloat32LSB:
+ Output_Float32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset,flags,swap);
+ break;
+ case ASIOSTFloat32MSB:
+ Output_Float32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+ }
+ break;
+ }
+
+ case paInt32:
+ {
+ long *outBufPtr = (long *) outputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Output_Int32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTInt16MSB:
+ Output_Int32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTInt32LSB:
+ Output_Int32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTInt32MSB:
+ Output_Int32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTFloat32LSB:
+ Output_Int32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTFloat32MSB:
+ Output_Int32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+ }
+ break;
+ }
+
+ case paInt16:
+ {
+ short *outBufPtr = (short *) outputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Output_Int16_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Output_Int16_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTInt32LSB:
+ Output_Int16_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt32MSB:
+ Output_Int16_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTFloat32LSB:
+ Output_Int16_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTFloat32MSB:
+ Output_Int16_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+
+ }
+ break;
+ }
+
+
+ case paInt8:
+ {
+ char *outBufPtr = (char *) outputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Output_Int8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Output_Int8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTInt32LSB:
+ Output_Int8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt32MSB:
+ Output_Int8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTFloat32LSB:
+ Output_Int8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTFloat32MSB:
+ Output_Int8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+ }
+ break;
+ }
+
+ case paUInt8:
+ {
+ unsigned char *outBufPtr = (unsigned char *) outputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Output_IntU8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Output_IntU8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTInt32LSB:
+ Output_IntU8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt32MSB:
+ Output_IntU8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTFloat32LSB:
+ Output_IntU8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTFloat32MSB:
+ Output_IntU8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+}
+
+
+
+/* Load a ASIO driver corresponding to the required device */
+static PaError Pa_ASIO_loadDevice (long device)
+{
+ PaDeviceInfo * dev = &(sDevices[device].pad_Info);
+
+ if (!Pa_ASIO_loadAsioDriver((char *) dev->name)) return paHostError;
+ if (ASIOInit(&asioDriverInfo.pahsc_driverInfo) != ASE_OK) return paHostError;
+ if (ASIOGetChannels(&asioDriverInfo.pahsc_NumInputChannels, &asioDriverInfo.pahsc_NumOutputChannels) != ASE_OK) return paHostError;
+ if (ASIOGetBufferSize(&asioDriverInfo.pahsc_minSize, &asioDriverInfo.pahsc_maxSize, &asioDriverInfo.pahsc_preferredSize, &asioDriverInfo.pahsc_granularity) != ASE_OK) return paHostError;
+
+ if(ASIOOutputReady() == ASE_OK)
+ asioDriverInfo.pahsc_postOutput = true;
+ else
+ asioDriverInfo.pahsc_postOutput = false;
+
+ return paNoError;
+}
+
+//---------------------------------------------------
+static int GetHighestBitPosition (unsigned long n)
+{
+ int pos = -1;
+ while( n != 0 )
+ {
+ pos++;
+ n = n >> 1;
+ }
+ return pos;
+}
+
+//------------------------------------------------------------------------------------------
+static int GetFirstMultiple(long min, long val ){ return ((min + val - 1) / val) * val; }
+
+//------------------------------------------------------------------------------------------
+static int GetFirstPossibleDivisor(long max, long val )
+{
+ for (int i = 2; i < 20; i++) {if (((val%i) == 0) && ((val/i) <= max)) return (val/i); }
+ return val;
+}
+
+//------------------------------------------------------------------------
+static int IsPowerOfTwo( unsigned long n ) { return ((n & (n-1)) == 0); }
+
+
+/*******************************************************************
+* Determine size of native ASIO audio buffer size
+* Input parameters : FramesPerUserBuffer, NumUserBuffers
+* Output values : FramesPerHostBuffer, OutputBufferOffset or InputtBufferOffset
+*/
+
+static PaError PaHost_CalcNumHostBuffers( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ long requestedBufferSize;
+ long firstMultiple, firstDivisor;
+
+ // Compute requestedBufferSize
+ if( past->past_NumUserBuffers < 1 ){
+ requestedBufferSize = past->past_FramesPerUserBuffer;
+ }else{
+ requestedBufferSize = past->past_NumUserBuffers * past->past_FramesPerUserBuffer;
+ }
+
+ // Adjust FramesPerHostBuffer using requestedBufferSize, ASIO minSize and maxSize,
+ if (requestedBufferSize < asioDriverInfo.pahsc_minSize){
+
+ firstMultiple = GetFirstMultiple(asioDriverInfo.pahsc_minSize, requestedBufferSize);
+
+ if (firstMultiple <= asioDriverInfo.pahsc_maxSize)
+ asioDriverInfo.past_FramesPerHostBuffer = firstMultiple;
+ else
+ asioDriverInfo.past_FramesPerHostBuffer = asioDriverInfo.pahsc_minSize;
+
+ }else if (requestedBufferSize > asioDriverInfo.pahsc_maxSize){
+
+ firstDivisor = GetFirstPossibleDivisor(asioDriverInfo.pahsc_maxSize, requestedBufferSize);
+
+ if ((firstDivisor >= asioDriverInfo.pahsc_minSize) && (firstDivisor <= asioDriverInfo.pahsc_maxSize))
+ asioDriverInfo.past_FramesPerHostBuffer = firstDivisor;
+ else
+ asioDriverInfo.past_FramesPerHostBuffer = asioDriverInfo.pahsc_maxSize;
+ }else{
+ asioDriverInfo.past_FramesPerHostBuffer = requestedBufferSize;
+ }
+
+ // If ASIO buffer size needs to be a power of two
+ if( asioDriverInfo.pahsc_granularity < 0 ){
+ // Needs to be a power of two.
+
+ if( !IsPowerOfTwo( asioDriverInfo.past_FramesPerHostBuffer ) )
+ {
+ int highestBit = GetHighestBitPosition(asioDriverInfo.past_FramesPerHostBuffer);
+ asioDriverInfo.past_FramesPerHostBuffer = 1 << (highestBit + 1);
+ }
+ }
+
+ DBUG(("----------------------------------\n"));
+ DBUG(("PortAudio : minSize = %ld \n",asioDriverInfo.pahsc_minSize));
+ DBUG(("PortAudio : preferredSize = %ld \n",asioDriverInfo.pahsc_preferredSize));
+ DBUG(("PortAudio : maxSize = %ld \n",asioDriverInfo.pahsc_maxSize));
+ DBUG(("PortAudio : granularity = %ld \n",asioDriverInfo.pahsc_granularity));
+ DBUG(("PortAudio : User buffer size = %d\n", asioDriverInfo.past->past_FramesPerUserBuffer ));
+ DBUG(("PortAudio : ASIO buffer size = %d\n", asioDriverInfo.past_FramesPerHostBuffer ));
+
+ if (asioDriverInfo.past_FramesPerHostBuffer > past->past_FramesPerUserBuffer){
+
+ // Computes the MINIMUM value of null frames shift for the output buffer alignement
+ asioDriverInfo.pahsc_OutputBufferOffset = Pa_ASIO_CalcFrameShift (asioDriverInfo.past_FramesPerHostBuffer,past->past_FramesPerUserBuffer);
+ asioDriverInfo.pahsc_InputBufferOffset = 0;
+ DBUG(("PortAudio : Minimum BufferOffset for Output = %d\n", asioDriverInfo.pahsc_OutputBufferOffset));
+ }else{
+
+ //Computes the MINIMUM value of null frames shift for the input buffer alignement
+ asioDriverInfo.pahsc_InputBufferOffset = Pa_ASIO_CalcFrameShift (asioDriverInfo.past_FramesPerHostBuffer,past->past_FramesPerUserBuffer);
+ asioDriverInfo.pahsc_OutputBufferOffset = 0;
+ DBUG(("PortAudio : Minimum BufferOffset for Input = %d\n", asioDriverInfo.pahsc_InputBufferOffset));
+ }
+
+ return paNoError;
+}
+
+
+/***********************************************************************/
+int Pa_CountDevices()
+{
+ PaError err ;
+
+ if( sNumDevices <= 0 )
+ {
+ /* Force loading of ASIO drivers */
+ err = Pa_ASIO_QueryDeviceInfo(sDevices);
+ if( err != paNoError ) goto error;
+ }
+
+ return sNumDevices;
+
+error:
+ PaHost_Term();
+ DBUG(("Pa_CountDevices: returns %d\n", err ));
+ return err;
+}
+
+/***********************************************************************/
+PaError PaHost_Init( void )
+{
+ /* Have we already initialized the device info? */
+ PaError err = (PaError) Pa_CountDevices();
+ return ( err < 0 ) ? err : paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_Term( void )
+{
+ int i;
+ PaDeviceInfo *dev;
+ double *rates;
+ PaError result = paNoError;
+
+ if (sNumDevices > 0) {
+
+ /* Free allocated sample rate arrays and names*/
+ for( i=0; i<sNumDevices; i++ ){
+ dev = &sDevices[i].pad_Info;
+ rates = (double *) dev->sampleRates;
+ if ((rates != NULL)) PaHost_FreeFastMemory(rates, MAX_NUMSAMPLINGRATES * sizeof(double));
+ dev->sampleRates = NULL;
+ if(dev->name != NULL) PaHost_FreeFastMemory((void *) dev->name, 32);
+ dev->name = NULL;
+
+ }
+
+ sNumDevices = 0;
+
+ /* Dispose : if not done by Pa_CloseStream */
+ if(ASIODisposeBuffers() != ASE_OK) result = paHostError;
+ if(ASIOExit() != ASE_OK) result = paHostError;
+
+ /* remove the loaded ASIO driver */
+ asioDrivers->removeCurrentDriver();
+ }
+
+ return result;
+}
+
+/***********************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ ASIOError err;
+ int32 device;
+
+ /* Check if a stream already runs */
+ if (asioDriverInfo.past != NULL) return paHostError;
+
+ /* Check the device number */
+ if ((past->past_InputDeviceID != paNoDevice)
+ &&(past->past_OutputDeviceID != paNoDevice)
+ &&(past->past_InputDeviceID != past->past_OutputDeviceID))
+ {
+ return paInvalidDeviceId;
+ }
+
+ /* Allocation */
+ memset(&asioDriverInfo, 0, sizeof(PaHostSoundControl));
+ past->past_DeviceData = (void*) &asioDriverInfo;
+
+
+ /* FIXME */
+ asioDriverInfo.past = past;
+
+ /* load the ASIO device */
+ device = (past->past_InputDeviceID < 0) ? past->past_OutputDeviceID : past->past_InputDeviceID;
+ result = Pa_ASIO_loadDevice(device);
+ if (result != paNoError) goto error;
+
+ /* Check ASIO parameters and input parameters */
+ if ((past->past_NumInputChannels > asioDriverInfo.pahsc_NumInputChannels)
+ || (past->past_NumOutputChannels > asioDriverInfo.pahsc_NumOutputChannels)) {
+ result = paInvalidChannelCount;
+ goto error;
+ }
+
+ /* Set sample rate */
+ if (ASIOSetSampleRate(past->past_SampleRate) != ASE_OK) {
+ result = paInvalidSampleRate;
+ goto error;
+ }
+
+ /* if OK calc buffer size */
+ result = PaHost_CalcNumHostBuffers( past );
+ if (result != paNoError) goto error;
+
+
+ /*
+ Allocating input and output buffers number for the real past_NumInputChannels and past_NumOutputChannels
+ optimize the data transfer.
+ */
+
+ asioDriverInfo.pahsc_NumInputChannels = past->past_NumInputChannels;
+ asioDriverInfo.pahsc_NumOutputChannels = past->past_NumOutputChannels;
+
+ /* Allocate ASIO buffers and callback*/
+ err = Pa_ASIO_CreateBuffers(&asioDriverInfo,
+ asioDriverInfo.pahsc_NumInputChannels,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ asioDriverInfo.past_FramesPerHostBuffer);
+
+ if (err == ASE_OK)
+ return paNoError;
+ else if (err == ASE_NoMemory)
+ result = paInsufficientMemory;
+ else if (err == ASE_InvalidParameter)
+ result = paInvalidChannelCount;
+ else if (err == ASE_InvalidMode)
+ result = paBufferTooBig;
+ else
+ result = paHostError;
+
+error:
+ ASIOExit();
+ return result;
+
+}
+
+/***********************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ PaError result = paNoError;
+
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+
+ #if PA_TRACE_START_STOP
+ AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut );
+ #endif
+
+ /* Dispose */
+ if(ASIODisposeBuffers() != ASE_OK) result = paHostError;
+ if(ASIOExit() != ASE_OK) result = paHostError;
+
+ /* Free data and device for output. */
+ past->past_DeviceData = NULL;
+ asioDriverInfo.past = NULL;
+
+ return result;
+}
+
+/***********************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+ /* Clear the index 0 host output buffer */
+ Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ asioDriverInfo.pahsc_NumInputChannels,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ 0,
+ 0,
+ asioDriverInfo.past_FramesPerHostBuffer);
+
+ /* Clear the index 1 host output buffer */
+ Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ asioDriverInfo.pahsc_NumInputChannels,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ 1,
+ 0,
+ asioDriverInfo.past_FramesPerHostBuffer);
+
+ Pa_ASIO_Clear_User_Buffers();
+
+ Pa_ASIO_Adaptor_Init();
+
+ return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+ /* Nothing to do ?? */
+ return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+ /* Nothing to do ?? */
+ return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+ /* Nothing to do */
+ return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+ // TO DO : count of samples
+ past->past_IsActive = 1;
+ return (ASIOStart() == ASE_OK) ? paNoError : paHostError;
+}
+
+/***********************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+ // TO DO : count of samples
+ past->past_IsActive = 0;
+ return (ASIOStop() == ASE_OK) ? paNoError : paHostError;
+}
+
+/***********************************************************************/
+// TO BE CHECKED
+PaError PaHost_StreamActive( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+ return (PaError) past->past_IsActive;
+}
+
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ PaHostSoundControl *pahsc;
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ return pahsc->pahsc_NumFramesDone;
+}
+
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+ #if MAC
+ void *addr = NewPtrClear( numBytes );
+ if( (addr == NULL) || (MemError () != 0) ) return NULL;
+
+ #if (CARBON_COMPATIBLE == 0)
+ if( HoldMemory( addr, numBytes ) != noErr )
+ {
+ DisposePtr( (Ptr) addr );
+ return NULL;
+ }
+ #endif
+ return addr;
+ #elif WINDOWS
+ void *addr = malloc( numBytes ); /* FIXME - do we need physical memory? */
+ if( addr != NULL ) memset( addr, 0, numBytes );
+ return addr;
+ #endif
+}
+
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+ #if MAC
+ if( addr == NULL ) return;
+ #if CARBON_COMPATIBLE
+ (void) numBytes;
+ #else
+ UnholdMemory( addr, numBytes );
+ #endif
+ DisposePtr( (Ptr) addr );
+ #elif WINDOWS
+ if( addr != NULL ) free( addr );
+ #endif
+}
+
+
+/*************************************************************************/
+void Pa_Sleep( long msec )
+{
+ #if MAC
+ int32 sleepTime, endTime;
+ /* Convert to ticks. Round up so we sleep a MINIMUM of msec time. */
+ sleepTime = ((msec * 60) + 999) / 1000;
+ if( sleepTime < 1 ) sleepTime = 1;
+ endTime = TickCount() + sleepTime;
+ do{
+ DBUGX(("Sleep for %d ticks.\n", sleepTime ));
+ WaitNextEvent( 0, NULL, sleepTime, NULL ); /* Use this just to sleep without getting events. */
+ sleepTime = endTime - TickCount();
+ } while( sleepTime > 0 );
+ #elif WINDOWS
+ Sleep( msec );
+ #endif
+}
+
+/*************************************************************************/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+ if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+ return &sDevices[id].pad_Info;
+}
+
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+ return sDefaultInputDeviceID;
+}
+
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+ return sDefaultOutputDeviceID;
+}
+
+/*************************************************************************/
+int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate )
+{
+ // TO BE IMPLEMENTED : using the ASIOGetLatency call??
+ return 2;
+}
+
+/*************************************************************************/
+int32 Pa_GetHostError( void )
+{
+ int32 err = sPaHostError;
+ sPaHostError = 0;
+ return err;
+}
+
+
+#ifdef MAC
+
+/**************************************************************************/
+static void Pa_StartUsageCalculation( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ UnsignedWide widePad;
+ if( pahsc == NULL ) return;
+/* Query system timer for usage analysis and to prevent overuse of CPU. */
+ Microseconds( &widePad );
+ pahsc->pahsc_EntryCount = UnsignedWideToUInt64( widePad );
+}
+/**************************************************************************/
+static void Pa_EndUsageCalculation( internalPortAudioStream *past )
+{
+ UnsignedWide widePad;
+ UInt64 CurrentCount;
+ long InsideCount;
+ long TotalCount;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+/* Measure CPU utilization during this callback. Note that this calculation
+** assumes that we had the processor the whole time.
+*/
+#define LOWPASS_COEFFICIENT_0 (0.9)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+ Microseconds( &widePad );
+ CurrentCount = UnsignedWideToUInt64( widePad );
+ if( past->past_IfLastExitValid )
+ {
+ InsideCount = (long) U64Subtract(CurrentCount, pahsc->pahsc_EntryCount);
+ TotalCount = (long) U64Subtract(CurrentCount, pahsc->pahsc_LastExitCount);
+/* Low pass filter the result because sometimes we get called several times in a row.
+* That can cause the TotalCount to be very low which can cause the usage to appear
+* unnaturally high. So we must filter numerator and denominator separately!!!
+*/
+ past->past_AverageInsideCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
+ (LOWPASS_COEFFICIENT_1 * InsideCount));
+ past->past_AverageTotalCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
+ (LOWPASS_COEFFICIENT_1 * TotalCount));
+ past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
+ }
+ pahsc->pahsc_LastExitCount = CurrentCount;
+ past->past_IfLastExitValid = 1;
+}
+
+#elif WINDOWS
+
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+static void Pa_StartUsageCalculation( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+/* Query system timer for usage analysis and to prevent overuse of CPU. */
+ QueryPerformanceCounter( &pahsc->pahsc_EntryCount );
+}
+
+static void Pa_EndUsageCalculation( internalPortAudioStream *past )
+{
+ LARGE_INTEGER CurrentCount = { 0, 0 };
+ LONGLONG InsideCount;
+ LONGLONG TotalCount;
+/*
+** Measure CPU utilization during this callback. Note that this calculation
+** assumes that we had the processor the whole time.
+*/
+#define LOWPASS_COEFFICIENT_0 (0.9)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+
+ if( QueryPerformanceCounter( &CurrentCount ) )
+ {
+ if( past->past_IfLastExitValid )
+ {
+ InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart;
+ TotalCount = CurrentCount.QuadPart - pahsc->pahsc_LastExitCount.QuadPart;
+/* Low pass filter the result because sometimes we get called several times in a row.
+ * That can cause the TotalCount to be very low which can cause the usage to appear
+ * unnaturally high. So we must filter numerator and denominator separately!!!
+ */
+ past->past_AverageInsideCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
+ (LOWPASS_COEFFICIENT_1 * InsideCount));
+ past->past_AverageTotalCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
+ (LOWPASS_COEFFICIENT_1 * TotalCount));
+ past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
+ }
+ pahsc->pahsc_LastExitCount = CurrentCount;
+ past->past_IfLastExitValid = 1;
+ }
+}
+
+#endif
+
+
+
+
diff --git a/pd/portaudio/pa_common/pa_convert.c b/pd/portaudio/pa_common/pa_convert.c
new file mode 100644
index 00000000..377a9554
--- /dev/null
+++ b/pd/portaudio/pa_common/pa_convert.c
@@ -0,0 +1,402 @@
+/*
+ * pa_conversions.c
+ * portaudio
+ *
+ * Created by Phil Burk on Mon Mar 18 2002.
+ *
+ */
+#include <stdio.h>
+
+#include "portaudio.h"
+#include "pa_host.h"
+
+#define CLIP( val, min, max ) { val = ((val) < (min)) ? min : (((val) < (max)) ? (max) : (val)); }
+
+/*************************************************************************/
+static void PaConvert_Float32_Int16(
+ float *sourceBuffer, int sourceStride,
+ short *targetBuffer, int targetStride,
+ int numSamples )
+{
+ int i;
+ for( i=0; i<numSamples; i++ )
+ {
+ short samp = (short) (*sourceBuffer * (32767.0f));
+ *targetBuffer = samp;
+ sourceBuffer += sourceStride;
+ targetBuffer += targetStride;
+ }
+}
+
+/*************************************************************************/
+static void PaConvert_Float32_Int16_Clip(
+ float *sourceBuffer, int sourceStride,
+ short *targetBuffer, int targetStride,
+ int numSamples )
+{
+ int i;
+ for( i=0; i<numSamples; i++ )
+ {
+ long samp = (long) (*sourceBuffer * (32767.0f));
+ CLIP( samp, -0x8000, 0x7FFF );
+ *targetBuffer = (short) samp;
+ sourceBuffer += sourceStride;
+ targetBuffer += targetStride;
+ }
+}
+
+/*************************************************************************/
+static void PaConvert_Float32_Int16_ClipDither(
+ float *sourceBuffer, int sourceStride,
+ short *targetBuffer, int targetStride,
+ int numSamples )
+{
+ int i;
+ for( i=0; i<numSamples; i++ )
+ {
+ // use smaller scaler to prevent overflow when we add the dither
+ float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE;
+ float dithered = (*sourceBuffer * (32766.0f)) + dither;
+ long samp = (long) dithered;
+ CLIP( samp, -0x8000, 0x7FFF );
+ *targetBuffer = (short) samp;
+ sourceBuffer += sourceStride;
+ targetBuffer += targetStride;
+ }
+}
+
+/*************************************************************************/
+static void PaConvert_Float32_Int16_Dither(
+ float *sourceBuffer, int sourceStride,
+ short *targetBuffer, int targetStride,
+ int numSamples )
+{
+ int i;
+ for( i=0; i<numSamples; i++ )
+ {
+ // use smaller scaler to prevent overflow when we add the dither
+ float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE;
+ float dithered = (*sourceBuffer * (32766.0f)) + dither;
+ *targetBuffer = (short) dithered;
+ sourceBuffer += sourceStride;
+ targetBuffer += targetStride;
+ }
+}
+
+
+/*************************************************************************/
+static void PaConvert_Int16_Float32(
+ short *sourceBuffer, int sourceStride,
+ float *targetBuffer, int targetStride,
+ int numSamples )
+{
+ int i;
+ for( i=0; i<numSamples; i++ )
+ {
+ float samp = *sourceBuffer * (1.0f / 32768.0f);
+ *targetBuffer = samp;
+ sourceBuffer += sourceStride;
+ targetBuffer += targetStride;
+ }
+}
+
+/*************************************************************************/
+static void PaConvert_Float32_Int8(
+ float *sourceBuffer, int sourceStride,
+ char *targetBuffer, int targetStride,
+ int numSamples )
+{
+ int i;
+ for( i=0; i<numSamples; i++ )
+ {
+ char samp = (char) (*sourceBuffer * (127.0));
+ *targetBuffer = samp;
+ sourceBuffer += sourceStride;
+ targetBuffer += targetStride;
+ }
+}
+
+
+/*************************************************************************/
+static void PaConvert_Float32_Int8_Clip(
+ float *sourceBuffer, int sourceStride,
+ char *targetBuffer, int targetStride,
+ int numSamples )
+{
+ int i;
+ for( i=0; i<numSamples; i++ )
+ {
+ long samp = *sourceBuffer * 127.0f;
+ CLIP( samp, -0x80, 0x7F );
+ *targetBuffer = (char) samp;
+ sourceBuffer += sourceStride;
+ targetBuffer += targetStride;
+ }
+}
+
+/*************************************************************************/
+static void PaConvert_Float32_Int8_ClipDither(
+ float *sourceBuffer, int sourceStride,
+ char *targetBuffer, int targetStride,
+ int numSamples )
+{
+ int i;
+ for( i=0; i<numSamples; i++ )
+ {
+ // use smaller scaler to prevent overflow when we add the dither
+ float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE;
+ float dithered = (*sourceBuffer * (126.0f)) + dither;
+ long samp = (long) dithered;
+ CLIP( samp, -0x80, 0x7F );
+ *targetBuffer = (char) samp;
+ sourceBuffer += sourceStride;
+ targetBuffer += targetStride;
+ }
+}
+
+/*************************************************************************/
+static void PaConvert_Float32_Int8_Dither(
+ float *sourceBuffer, int sourceStride,
+ char *targetBuffer, int targetStride,
+ int numSamples )
+{
+ int i;
+ for( i=0; i<numSamples; i++ )
+ {
+ // use smaller scaler to prevent overflow when we add the dither
+ float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE; //FIXME
+ float dithered = (*sourceBuffer * (126.0f)) + dither;
+ long samp = (long) dithered;
+ *targetBuffer = (char) samp;
+ sourceBuffer += sourceStride;
+ targetBuffer += targetStride;
+ }
+}
+
+/*************************************************************************/
+static void PaConvert_Int8_Float32(
+ char *sourceBuffer, int sourceStride,
+ float *targetBuffer, int targetStride,
+ int numSamples )
+{
+ int i;
+ for( i=0; i<numSamples; i++ )
+ {
+ float samp = *sourceBuffer * (1.0f / 128.0f);
+ *targetBuffer = samp;
+ sourceBuffer += sourceStride;
+ targetBuffer += targetStride;
+ }
+}
+
+/*************************************************************************/
+static void PaConvert_Float32_UInt8(
+ float *sourceBuffer, int sourceStride,
+ unsigned char *targetBuffer, int targetStride,
+ int numSamples )
+{
+ int i;
+ for( i=0; i<numSamples; i++ )
+ {
+ unsigned char samp = 128 + (unsigned char) (*sourceBuffer * (127.0));
+ *targetBuffer = samp;
+ sourceBuffer += sourceStride;
+ targetBuffer += targetStride;
+ }
+}
+
+/*************************************************************************/
+static void PaConvert_UInt8_Float32(
+ unsigned char *sourceBuffer, int sourceStride,
+ float *targetBuffer, int targetStride,
+ int numSamples )
+{
+ int i;
+ for( i=0; i<numSamples; i++ )
+ {
+ float samp = (*sourceBuffer - 128) * (1.0f / 128.0f);
+ *targetBuffer = samp;
+ sourceBuffer += sourceStride;
+ targetBuffer += targetStride;
+ }
+}
+
+/*************************************************************************/
+static PortAudioConverter *PaConvert_SelectProc( PaSampleFormat sourceFormat,
+ PaSampleFormat targetFormat, int ifClip, int ifDither )
+{
+ PortAudioConverter *proc = NULL;
+ switch( sourceFormat )
+ {
+ case paUInt8:
+ switch( targetFormat )
+ {
+ case paFloat32:
+ proc = (PortAudioConverter *) PaConvert_UInt8_Float32;
+ break;
+ default:
+ break;
+ }
+ break;
+ case paInt8:
+ switch( targetFormat )
+ {
+ case paFloat32:
+ proc = (PortAudioConverter *) PaConvert_Int8_Float32;
+ break;
+ default:
+ break;
+ }
+ break;
+ case paInt16:
+ switch( targetFormat )
+ {
+ case paFloat32:
+ proc = (PortAudioConverter *) PaConvert_Int16_Float32;
+ break;
+ default:
+ break;
+ }
+ break;
+ case paFloat32:
+ switch( targetFormat )
+ {
+ case paUInt8:
+ proc = (PortAudioConverter *) PaConvert_Float32_UInt8;
+ break;
+ case paInt8:
+ if( ifClip && ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_ClipDither;
+ else if( ifClip ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_Clip;
+ else if( ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_Dither;
+ else proc = (PortAudioConverter *) PaConvert_Float32_Int8;
+ break;
+ case paInt16:
+ if( ifClip && ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_ClipDither;
+ else if( ifClip ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_Clip;
+ else if( ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_Dither;
+ else proc = (PortAudioConverter *) PaConvert_Float32_Int16;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return proc;
+
+}
+
+/*************************************************************************/
+PaError PaConvert_SetupInput( internalPortAudioStream *past,
+ PaSampleFormat nativeInputSampleFormat )
+{
+ past->past_NativeInputSampleFormat = nativeInputSampleFormat;
+ past->past_InputConversionSourceStride = 1;
+ past->past_InputConversionTargetStride = 1;
+
+ if( nativeInputSampleFormat != past->past_InputSampleFormat )
+ {
+ int ifDither = (past->past_Flags & paDitherOff) == 0;
+ past->past_InputConversionProc = PaConvert_SelectProc( nativeInputSampleFormat,
+ past->past_InputSampleFormat, 0, ifDither );
+ if( past->past_InputConversionProc == NULL ) return paSampleFormatNotSupported;
+ }
+ else
+ {
+ past->past_InputConversionProc = NULL; /* no conversion necessary */
+ }
+
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaConvert_SetupOutput( internalPortAudioStream *past,
+ PaSampleFormat nativeOutputSampleFormat )
+{
+
+ past->past_NativeOutputSampleFormat = nativeOutputSampleFormat;
+ past->past_OutputConversionSourceStride = 1;
+ past->past_OutputConversionTargetStride = 1;
+
+ if( nativeOutputSampleFormat != past->past_OutputSampleFormat )
+ {
+ int ifDither = (past->past_Flags & paDitherOff) == 0;
+ int ifClip = (past->past_Flags & paClipOff) == 0;
+
+ past->past_OutputConversionProc = PaConvert_SelectProc( past->past_OutputSampleFormat,
+ nativeOutputSampleFormat, ifClip, ifDither );
+ if( past->past_OutputConversionProc == NULL ) return paSampleFormatNotSupported;
+ }
+ else
+ {
+ past->past_OutputConversionProc = NULL; /* no conversion necessary */
+ }
+
+ return paNoError;
+}
+
+/*************************************************************************
+** Called by host code.
+** Convert input from native format to user format,
+** call user code,
+** then convert output to native format.
+** Returns result from user callback.
+*/
+long PaConvert_Process( internalPortAudioStream *past,
+ void *nativeInputBuffer,
+ void *nativeOutputBuffer )
+{
+ int userResult;
+ void *inputBuffer = NULL;
+ void *outputBuffer = NULL;
+
+ /* Get native input data. */
+ if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
+ {
+ if( past->past_InputSampleFormat == past->past_NativeInputSampleFormat )
+ {
+ /* Already in native format so just read directly from native buffer. */
+ inputBuffer = nativeInputBuffer;
+ }
+ else
+ {
+ inputBuffer = past->past_InputBuffer;
+ /* Convert input data to user format. */
+ (*past->past_InputConversionProc)(nativeInputBuffer, past->past_InputConversionSourceStride,
+ inputBuffer, past->past_InputConversionTargetStride,
+ past->past_FramesPerUserBuffer * past->past_NumInputChannels );
+ }
+ }
+
+ /* Are we doing output? */
+ if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
+ {
+ outputBuffer = (past->past_OutputConversionProc == NULL) ?
+ nativeOutputBuffer : past->past_OutputBuffer;
+ }
+ /*
+ AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
+ AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
+ */
+ /* Call user callback routine. */
+ userResult = past->past_Callback(
+ inputBuffer,
+ outputBuffer,
+ past->past_FramesPerUserBuffer,
+ past->past_FrameCount,
+ past->past_UserData );
+
+ /* Advance frame counter for timestamp. */
+ past->past_FrameCount += past->past_FramesPerUserBuffer; // FIXME - should this be in here?
+
+ /* Convert to native format if necessary. */
+ if( (past->past_OutputConversionProc != NULL ) && (outputBuffer != NULL) )
+ {
+ (*past->past_OutputConversionProc)( outputBuffer, past->past_OutputConversionSourceStride,
+ nativeOutputBuffer, past->past_OutputConversionTargetStride,
+ past->past_FramesPerUserBuffer * past->past_NumOutputChannels );
+ }
+
+ return userResult;
+}
diff --git a/pd/portaudio/pa_common/pa_host.h b/pd/portaudio/pa_common/pa_host.h
new file mode 100644
index 00000000..d9cd71ab
--- /dev/null
+++ b/pd/portaudio/pa_common/pa_host.h
@@ -0,0 +1,185 @@
+#ifndef PA_HOST_H
+#define PA_HOST_H
+
+/*
+ * $Id: pa_host.h,v 1.1.1.1 2002-07-29 17:06:18 ggeiger Exp $
+ * Host dependant internal API for PortAudio
+ *
+ * Author: Phil Burk <philburk@softsynth.com>
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.softsynth.com/portaudio/
+ * DirectSound and Macintosh Implementation
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#ifndef SUPPORT_AUDIO_CAPTURE
+#define SUPPORT_AUDIO_CAPTURE (1)
+#endif
+
+#ifndef int32
+ typedef long int32;
+#endif
+#ifndef uint32
+ typedef unsigned long uint32;
+#endif
+#ifndef int16
+ typedef short int16;
+#endif
+#ifndef uint16
+ typedef unsigned short uint16;
+#endif
+
+/* Used to convert between various sample formats. */
+typedef void (PortAudioConverter)(
+ void *inputBuffer, int inputStride,
+ void *outputBuffer, int outputStride,
+ int numSamples );
+
+#define PA_MAGIC (0x18273645)
+
+/************************************************************************************/
+/****************** Structures ******************************************************/
+/************************************************************************************/
+
+typedef struct internalPortAudioStream
+{
+ uint32 past_Magic; /* ID for struct to catch bugs. */
+ /* User specified information. */
+ uint32 past_FramesPerUserBuffer;
+ uint32 past_NumUserBuffers;
+ double past_SampleRate; /* Closest supported sample rate. */
+ int past_NumInputChannels;
+ int past_NumOutputChannels;
+ PaDeviceID past_InputDeviceID;
+ PaDeviceID past_OutputDeviceID;
+ PaSampleFormat past_NativeInputSampleFormat;
+ PaSampleFormat past_InputSampleFormat;
+ PaSampleFormat past_NativeOutputSampleFormat;
+ PaSampleFormat past_OutputSampleFormat;
+ void *past_DeviceData;
+ PortAudioCallback *past_Callback;
+ void *past_UserData;
+ uint32 past_Flags;
+ /* Flags for communicating between foreground and background. */
+ volatile int past_IsActive; /* Background is still playing. */
+ volatile int past_StopSoon; /* Background should keep playing when buffers empty. */
+ volatile int past_StopNow; /* Background should stop playing now. */
+ /* These buffers are used when the native format does not match the user format. */
+ void *past_InputBuffer;
+ uint32 past_InputBufferSize;
+ void *past_OutputBuffer;
+ uint32 past_OutputBufferSize;
+ /* Measurements */
+ uint32 past_NumCallbacks;
+ PaTimestamp past_FrameCount; /* Frames output to buffer. */
+ /* For measuring CPU utilization. */
+ double past_AverageInsideCount;
+ double past_AverageTotalCount;
+ double past_Usage;
+ int past_IfLastExitValid;
+ /* Format Conversion */
+ /* These are setup by PaConversion_Setup() */
+ PortAudioConverter *past_InputConversionProc;
+ int past_InputConversionSourceStride;
+ int past_InputConversionTargetStride;
+ PortAudioConverter *past_OutputConversionProc;
+ int past_OutputConversionSourceStride;
+ int past_OutputConversionTargetStride;
+}
+internalPortAudioStream;
+
+/************************************************************************************/
+/******** These functions must be provided by a platform implementation. ************/
+/************************************************************************************/
+
+PaError PaHost_Init( void );
+PaError PaHost_Term( void );
+
+PaError PaHost_OpenStream( internalPortAudioStream *past );
+PaError PaHost_CloseStream( internalPortAudioStream *past );
+
+PaError PaHost_StartOutput( internalPortAudioStream *past );
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort );
+PaError PaHost_StartInput( internalPortAudioStream *past );
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort );
+PaError PaHost_StartEngine( internalPortAudioStream *past );
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort );
+PaError PaHost_StreamActive( internalPortAudioStream *past );
+
+void *PaHost_AllocateFastMemory( long numBytes );
+void PaHost_FreeFastMemory( void *addr, long numBytes );
+
+/* This only called if PA_VALIDATE_RATE IS CALLED. */
+PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
+ double *closestFrameRatePtr );
+
+/**********************************************************************/
+/************ Common Utility Routines provided by PA ******************/
+/**********************************************************************/
+
+/* PaHost_IsInitialized() returns non-zero if PA is initialized, 0 otherwise */
+int PaHost_IsInitialized( void );
+
+internalPortAudioStream* PaHost_GetStreamRepresentation( PortAudioStream *stream );
+
+int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable,
+ int numRates, double frameRate );
+
+long Pa_CallConvertInt16( internalPortAudioStream *past,
+ short *nativeInputBuffer,
+ short *nativeOutputBuffer );
+
+/* Calculate 2 LSB dither signal with a triangular distribution.
+** Ranged properly for adding to a 32 bit 1.31 fixed point value prior to >>15.
+** Range of output is +/- 65535
+** Multiply by PA_DITHER_SCALE to get a float between -2.0 and 2.0. */
+#define PA_DITHER_BITS (15)
+#define PA_DITHER_SCALE (1.0f / ((1<<PA_DITHER_BITS)-1))
+long PaConvert_TriangularDither( void );
+
+PaError PaConvert_SetupInput( internalPortAudioStream *past,
+ PaSampleFormat nativeInputSampleFormat );
+
+PaError PaConvert_SetupOutput( internalPortAudioStream *past,
+ PaSampleFormat nativeOutputSampleFormat );
+
+long PaConvert_Process( internalPortAudioStream *past,
+ void *nativeInputBuffer,
+ void *nativeOutputBuffer );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_HOST_H */
diff --git a/pd/portaudio/pa_common/pa_lib.c b/pd/portaudio/pa_common/pa_lib.c
new file mode 100644
index 00000000..ade2d82b
--- /dev/null
+++ b/pd/portaudio/pa_common/pa_lib.c
@@ -0,0 +1,806 @@
+/*
+ * $Id: pa_lib.c,v 1.1.1.1 2002-07-29 17:06:18 ggeiger Exp $
+ * Portable Audio I/O Library
+ * Host Independant Layer
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* Modification History:
+ PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
+ PLB20010820 - fix dither and shift for recording PaUInt8 format
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
+#ifdef _WIN32
+#ifndef __MWERKS__
+#include <memory.h>
+#endif /* __MWERKS__ */
+#else /* !_WIN32 */
+#include <memory.h>
+#endif /* _WIN32 */
+
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+/* The reason we might NOT want to validate the rate before opening the stream
+ * is because many DirectSound drivers lie about the rates they actually support.
+ */
+#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */
+
+/*
+O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion
+*/
+
+#ifndef FALSE
+ #define FALSE (0)
+ #define TRUE (!FALSE)
+#endif
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+
+static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */
+
+static PaError Pa_KillStream( PortAudioStream *stream, int abort );
+
+/***********************************************************************/
+int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate )
+{
+ double err, minErr = allowableError;
+ int i, bestFit = -1;
+
+ for( i=0; i<numRates; i++ )
+ {
+ err = fabs( frameRate - rateTable[i] );
+ if( err < minErr )
+ {
+ minErr = err;
+ bestFit = i;
+ }
+ }
+ return bestFit;
+}
+
+/**************************************************************************
+** Make sure sample rate is legal and also convert to enumeration for driver.
+*/
+PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
+ double *closestFrameRatePtr )
+{
+ long bestRateIndex;
+ const PaDeviceInfo *pdi;
+ pdi = Pa_GetDeviceInfo( id );
+ if( pdi == NULL )
+ {
+ return paInvalidDeviceId;
+ }
+
+ if( pdi->numSampleRates == -1 )
+ {
+ /* Is it out of range? */
+ if( (requestedFrameRate < pdi->sampleRates[0]) ||
+ (requestedFrameRate > pdi->sampleRates[1]) )
+ {
+ return paInvalidSampleRate;
+ }
+
+ *closestFrameRatePtr = requestedFrameRate;
+ }
+ else
+ {
+ bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate );
+ if( bestRateIndex < 0 ) return paInvalidSampleRate;
+ *closestFrameRatePtr = pdi->sampleRates[bestRateIndex];
+ }
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError Pa_OpenStream(
+ PortAudioStream** streamPtrPtr,
+ PaDeviceID inputDeviceID,
+ int numInputChannels,
+ PaSampleFormat inputSampleFormat,
+ void *inputDriverInfo,
+ PaDeviceID outputDeviceID,
+ int numOutputChannels,
+ PaSampleFormat outputSampleFormat,
+ void *outputDriverInfo,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ unsigned long streamFlags,
+ PortAudioCallback *callback,
+ void *userData )
+{
+ internalPortAudioStream *past = NULL;
+ PaError result = paNoError;
+ int bitsPerInputSample;
+ int bitsPerOutputSample;
+ /* Print passed parameters. */
+ DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n",
+ streamPtrPtr, inputDeviceID, numInputChannels,
+ inputSampleFormat, inputDriverInfo ));
+ DBUG((" %d, %d, %d, %p, /* output */\n",
+ outputDeviceID, numOutputChannels,
+ outputSampleFormat, outputDriverInfo ));
+ DBUG((" %g, %d, %d, 0x%x, , %p )\n",
+ sampleRate, framesPerBuffer, numberOfBuffers,
+ streamFlags, userData ));
+
+ /* Check for parameter errors. */
+ if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag;
+ if( streamPtrPtr == NULL ) return paBadStreamPtr;
+ if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */
+ if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */
+ if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId;
+ if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) )
+ {
+ return paInvalidDeviceId;
+ }
+ if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount;
+
+#if SUPPORT_AUDIO_CAPTURE
+ if( inputDeviceID >= 0 )
+ {
+ PaError size = Pa_GetSampleSize( inputSampleFormat );
+ if( size < 0 ) return size;
+ bitsPerInputSample = 8 * size;
+ if( (numInputChannels <= 0) ) return paInvalidChannelCount;
+ }
+#else
+ if( inputDeviceID >= 0 )
+ {
+ return paInvalidChannelCount;
+ }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ else
+ {
+ if( numInputChannels > 0 ) return paInvalidChannelCount;
+ bitsPerInputSample = 0;
+ }
+
+ if( outputDeviceID >= 0 )
+ {
+ PaError size = Pa_GetSampleSize( outputSampleFormat );
+ if( size < 0 ) return size;
+ bitsPerOutputSample = 8 * size;
+ if( (numOutputChannels <= 0) ) return paInvalidChannelCount;
+ }
+ else
+ {
+ if( numOutputChannels > 0 ) return paInvalidChannelCount;
+ bitsPerOutputSample = 0;
+ }
+
+ if( callback == NULL ) return paNullCallback;
+
+ /* Allocate and clear stream structure. */
+ past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) );
+ if( past == NULL ) return paInsufficientMemory;
+ memset( past, 0, sizeof(internalPortAudioStream) );
+ AddTraceMessage("Pa_OpenStream: past", (long) past );
+
+ past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */
+ past->past_FramesPerUserBuffer = framesPerBuffer;
+ past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() MUST CHECK FOR ZERO! */
+ past->past_Callback = callback;
+ past->past_UserData = userData;
+ past->past_OutputSampleFormat = outputSampleFormat;
+ past->past_InputSampleFormat = inputSampleFormat;
+ past->past_OutputDeviceID = outputDeviceID;
+ past->past_InputDeviceID = inputDeviceID;
+ past->past_NumInputChannels = numInputChannels;
+ past->past_NumOutputChannels = numOutputChannels;
+ past->past_Flags = streamFlags;
+
+ /* Check for absurd sample rates. */
+ if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
+ {
+ result = paInvalidSampleRate;
+ goto cleanup;
+ }
+
+ /* Allocate buffers that may be used for format conversion from user to native buffers. */
+ if( numInputChannels > 0 )
+ {
+
+#if PA_VALIDATE_RATE
+ result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate );
+ if( result < 0 )
+ {
+ goto cleanup;
+ }
+#else
+ past->past_SampleRate = sampleRate;
+#endif
+ /* Allocate single Input buffer for passing formatted samples to user callback. */
+ past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8);
+ past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize);
+ if( past->past_InputBuffer == NULL )
+ {
+ result = paInsufficientMemory;
+ goto cleanup;
+ }
+ }
+ else
+ {
+ past->past_InputBuffer = NULL;
+ }
+
+ /* Allocate single Output buffer. */
+ if( numOutputChannels > 0 )
+ {
+#if PA_VALIDATE_RATE
+ result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate );
+ if( result < 0 )
+ {
+ goto cleanup;
+ }
+#else
+ past->past_SampleRate = sampleRate;
+#endif
+ past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8);
+ past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize);
+ if( past->past_OutputBuffer == NULL )
+ {
+ result = paInsufficientMemory;
+ goto cleanup;
+ }
+ }
+ else
+ {
+ past->past_OutputBuffer = NULL;
+ }
+
+ result = PaHost_OpenStream( past );
+ if( result < 0 ) goto cleanup;
+
+ *streamPtrPtr = (void *) past;
+
+ return result;
+
+cleanup:
+ if( past != NULL ) Pa_CloseStream( past );
+ *streamPtrPtr = NULL;
+ return result;
+}
+
+
+/*************************************************************************/
+PaError Pa_OpenDefaultStream( PortAudioStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PortAudioCallback *callback,
+ void *userData )
+{
+ return Pa_OpenStream(
+ stream,
+ ((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice),
+ numInputChannels, sampleFormat, NULL,
+ ((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
+ numOutputChannels, sampleFormat, NULL,
+ sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData );
+}
+
+/*************************************************************************/
+PaError Pa_CloseStream( PortAudioStream* stream)
+{
+ PaError result;
+ internalPortAudioStream *past;
+
+ DBUG(("Pa_CloseStream()\n"));
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+
+ Pa_AbortStream( past );
+ result = PaHost_CloseStream( past );
+
+ if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize );
+ if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize );
+ PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) );
+
+ return result;
+}
+
+/*************************************************************************/
+PaError Pa_StartStream( PortAudioStream *stream )
+{
+ PaError result = paHostError;
+ internalPortAudioStream *past;
+
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+
+ past->past_FrameCount = 0.0;
+
+ if( past->past_NumInputChannels > 0 )
+ {
+ result = PaHost_StartInput( past );
+ DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+ }
+
+ if( past->past_NumOutputChannels > 0 )
+ {
+ result = PaHost_StartOutput( past );
+ DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+ }
+
+ result = PaHost_StartEngine( past );
+ DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+
+ return paNoError;
+
+error:
+ return result;
+}
+
+/*************************************************************************/
+PaError Pa_StopStream( PortAudioStream *stream )
+{
+ return Pa_KillStream( stream, 0 );
+}
+
+/*************************************************************************/
+PaError Pa_AbortStream( PortAudioStream *stream )
+{
+ return Pa_KillStream( stream, 1 );
+}
+
+/*************************************************************************/
+static PaError Pa_KillStream( PortAudioStream *stream, int abort )
+{
+ PaError result = paNoError;
+ internalPortAudioStream *past;
+
+ DBUG(("Pa_StopStream().\n"));
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+
+ if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) )
+ {
+ result = PaHost_StopEngine( past, abort );
+ DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+ }
+
+ if( past->past_NumInputChannels > 0 )
+ {
+ result = PaHost_StopInput( past, abort );
+ DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result));
+ if( result != paNoError ) goto error;
+ }
+
+ if( past->past_NumOutputChannels > 0 )
+ {
+ result = PaHost_StopOutput( past, abort );
+ DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result));
+ if( result != paNoError ) goto error;
+ }
+
+error:
+ past->past_Usage = 0;
+ past->past_IfLastExitValid = 0;
+
+ return result;
+}
+
+/*************************************************************************/
+PaError Pa_StreamActive( PortAudioStream *stream )
+{
+ internalPortAudioStream *past;
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+ return PaHost_StreamActive( past );
+}
+
+/*************************************************************************/
+const char *Pa_GetErrorText( PaError errnum )
+{
+ const char *msg;
+
+ switch(errnum)
+ {
+ case paNoError: msg = "Success"; break;
+ case paHostError: msg = "Host error."; break;
+ case paInvalidChannelCount: msg = "Invalid number of channels."; break;
+ case paInvalidSampleRate: msg = "Invalid sample rate."; break;
+ case paInvalidDeviceId: msg = "Invalid device ID."; break;
+ case paInvalidFlag: msg = "Invalid flag."; break;
+ case paSampleFormatNotSupported: msg = "Sample format not supported"; break;
+ case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break;
+ case paInsufficientMemory: msg = "Insufficient memory."; break;
+ case paBufferTooBig: msg = "Buffer too big."; break;
+ case paBufferTooSmall: msg = "Buffer too small."; break;
+ case paNullCallback: msg = "No callback routine specified."; break;
+ case paBadStreamPtr: msg = "Invalid stream pointer."; break;
+ case paTimedOut : msg = "Wait Timed Out."; break;
+ case paInternalError: msg = "Internal PortAudio Error."; break;
+ case paDeviceUnavailable: msg = "Device Unavailable."; break;
+ default: msg = "Illegal error number."; break;
+ }
+ return msg;
+}
+
+/*
+ Get CPU Load as a fraction of total CPU time.
+ A value of 0.5 would imply that PortAudio and the sound generating
+ callback was consuming roughly 50% of the available CPU time.
+ The amount may vary depending on CPU load.
+ This function may be called from the callback function.
+*/
+double Pa_GetCPULoad( PortAudioStream* stream)
+{
+ internalPortAudioStream *past;
+ if( stream == NULL ) return (double) paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+ return past->past_Usage;
+}
+
+/*************************************************************************/
+internalPortAudioStream* PaHost_GetStreamRepresentation( PortAudioStream *stream )
+{
+ internalPortAudioStream* result = (internalPortAudioStream*) stream;
+
+ if( result == NULL || result->past_Magic != PA_MAGIC )
+ return NULL;
+ else
+ return result;
+}
+
+/*************************************************************
+** Calculate 2 LSB dither signal with a triangular distribution.
+** Ranged properly for adding to a 32 bit integer prior to >>15.
+** Range of output is +/- 32767
+*/
+#define PA_DITHER_BITS (15)
+#define PA_DITHER_SCALE (1.0f / ((1<<PA_DITHER_BITS)-1))
+long PaConvert_TriangularDither( void )
+{
+ static unsigned long previous = 0;
+ static unsigned long randSeed1 = 22222;
+ static unsigned long randSeed2 = 5555555;
+ long current, highPass;
+ /* Generate two random numbers. */
+ randSeed1 = (randSeed1 * 196314165) + 907633515;
+ randSeed2 = (randSeed2 * 196314165) + 907633515;
+ /* Generate triangular distribution about 0.
+ * Shift before adding to prevent overflow which would skew the distribution.
+ * Also shift an extra bit for the high pass filter.
+ */
+#define DITHER_SHIFT ((32 - PA_DITHER_BITS) + 1)
+ current = (((long)randSeed1)>>DITHER_SHIFT) + (((long)randSeed2)>>DITHER_SHIFT);
+ /* High pass filter to reduce audibility. */
+ highPass = current - previous;
+ previous = current;
+ return highPass;
+}
+
+/*************************************************************************
+** Called by host code.
+** Convert input from Int16, call user code, then convert output
+** to Int16 format for native use.
+** Assumes host native format is paInt16.
+** Returns result from user callback.
+*/
+long Pa_CallConvertInt16( internalPortAudioStream *past,
+ short *nativeInputBuffer,
+ short *nativeOutputBuffer )
+{
+ long temp;
+ int userResult;
+ unsigned int i;
+ void *inputBuffer = NULL;
+ void *outputBuffer = NULL;
+
+#if SUPPORT_AUDIO_CAPTURE
+ /* Get native data from DirectSound. */
+ if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
+ {
+ /* Convert from native format to PA format. */
+ unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels;
+ switch(past->past_InputSampleFormat)
+ {
+
+ case paFloat32:
+ {
+ float *inBufPtr = (float *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = nativeInputBuffer[i] * (1.0f / 32767.0f);
+ }
+ break;
+ }
+
+ case paInt32:
+ {
+ /* Convert 16 bit data to 32 bit integers */
+ int *inBufPtr = (int *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = nativeInputBuffer[i] << 16;
+ }
+ break;
+ }
+
+ case paInt16:
+ {
+ /* Already in correct format so don't copy. */
+ inputBuffer = nativeInputBuffer;
+ break;
+ }
+
+ case paInt8:
+ {
+ /* Convert 16 bit data to 8 bit chars */
+ char *inBufPtr = (char *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = (char)(nativeInputBuffer[i] >> 8);
+ }
+ }
+ else
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ temp = nativeInputBuffer[i];
+ temp += PaConvert_TriangularDither() >> 8; /* PLB20010820 */
+ temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ inBufPtr[i] = (char)(temp >> 8);
+ }
+ }
+ break;
+ }
+
+ case paUInt8:
+ {
+ /* Convert 16 bit data to 8 bit unsigned chars */
+ unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = ((unsigned char)(nativeInputBuffer[i] >> 8)) + 0x80;
+ }
+ }
+ else
+ {
+ /* If you dither then you have to clip because dithering could push the signal out of range! */
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ temp = nativeInputBuffer[i];
+ temp += PaConvert_TriangularDither() >> 8; /* PLB20010820 */
+ temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ inBufPtr[i] = (unsigned char)((temp>>8) + 0x80); /* PLB20010820 */
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+
+ /* Are we doing output time? */
+ if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
+ {
+ /* May already be in native format so just write directly to native buffer. */
+ outputBuffer = (past->past_OutputSampleFormat == paInt16) ?
+ nativeOutputBuffer : past->past_OutputBuffer;
+ }
+ /*
+ AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
+ AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
+ */
+ /* Call user callback routine. */
+ userResult = past->past_Callback(
+ inputBuffer,
+ outputBuffer,
+ past->past_FramesPerUserBuffer,
+ past->past_FrameCount,
+ past->past_UserData );
+
+ past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
+
+ /* Convert to native format if necessary. */
+ if( outputBuffer != NULL )
+ {
+ unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
+ switch(past->past_OutputSampleFormat)
+ {
+ case paFloat32:
+ {
+ float *outBufPtr = (float *) past->past_OutputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ if( past->past_Flags & paClipOff ) /* NOTHING */
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = (short) (outBufPtr[i] * (32767.0f));
+ }
+ }
+ else /* CLIP */
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ temp = (long)(outBufPtr[i] * 32767.0f);
+ *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ }
+ }
+ }
+ else
+ {
+ /* If you dither then you have to clip because dithering could push the signal out of range! */
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ float dither = PaConvert_TriangularDither()*PA_DITHER_SCALE;
+ float dithered = (outBufPtr[i] * (32767.0f)) + dither;
+ temp = (long) (dithered);
+ *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ }
+ }
+ break;
+ }
+
+ case paInt32:
+ {
+ int *outBufPtr = (int *) past->past_OutputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = (short) (outBufPtr[i] >> 16 );
+ }
+ }
+ else
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ /* Shift one bit down before dithering so that we have room for overflow from add. */
+ temp = (outBufPtr[i] >> 1) + PaConvert_TriangularDither();
+ temp = temp >> 15;
+ *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ }
+ }
+ break;
+ }
+
+ case paInt8:
+ {
+ char *outBufPtr = (char *) past->past_OutputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = ((short)outBufPtr[i]) << 8;
+ }
+ break;
+ }
+
+ case paUInt8:
+ {
+ unsigned char *outBufPtr = (unsigned char *) past->past_OutputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = ((short)(outBufPtr[i] - 0x80)) << 8;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ }
+
+ return userResult;
+}
+
+/*************************************************************************/
+PaError Pa_Initialize( void )
+{
+ if( gInitCount++ > 0 ) return paNoError;
+ ResetTraceMessages();
+ return PaHost_Init();
+}
+
+PaError Pa_Terminate( void )
+{
+ PaError result = paNoError;
+
+ if( gInitCount == 0 ) return paNoError;
+ else if( --gInitCount == 0 )
+ {
+ result = PaHost_Term();
+ DumpTraceMessages();
+ }
+ return result;
+}
+
+int PaHost_IsInitialized()
+{
+ return gInitCount;
+}
+
+/*************************************************************************/
+PaError Pa_GetSampleSize( PaSampleFormat format )
+{
+ int size;
+ switch(format )
+ {
+
+ case paUInt8:
+ case paInt8:
+ size = 1;
+ break;
+
+ case paInt16:
+ size = 2;
+ break;
+
+ case paPackedInt24:
+ size = 3;
+ break;
+
+ case paFloat32:
+ case paInt32:
+ case paInt24:
+ size = 4;
+ break;
+
+ default:
+ size = paSampleFormatNotSupported;
+ break;
+ }
+ return (PaError) size;
+}
+
+
diff --git a/pd/portaudio/pa_common/pa_trace.c b/pd/portaudio/pa_common/pa_trace.c
new file mode 100644
index 00000000..d55a6d37
--- /dev/null
+++ b/pd/portaudio/pa_common/pa_trace.c
@@ -0,0 +1,83 @@
+/*
+ * $Id: pa_trace.c,v 1.1.1.1 2002/01/22 00:52:11 phil Exp $
+ * Portable Audio I/O Library Trace Facility
+ * Store trace information in real-time for later printing.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pa_trace.h"
+
+#if TRACE_REALTIME_EVENTS
+
+static char *traceTextArray[MAX_TRACE_RECORDS];
+static int traceIntArray[MAX_TRACE_RECORDS];
+static int traceIndex = 0;
+static int traceBlock = 0;
+
+/*********************************************************************/
+void ResetTraceMessages()
+{
+ traceIndex = 0;
+}
+
+/*********************************************************************/
+void DumpTraceMessages()
+{
+ int i;
+ int numDump = (traceIndex < MAX_TRACE_RECORDS) ? traceIndex : MAX_TRACE_RECORDS;
+
+ printf("DumpTraceMessages: traceIndex = %d\n", traceIndex );
+ for( i=0; i<numDump; i++ )
+ {
+ printf("%3d: %s = 0x%08X\n",
+ i, traceTextArray[i], traceIntArray[i] );
+ }
+ ResetTraceMessages();
+ fflush(stdout);
+}
+
+/*********************************************************************/
+void AddTraceMessage( char *msg, int data )
+{
+ if( (traceIndex == MAX_TRACE_RECORDS) && (traceBlock == 0) )
+ {
+ traceBlock = 1;
+ /* DumpTraceMessages(); */
+ }
+ else if( traceIndex < MAX_TRACE_RECORDS )
+ {
+ traceTextArray[traceIndex] = msg;
+ traceIntArray[traceIndex] = data;
+ traceIndex++;
+ }
+}
+
+#endif
diff --git a/pd/portaudio/pa_common/pa_trace.h b/pd/portaudio/pa_common/pa_trace.h
new file mode 100644
index 00000000..d0fc904c
--- /dev/null
+++ b/pd/portaudio/pa_common/pa_trace.h
@@ -0,0 +1,67 @@
+#ifndef PA_TRACE_H
+#define PA_TRACE_H
+/*
+ * $Id: pa_trace.h,v 1.1.1.1 2002/01/22 00:52:11 phil Exp $
+ * Portable Audio I/O Library Trace Facility
+ * Store trace information in real-time for later printing.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+#define TRACE_REALTIME_EVENTS (0) /* Keep log of various real-time events. */
+#define MAX_TRACE_RECORDS (2048)
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+ /************************************************************************************/
+ /****************** Prototypes ******************************************************/
+ /************************************************************************************/
+
+#if TRACE_REALTIME_EVENTS
+
+ void DumpTraceMessages();
+ void ResetTraceMessages();
+ void AddTraceMessage( char *msg, int data );
+
+#else
+
+#define AddTraceMessage(msg,data) /* noop */
+#define ResetTraceMessages() /* noop */
+#define DumpTraceMessages() /* noop */
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_TRACE_H */
diff --git a/pd/portaudio/pa_common/portaudio.h b/pd/portaudio/pa_common/portaudio.h
new file mode 100644
index 00000000..06f7079b
--- /dev/null
+++ b/pd/portaudio/pa_common/portaudio.h
@@ -0,0 +1,463 @@
+#ifndef PORT_AUDIO_H
+#define PORT_AUDIO_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * $Id: portaudio.h,v 1.5 2002/03/26 18:04:22 philburk Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.audiomulch.com/portaudio/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+typedef int PaError;
+typedef enum {
+ paNoError = 0,
+
+ paHostError = -10000,
+ paInvalidChannelCount,
+ paInvalidSampleRate,
+ paInvalidDeviceId,
+ paInvalidFlag,
+ paSampleFormatNotSupported,
+ paBadIODeviceCombination,
+ paInsufficientMemory,
+ paBufferTooBig,
+ paBufferTooSmall,
+ paNullCallback,
+ paBadStreamPtr,
+ paTimedOut,
+ paInternalError,
+ paDeviceUnavailable
+} PaErrorNum;
+
+/*
+ Pa_Initialize() is the library initialisation function - call this before
+ using the library.
+
+*/
+
+PaError Pa_Initialize( void );
+
+/*
+ Pa_Terminate() is the library termination function - call this after
+ using the library.
+
+*/
+
+PaError Pa_Terminate( void );
+
+/*
+ Pa_GetHostError() returns a host specific error code.
+ This can be called after receiving a PortAudio error code of paHostError.
+
+*/
+
+long Pa_GetHostError( void );
+
+/*
+ Pa_GetErrorText() translates the supplied PortAudio error number
+ into a human readable message.
+
+*/
+
+const char *Pa_GetErrorText( PaError errnum );
+
+/*
+ Sample formats
+
+ These are formats used to pass sound data between the callback and the
+ stream. Each device has a "native" format which may be used when optimum
+ efficiency or control over conversion is required.
+
+ Formats marked "always available" are supported (emulated) by all
+ PortAudio implementations.
+
+ The floating point representation (paFloat32) uses +1.0 and -1.0 as the
+ maximum and minimum respectively.
+
+ paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
+
+*/
+
+typedef unsigned long PaSampleFormat;
+#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
+#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
+#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
+#define paInt24 ((PaSampleFormat) (1<<3))
+#define paPackedInt24 ((PaSampleFormat) (1<<4))
+#define paInt8 ((PaSampleFormat) (1<<5))
+#define paUInt8 ((PaSampleFormat) (1<<6))
+#define paCustomFormat ((PaSampleFormat) (1<<16))
+
+/*
+ Device enumeration mechanism.
+
+ Device ids range from 0 to Pa_CountDevices()-1.
+
+ Devices may support input, output or both.
+
+*/
+
+typedef int PaDeviceID;
+#define paNoDevice -1
+
+int Pa_CountDevices( void );
+
+typedef struct
+{
+ int structVersion;
+ const char *name;
+ int maxInputChannels;
+ int maxOutputChannels;
+ /* Number of discrete rates, or -1 if range supported. */
+ int numSampleRates;
+ /* Array of supported sample rates, or {min,max} if range supported. */
+ const double *sampleRates;
+ PaSampleFormat nativeSampleFormats;
+}
+PaDeviceInfo;
+
+/*
+ Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID() return the
+ default device ids for input and output respectively, or paNoDevice if
+ no device is available.
+ The result can be passed to Pa_OpenStream().
+
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+
+ The user should first determine the available device ids by using
+ the supplied application "pa_devs".
+
+*/
+
+PaDeviceID Pa_GetDefaultInputDeviceID( void );
+PaDeviceID Pa_GetDefaultOutputDeviceID( void );
+
+
+
+/*
+ Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
+ for the device specified.
+ If the device parameter is out of range the function returns NULL.
+
+ PortAudio manages the memory referenced by the returned pointer, the client
+ must not manipulate or free the memory. The pointer is only guaranteed to be
+ valid between calls to Pa_Initialize() and Pa_Terminate().
+
+*/
+
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID device );
+
+/*
+ PaTimestamp is used to represent a continuous sample clock with arbitrary
+ start time that can be used for syncronization. The type is used for the
+ outTime argument to the PortAudioCallback and as the result of Pa_StreamTime()
+
+*/
+
+typedef double PaTimestamp;
+
+/*
+ PortAudioCallback is implemented by PortAudio clients.
+
+ inputBuffer and outputBuffer are arrays of interleaved samples,
+ the format, packing and number of channels used by the buffers are
+ determined by parameters to Pa_OpenStream() (see below).
+
+ framesPerBuffer is the number of sample frames to be processed by the callback.
+
+ outTime is the time in samples when the buffer(s) processed by
+ this callback will begin being played at the audio output.
+ See also Pa_StreamTime()
+
+ userData is the value of a user supplied pointer passed to Pa_OpenStream()
+ intended for storing synthesis data etc.
+
+ return value:
+ The callback can return a non-zero value to stop the stream. This may be
+ useful in applications such as soundfile players where a specific duration
+ of output is required. However, it is not necessary to utilise this mechanism
+ as StopStream() will also terminate the stream. A callback returning a
+ non-zero value must fill the entire outputBuffer.
+
+ NOTE: None of the other stream functions may be called from within the
+ callback function except for Pa_GetCPULoad().
+
+*/
+
+typedef int (PortAudioCallback)(
+ void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+
+
+/*
+ Stream flags
+
+ These flags may be supplied (ored together) in the streamFlags argument to
+ the Pa_OpenStream() function.
+
+*/
+
+#define paNoFlag (0)
+#define paClipOff (1<<0) /* disable default clipping of out of range samples */
+#define paDitherOff (1<<1) /* disable default dithering */
+#define paPlatformSpecificFlags (0x00010000)
+typedef unsigned long PaStreamFlags;
+
+/*
+ A single PortAudioStream provides multiple channels of real-time
+ input and output audio streaming to a client application.
+ Pointers to PortAudioStream objects are passed between PortAudio functions.
+*/
+
+typedef void PortAudioStream;
+#define PaStream PortAudioStream
+
+/*
+ Pa_OpenStream() opens a stream for either input, output or both.
+
+ stream is the address of a PortAudioStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ inputDevice is the id of the device used for input (see PaDeviceID above.)
+ inputDevice may be paNoDevice to indicate that an input device is not required.
+
+ numInputChannels is the number of channels of sound to be delivered to the
+ callback. It can range from 1 to the value of maxInputChannels in the
+ PaDeviceInfo record for the device specified by the inputDevice parameter.
+ If inputDevice is paNoDevice numInputChannels is ignored.
+
+ inputSampleFormat is the sample format of inputBuffer provided to the callback
+ function. inputSampleFormat may be any of the formats described by the
+ PaSampleFormat enumeration (see above). PortAudio guarantees support for
+ the device's native formats (nativeSampleFormats in the device info record)
+ and additionally 16 and 32 bit integer and 32 bit floating point formats.
+ Support for other formats is implementation defined.
+
+ inputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ inputDriverInfo is never required for correct operation. If not used
+ inputDriverInfo should be NULL.
+
+ outputDevice is the id of the device used for output (see PaDeviceID above.)
+ outputDevice may be paNoDevice to indicate that an output device is not required.
+
+ numOutputChannels is the number of channels of sound to be supplied by the
+ callback. See the definition of numInputChannels above for more details.
+
+ outputSampleFormat is the sample format of the outputBuffer filled by the
+ callback function. See the definition of inputSampleFormat above for more
+ details.
+
+ outputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ outputDriverInfo is never required for correct operation. If not used
+ outputDriverInfo should be NULL.
+
+ sampleRate is the desired sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+
+ framesPerBuffer is the length in sample frames of all internal sample buffers
+ used for communication with platform specific audio routines. Wherever
+ possible this corresponds to the framesPerBuffer parameter passed to the
+ callback function.
+
+ numberOfBuffers is the number of buffers used for multibuffered communication
+ with the platform specific audio routines. If you pass zero, then an optimum
+ value will be chosen for you internally. This parameter is provided only
+ as a guide - and does not imply that an implementation must use multibuffered
+ i/o when reliable double buffering is available (such as SndPlayDoubleBuffer()
+ on the Macintosh.)
+
+ streamFlags may contain a combination of flags ORed together.
+ These flags modify the behaviour of the streaming process. Some flags may only
+ be relevant to certain buffer formats.
+
+ callback is a pointer to a client supplied function that is responsible
+ for processing and filling input and output buffers (see above for details.)
+
+ userData is a client supplied pointer which is passed to the callback
+ function. It could for example, contain a pointer to instance data necessary
+ for processing the audio buffers.
+
+ return value:
+ Upon success Pa_OpenStream() returns PaNoError and places a pointer to a
+ valid PortAudioStream in the stream argument. The stream is inactive (stopped).
+ If a call to Pa_OpenStream() fails a non-zero error code is returned (see
+ PaError above) and the value of stream is invalid.
+
+*/
+
+PaError Pa_OpenStream( PortAudioStream** stream,
+ PaDeviceID inputDevice,
+ int numInputChannels,
+ PaSampleFormat inputSampleFormat,
+ void *inputDriverInfo,
+ PaDeviceID outputDevice,
+ int numOutputChannels,
+ PaSampleFormat outputSampleFormat,
+ void *outputDriverInfo,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PaStreamFlags streamFlags,
+ PortAudioCallback *callback,
+ void *userData );
+
+
+/*
+ Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that opens
+ the default input and/or output devices. Most parameters have identical meaning
+ to their Pa_OpenStream() counterparts, with the following exceptions:
+
+ If either numInputChannels or numOutputChannels is 0 the respective device
+ is not opened. This has the same effect as passing paNoDevice in the device
+ arguments to Pa_OpenStream().
+
+ sampleFormat applies to both the input and output buffers.
+
+*/
+
+PaError Pa_OpenDefaultStream( PortAudioStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PortAudioCallback *callback,
+ void *userData );
+
+/*
+ Pa_CloseStream() closes an audio stream, flushing any pending buffers.
+
+*/
+
+PaError Pa_CloseStream( PortAudioStream* );
+
+/*
+ Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
+ Pa_StopStream() waits until all pending audio buffers have been played.
+ Pa_AbortStream() stops playing immediately without waiting for pending
+ buffers to complete.
+
+*/
+
+PaError Pa_StartStream( PortAudioStream *stream );
+
+PaError Pa_StopStream( PortAudioStream *stream );
+
+PaError Pa_AbortStream( PortAudioStream *stream );
+
+/*
+ Pa_StreamActive() returns one (1) when the stream is active (ie playing
+ or recording audio), zero (0) when not playing, or a negative error number
+ if the stream is invalid.
+ The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
+ but may also become inactive if the callback returns a non-zero value.
+ In the latter case, the stream is considered inactive after the last
+ buffer has finished playing.
+
+*/
+
+PaError Pa_StreamActive( PortAudioStream *stream );
+
+/*
+ Pa_StreamTime() returns the current output time in samples for the stream.
+ This time may be used as a time reference (for example synchronizing audio to
+ MIDI).
+
+*/
+
+PaTimestamp Pa_StreamTime( PortAudioStream *stream );
+
+/*
+ Pa_GetCPULoad() returns the CPU Load for the stream.
+ The "CPU Load" is a fraction of total CPU time consumed by the stream's
+ audio processing routines including, but not limited to the client supplied
+ callback.
+ A value of 0.5 would imply that PortAudio and the sound generating
+ callback was consuming roughly 50% of the available CPU time.
+ This function may be called from the callback function or the application.
+
+*/
+
+double Pa_GetCPULoad( PortAudioStream* stream );
+
+/*
+ Pa_GetMinNumBuffers() returns the minimum number of buffers required by
+ the current host based on minimum latency.
+ On the PC, for the DirectSound implementation, latency can be optionally set
+ by user by setting an environment variable.
+ For example, to set latency to 200 msec, put:
+
+ set PA_MIN_LATENCY_MSEC=200
+
+ in the AUTOEXEC.BAT file and reboot.
+ If the environment variable is not set, then the latency will be determined
+ based on the OS. Windows NT has higher latency than Win95.
+
+*/
+
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
+
+/*
+ Pa_Sleep() puts the caller to sleep for at least 'msec' milliseconds.
+ You may sleep longer than the requested time so don't rely on this for
+ accurate musical timing.
+
+ Pa_Sleep() is provided as a convenience for authors of portable code (such as
+ the tests and examples in the PortAudio distribution.)
+
+*/
+
+void Pa_Sleep( long msec );
+
+/*
+ Pa_GetSampleSize() returns the size in bytes of a single sample in the
+ supplied PaSampleFormat, or paSampleFormatNotSupported if the format is
+ no supported.
+
+*/
+
+PaError Pa_GetSampleSize( PaSampleFormat format );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORT_AUDIO_H */
diff --git a/pd/portaudio/pa_mac_core/notes.txt b/pd/portaudio/pa_mac_core/notes.txt
new file mode 100644
index 00000000..c79b90e6
--- /dev/null
+++ b/pd/portaudio/pa_mac_core/notes.txt
@@ -0,0 +1,34 @@
+Notes on Core Audio Implementation of PortAudio
+
+by Phil Burk and Darren Gibbs
+
+Document last updated March 20, 2002
+
+WHAT WORKS
+
+Output with very low latency, <10 msec.
+Half duplex input or output.
+Full duplex on the same CoreAudio device.
+The paFLoat32, paInt16, paInt8, paUInt8 sample formats.
+Pa_GetCPULoad()
+Pa_StreamTime()
+
+KNOWN BUGS OR LIMITATIONS
+
+We do not yet support simultaneous input and output on different
+devices. Note that some CoreAudio devices like the Roland UH30 look
+like one device but are actually two different CoreAudio devices. The
+BuiltIn audio is typically one CoreAudio device.
+
+Mono doesn't work.
+
+DEVICE MAPPING
+
+CoreAudio devices can support both input and output. But the sample
+rates supported may be different. So we have map one or two PortAudio
+device to each CoreAudio device depending on whether it supports
+input, output or both.
+
+When we query devices, we first get a list of CoreAudio devices. Then
+we scan the list and add a PortAudio device for each CoreAudio device
+that supports input. Then we make a scan for output devices.
diff --git a/pd/portaudio/pa_mac_core/pa_mac_core.c b/pd/portaudio/pa_mac_core/pa_mac_core.c
new file mode 100644
index 00000000..9a4b1488
--- /dev/null
+++ b/pd/portaudio/pa_mac_core/pa_mac_core.c
@@ -0,0 +1,1261 @@
+/*
+ * $Id: pa_mac_core.c,v 1.8 2002/04/12 18:07:20 philburk Exp $
+ * pa_mac_core.c
+ * Implementation of PortAudio for Mac OS X Core Audio
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Authors: Ross Bencina and Phil Burk
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * CHANGE HISTORY:
+
+ 3.29.2001 - Phil Burk - First pass... converted from Window MME code with help from Darren.
+ 3.30.2001 - Darren Gibbs - Added more support for dynamically querying device info.
+ 12.7.2001 - Gord Peters - Tweaks to compile on PA V17 and OS X 10.1
+ 2.7.2002 - Darren and Phil - fixed isInput so GetProperty works better,
+ fixed device queries for numChannels and sampleRates,
+ one CoreAudio device now maps to separate input and output PaDevices,
+ audio input works if using same CoreAudio device (some HW devices make separate CoreAudio devices).
+ 2.22.2002 - Stephane Letz - Explicit cast needed for compilation with Code Warrior 7
+ 3.19.2002 - Phil Burk - Added paInt16, paInt8, format using new "pa_common/pa_convert.c" file.
+ Return error if opened in mono mode cuz not supported.
+ Add support for Pa_GetCPULoad();
+ Fixed timestamp in callback and Pa_StreamTime() (Thanks n++k for the advice!)
+ Check for invalid sample rates and return an error.
+ Check for getenv("PA_MIN_LATEWNCY_MSEC") to set latency externally.
+ Better error checking for invalid channel counts and invalid devices.
+ 3.29.2002 - Phil Burk - Fixed Pa_GetCPULoad() for small buffers.
+ 3.31.2002 - Phil Burk - Use getrusage() instead of gettimeofday() for CPU Load calculation.
+
+TODO:
+O- how do mono output?
+O- FIFO between input and output callbacks if different devices, like in pa_mac.c
+*/
+
+#include <CoreServices/CoreServices.h>
+#include <CoreAudio/CoreAudio.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+/************************************************* Constants ********/
+
+/* To trace program, enable TRACE_REALTIME_EVENTS in pa_trace.h */
+#define PA_TRACE_RUN (0)
+#define PA_TRACE_START_STOP (1)
+
+#define PA_MIN_LATENCY_MSEC (8)
+#define MIN_TIMEOUT_MSEC (1000)
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) /**/
+#define DBUGX(x) /* PRINT(x) /**/
+
+// define value of isInput passed to CoreAudio routines
+#define IS_INPUT (true)
+#define IS_OUTPUT (false)
+
+/**************************************************************
+ * Structure for internal host specific stream data.
+ * This is allocated on a per stream basis.
+ */
+typedef struct PaHostSoundControl
+{
+ AudioDeviceID pahsc_AudioDeviceID; // Must be the same for input and output for now.
+ /* Input -------------- */
+ int pahsc_BytesPerUserNativeInputBuffer; /* native buffer size in bytes per user chunk */
+ /* Output -------------- */
+ int pahsc_BytesPerUserNativeOutputBuffer; /* native buffer size in bytes per user chunk */
+ /* Init Time -------------- */
+ int pahsc_FramesPerHostBuffer;
+ int pahsc_UserBuffersPerHostBuffer;
+ /* For measuring CPU utilization. */
+ struct rusage pahsc_EntryRusage;
+ double pahsc_InverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */
+}
+PaHostSoundControl;
+
+/**************************************************************
+ * Structure for internal extended device info.
+ * There will be one or two PortAudio devices for each Core Audio device:
+ * one input and or one output.
+ */
+typedef struct PaHostDeviceInfo
+{
+ PaDeviceInfo paInfo;
+ AudioDeviceID audioDeviceID;
+}
+PaHostDeviceInfo;
+
+/************************************************* Shared Data ********/
+/* FIXME - put Mutex around this shared data. */
+static int sNumPaDevices = 0; /* Total number of PaDeviceInfos */
+static int sNumInputDevices = 0; /* Total number of input PaDeviceInfos */
+static int sNumOutputDevices = 0;
+static PaHostDeviceInfo *sDeviceInfos = NULL;
+static int sDefaultInputDeviceID = paNoDevice;
+static int sDefaultOutputDeviceID = paNoDevice;
+static int sPaHostError = 0;
+
+static int sNumCoreDevices = 0;
+static AudioDeviceID *sCoreDeviceIDs; // Array of Core AudioDeviceIDs
+
+static const char sMapperSuffixInput[] = " - Input";
+static const char sMapperSuffixOutput[] = " - Output";
+
+/* We index the input devices first, then the output devices. */
+#define LOWEST_INPUT_DEVID (0)
+#define HIGHEST_INPUT_DEVID (sNumInputDevices - 1)
+#define LOWEST_OUTPUT_DEVID (sNumInputDevices)
+#define HIGHEST_OUTPUT_DEVID (sNumPaDevices - 1)
+
+/************************************************* Macros ********/
+
+/************************************************* Prototypes **********/
+
+static PaError Pa_QueryDevices( void );
+PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past );
+
+static int PaHost_ScanDevices( Boolean isInput );
+static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput );
+
+static PaDeviceID Pa_QueryDefaultInputDevice( void );
+static PaDeviceID Pa_QueryDefaultOutputDevice( void );
+static void PaHost_CalcHostBufferSize( internalPortAudioStream *past );
+
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+static void Pa_StartUsageCalculation( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+ /* Query user CPU timer for usage analysis and to prevent overuse of CPU. */
+ getrusage( RUSAGE_SELF, &pahsc->pahsc_EntryRusage );
+}
+
+static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB )
+{
+ long secs = timeA->tv_sec - timeB->tv_sec;
+ long usecs = secs * 1000000;
+ usecs += (timeA->tv_usec - timeB->tv_usec);
+ return usecs;
+}
+
+/******************************************************************************
+** Measure fractional CPU load based on real-time it took to calculate
+** buffers worth of output.
+*/
+static void Pa_EndUsageCalculation( internalPortAudioStream *past )
+{
+ struct rusage currentRusage;
+ long usecsElapsed;
+ double newUsage;
+
+#define LOWPASS_COEFFICIENT_0 (0.95)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+
+ if( getrusage( RUSAGE_SELF, &currentRusage ) == 0 )
+ {
+ usecsElapsed = SubtractTime_AminusB( &currentRusage.ru_utime, &pahsc->pahsc_EntryRusage.ru_utime );
+
+ /* Use inverse because it is faster than the divide. */
+ newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerHostBuffer;
+
+ past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
+ (LOWPASS_COEFFICIENT_1 * newUsage);
+ }
+}
+/****************************************** END CPU UTILIZATION *******/
+
+/************************************************************************/
+static PaDeviceID Pa_QueryDefaultInputDevice( void )
+{
+ OSStatus err = noErr;
+ UInt32 count;
+ int i;
+ AudioDeviceID tempDeviceID = kAudioDeviceUnknown;
+ PaDeviceID defaultDeviceID = paNoDevice;
+
+ // get the default output device for the HAL
+ // it is required to pass the size of the data to be returned
+ count = sizeof(tempDeviceID);
+ err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice, &count, (void *) &tempDeviceID);
+ if (err != noErr) goto Bail;
+
+ // scan input devices to see which one matches this device
+ defaultDeviceID = paNoDevice;
+ for( i=LOWEST_INPUT_DEVID; i<=HIGHEST_INPUT_DEVID; i++ )
+ {
+ DBUG(("Pa_QueryDefaultInputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID ));
+ if( sDeviceInfos[i].audioDeviceID == tempDeviceID )
+ {
+ defaultDeviceID = i;
+ break;
+ }
+ }
+Bail:
+ return defaultDeviceID;
+}
+
+/************************************************************************/
+static PaDeviceID Pa_QueryDefaultOutputDevice( void )
+{
+ OSStatus err = noErr;
+ UInt32 count;
+ int i;
+ AudioDeviceID tempDeviceID = kAudioDeviceUnknown;
+ PaDeviceID defaultDeviceID = paNoDevice;
+
+ // get the default output device for the HAL
+ // it is required to pass the size of the data to be returned
+ count = sizeof(tempDeviceID);
+ err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &count, (void *) &tempDeviceID);
+ if (err != noErr) goto Bail;
+
+ // scan output devices to see which one matches this device
+ defaultDeviceID = paNoDevice;
+ for( i=LOWEST_OUTPUT_DEVID; i<=HIGHEST_OUTPUT_DEVID; i++ )
+ {
+ DBUG(("Pa_QueryDefaultOutputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID ));
+ if( sDeviceInfos[i].audioDeviceID == tempDeviceID )
+ {
+ defaultDeviceID = i;
+ break;
+ }
+ }
+Bail:
+ return defaultDeviceID;
+}
+
+/******************************************************************/
+static PaError Pa_QueryDevices( void )
+{
+ OSStatus err = noErr;
+ UInt32 outSize;
+ Boolean outWritable;
+ int numBytes;
+
+ // find out how many Core Audio devices there are, if any
+ err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &outSize, &outWritable);
+ if (err != noErr)
+ ERR_RPT(("Couldn't get info about list of audio devices\n"));
+
+ // calculate the number of device available
+ sNumCoreDevices = outSize / sizeof(AudioDeviceID);
+
+ // Bail if there aren't any devices
+ if (sNumCoreDevices < 1)
+ ERR_RPT(("No Devices Available\n"));
+
+ // make space for the devices we are about to get
+ sCoreDeviceIDs = (AudioDeviceID *)malloc(outSize);
+
+ // get an array of AudioDeviceIDs
+ err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &outSize, (void *)sCoreDeviceIDs);
+ if (err != noErr)
+ ERR_RPT(("Couldn't get list of audio device IDs\n"));
+
+ // Allocate structures to hold device info pointers.
+ // There will be a maximum of two Pa devices per Core Audio device, input and/or output.
+ numBytes = sNumCoreDevices * 2 * sizeof(PaHostDeviceInfo);
+ sDeviceInfos = (PaHostDeviceInfo *) PaHost_AllocateFastMemory( numBytes );
+ if( sDeviceInfos == NULL ) return paInsufficientMemory;
+
+ // Scan all the Core Audio devices to see which support input and allocate a
+ // PaHostDeviceInfo structure for each one.
+ PaHost_ScanDevices( IS_INPUT );
+ sNumInputDevices = sNumPaDevices;
+ // Now scan all the output devices.
+ PaHost_ScanDevices( IS_OUTPUT );
+ sNumOutputDevices = sNumPaDevices - sNumInputDevices;
+
+ // Figure out which of the devices that we scanned is the default device.
+ sDefaultInputDeviceID = Pa_QueryDefaultInputDevice();
+ sDefaultOutputDeviceID = Pa_QueryDefaultOutputDevice();
+
+ return paNoError;
+}
+
+/************************************************************************************/
+long Pa_GetHostError()
+{
+ return sPaHostError;
+}
+
+/*************************************************************************/
+int Pa_CountDevices()
+{
+ if( sNumPaDevices <= 0 ) Pa_Initialize();
+ return sNumPaDevices;
+}
+
+/*************************************************************************/
+/* Allocate a string containing the device name. */
+static char *PaHost_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput )
+{
+ OSStatus err = noErr;
+ UInt32 outSize;
+ Boolean outWritable;
+ char *deviceName = nil;
+
+ // query size of name
+ err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyDeviceName, &outSize, &outWritable);
+ if (err == noErr)
+ {
+ deviceName = (char*)malloc( outSize + 1);
+ if( deviceName )
+ {
+ err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyDeviceName, &outSize, deviceName);
+ if (err != noErr)
+ ERR_RPT(("Couldn't get audio device name.\n"));
+ }
+ }
+
+ return deviceName;
+}
+
+/*************************************************************************/
+// An AudioStreamBasicDescription is passed in to query whether or not
+// the format is supported. A kAudioDeviceUnsupportedFormatError will
+// be returned if the format is not supported and kAudioHardwareNoError
+// will be returned if it is supported. AudioStreamBasicDescription
+// fields set to 0 will be ignored in the query, but otherwise values
+// must match exactly.
+
+Boolean deviceDoesSupportFormat(AudioDeviceID deviceID, AudioStreamBasicDescription *desc, Boolean isInput )
+{
+ OSStatus err = noErr;
+ UInt32 outSize;
+
+ outSize = sizeof(*desc);
+ err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyStreamFormatSupported, &outSize, desc);
+
+ if (err == kAudioHardwareNoError)
+ return true;
+ else
+ return false;
+}
+
+/*************************************************************************/
+// return an error string
+char* coreAudioErrorString (int errCode )
+{
+ char *str;
+
+ switch (errCode)
+ {
+ case kAudioHardwareUnspecifiedError:
+ str = "kAudioHardwareUnspecifiedError";
+ break;
+ case kAudioHardwareNotRunningError:
+ str = "kAudioHardwareNotRunningError";
+ break;
+ case kAudioHardwareUnknownPropertyError:
+ str = "kAudioHardwareUnknownPropertyError";
+ break;
+ case kAudioDeviceUnsupportedFormatError:
+ str = "kAudioDeviceUnsupportedFormatError";
+ break;
+ case kAudioHardwareBadPropertySizeError:
+ str = "kAudioHardwareBadPropertySizeError";
+ break;
+ case kAudioHardwareIllegalOperationError:
+ str = "kAudioHardwareIllegalOperationError";
+ break;
+ default:
+ str = "Unknown CoreAudio Error!";
+ break;
+ }
+
+ return str;
+}
+
+/*************************************************************************
+** PaDeviceInfo structures have already been created
+** so just return the pointer.
+**
+*/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+ if( id < 0 || id >= sNumPaDevices )
+ return NULL;
+
+ return &sDeviceInfos[id].paInfo;
+}
+
+/*************************************************************************
+** Scan all of the Core Audio devices to see which support input or output.
+** Changes sNumDevices, and fills in sDeviceInfos.
+*/
+static int PaHost_ScanDevices( Boolean isInput )
+{
+ int coreDeviceIndex;
+ int result;
+ PaHostDeviceInfo *hostDeviceInfo;
+ int numAdded = 0;
+
+ for( coreDeviceIndex=0; coreDeviceIndex<sNumCoreDevices; coreDeviceIndex++ )
+ {
+ // try to fill in next PaHostDeviceInfo
+ hostDeviceInfo = &sDeviceInfos[sNumPaDevices];
+ result = PaHost_QueryDeviceInfo( hostDeviceInfo, coreDeviceIndex, isInput );
+ DBUG(("PaHost_ScanDevices: paDevId = %d, coreDevId = %d\n", sNumPaDevices, hostDeviceInfo->audioDeviceID ));
+ if( result > 0 )
+ {
+ sNumPaDevices += 1; // bump global counter if we got one
+ numAdded += 1;
+ }
+ else if( result < 0 ) return result;
+ }
+ return numAdded;
+}
+
+/*************************************************************************
+** Try to fill in the device info for this device.
+** Return 1 if a good device that PA can use.
+** Return 0 if not appropriate
+** or return negative error.
+**
+*/
+static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput )
+{
+ OSErr err;
+ int index;
+ UInt32 outSize;
+ AudioStreamBasicDescription formatDesc;
+ Boolean result;
+ AudioDeviceID devID;
+ double *sampleRates = NULL; /* non-const ptr */
+
+ PaDeviceInfo *deviceInfo = &hostDeviceInfo->paInfo;
+ double possibleSampleRates[] = {8000.0, 11025.0, 22050.0, 44100.0, 48000.0, 88200.0, 96000.0};
+ int maxNumSampleRates = sizeof( possibleSampleRates ) / sizeof( double );
+
+ deviceInfo->structVersion = 1;
+ deviceInfo->maxInputChannels = 0;
+ deviceInfo->maxOutputChannels = 0;
+ deviceInfo->numSampleRates = -1;
+
+ devID = sCoreDeviceIDs[ coreDeviceIndex ];
+ hostDeviceInfo->audioDeviceID = devID;
+
+ // Figure out supported sample rates
+ // Make room in case device supports all rates.
+ sampleRates = (double*)PaHost_AllocateFastMemory( maxNumSampleRates * sizeof(double) );
+ if( sampleRates == NULL ) return paInsufficientMemory;
+
+ deviceInfo->sampleRates = sampleRates;
+ deviceInfo->numSampleRates = 0;
+
+ // Loop through the possible sampling rates and check each to see if the device supports it.
+ for (index = 0; index < maxNumSampleRates; index ++)
+ {
+ memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) );
+ formatDesc.mSampleRate = possibleSampleRates[index];
+ result = deviceDoesSupportFormat( devID, &formatDesc, isInput );
+
+ if (result == true)
+ {
+ deviceInfo->numSampleRates += 1;
+ *sampleRates = possibleSampleRates[index];
+ sampleRates++;
+ }
+ }
+ // If no sample rates supported, then not a very good device.
+ if( deviceInfo->numSampleRates == 0 ) goto error;
+
+ // Get data format info from the device.
+ outSize = sizeof(formatDesc);
+ err = AudioDeviceGetProperty(devID, 0, isInput, kAudioDevicePropertyStreamFormat, &outSize, &formatDesc);
+
+ // If no channels supported, then not a very good device.
+ if( (err != noErr) || (formatDesc.mChannelsPerFrame == 0) ) goto error;
+
+ if( isInput )
+ {
+ deviceInfo->maxInputChannels = formatDesc.mChannelsPerFrame;
+ }
+ else
+ {
+ deviceInfo->maxOutputChannels = formatDesc.mChannelsPerFrame;
+ }
+
+ // FIXME - where to put current sample rate?: formatDesc.mSampleRate
+
+ // Right now the Core Audio headers only define one formatID: LinearPCM
+ // Apparently LinearPCM must be Float32 for now.
+ switch (formatDesc.mFormatID)
+ {
+ case kAudioFormatLinearPCM:
+ deviceInfo->nativeSampleFormats = paFloat32;
+
+ // FIXME - details about the format are in these flags.
+ // formatDesc.mFormatFlags
+
+ // here are the possibilities
+ // kLinearPCMFormatFlagIsFloat // set for floating point, clear for integer
+ // kLinearPCMFormatFlagIsBigEndian // set for big endian, clear for little
+ // kLinearPCMFormatFlagIsSignedInteger // set for signed integer, clear for unsigned integer,
+ // only valid if kLinearPCMFormatFlagIsFloat is clear
+ // kLinearPCMFormatFlagIsPacked // set if the sample bits are packed as closely together as possible,
+ // clear if they are high or low aligned within the channel
+ // kLinearPCMFormatFlagIsAlignedHigh // set if the sample bits are placed
+ break;
+
+ default:
+ deviceInfo->nativeSampleFormats = paFloat32; // FIXME
+ break;
+ }
+
+ // Get the device name
+ deviceInfo->name = PaHost_DeviceNameFromID( devID, isInput );
+ return 1;
+
+error:
+ if( sampleRates != NULL ) free( sampleRates );
+ return 0;
+}
+
+/*************************************************************************
+** Returns recommended device ID.
+** On the PC, the recommended device can be specified by the user by
+** setting an environment variable. For example, to use device #1.
+**
+** set PA_RECOMMENDED_OUTPUT_DEVICE=1
+**
+** The user should first determine the available device ID by using
+** the supplied application "pa_devs".
+*/
+#define PA_ENV_BUF_SIZE (32)
+#define PA_REC_IN_DEV_ENV_NAME ("PA_RECOMMENDED_INPUT_DEVICE")
+#define PA_REC_OUT_DEV_ENV_NAME ("PA_RECOMMENDED_OUTPUT_DEVICE")
+
+static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName )
+{
+#if 0
+ UInt32 hresult;
+ char envbuf[PA_ENV_BUF_SIZE];
+ PaDeviceID recommendedID = paNoDevice;
+
+ /* Let user determine default device by setting environment variable. */
+ hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE );
+ if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+ {
+ recommendedID = atoi( envbuf );
+ }
+
+ return recommendedID;
+#endif
+ return paNoDevice;
+}
+
+static PaError Pa_MaybeQueryDevices( void )
+{
+ if( sNumPaDevices == 0 )
+ {
+ return Pa_QueryDevices();
+ }
+ return 0;
+}
+
+/**********************************************************************
+** Check for environment variable, else query devices and use result.
+*/
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+ PaError result;
+ result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME );
+ if( result < 0 )
+ {
+ result = Pa_MaybeQueryDevices();
+ if( result < 0 ) return result;
+ result = sDefaultInputDeviceID;
+ }
+ return result;
+}
+
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+ PaError result;
+ result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME );
+ if( result < 0 )
+ {
+ result = Pa_MaybeQueryDevices();
+ if( result < 0 ) return result;
+ result = sDefaultOutputDeviceID;
+ }
+ return result;
+}
+
+/**********************************************************************
+** Initialize Host dependant part of API.
+*/
+
+PaError PaHost_Init( void )
+{
+ return Pa_MaybeQueryDevices();
+}
+
+/**********************************************************************
+** Fill any available output buffers and use any available
+** input buffers by calling user callback.
+*/
+static PaError Pa_TimeSlice( internalPortAudioStream *past, const AudioBufferList* inInputData,
+ AudioBufferList* outOutputData )
+{
+ PaError result = 0;
+ char *inputNativeBufferfPtr = NULL;
+ char *outputNativeBufferfPtr = NULL;
+ int i;
+ int buffersProcessed = 0;
+ int done = 0;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+
+ past->past_NumCallbacks += 1;
+
+#if PA_TRACE_RUN
+ AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", past->past_NumCallbacks );
+#endif
+
+ Pa_StartUsageCalculation( past );
+
+ /* If we are using output, then we need an empty output buffer. */
+ if( past->past_NumOutputChannels > 0 )
+ {
+ outputNativeBufferfPtr = (char*)outOutputData->mBuffers[0].mData;
+ }
+
+ /* If we are using input, then we need a full input buffer. */
+ if( past->past_NumInputChannels > 0 )
+ {
+ inputNativeBufferfPtr = (char*)inInputData->mBuffers[0].mData;
+ }
+
+ buffersProcessed += 1;
+
+ /* Each host buffer contains multiple user buffers so do them all now. */
+ for( i=0; i<pahsc->pahsc_UserBuffersPerHostBuffer; i++ )
+ {
+ if( done )
+ {
+ if( outputNativeBufferfPtr )
+ {
+ /* Clear remainder of wave buffer if we are waiting for stop. */
+ AddTraceMessage("Pa_TimeSlice: zero rest of wave buffer ", i );
+ memset( outputNativeBufferfPtr, 0, pahsc->pahsc_BytesPerUserNativeOutputBuffer );
+ }
+ }
+ else
+ {
+ /* Convert 32 bit native data to user data and call user routine. */
+ result = PaConvert_Process( past, inputNativeBufferfPtr, outputNativeBufferfPtr );
+ if( result != 0) done = 1;
+ }
+ if( inputNativeBufferfPtr ) inputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeInputBuffer;
+ if( outputNativeBufferfPtr) outputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeOutputBuffer;
+ }
+
+ Pa_EndUsageCalculation( past );
+
+#if PA_TRACE_RUN
+ AddTraceMessage("Pa_TimeSlice: buffersProcessed ", buffersProcessed );
+#endif
+
+ return (result != 0) ? result : done;
+}
+
+OSStatus appIOProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow,
+ const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime,
+ AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime,
+ void* contextPtr)
+{
+
+ PaError result = 0;
+ internalPortAudioStream *past;
+ PaHostSoundControl *pahsc;
+ past = (internalPortAudioStream *) contextPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+// printf("Num input Buffers: %d; Num output Buffers: %d.\n", inInputData->mNumberBuffers, outOutputData->mNumberBuffers);
+
+ /* Has someone asked us to abort by calling Pa_AbortStream()? */
+ if( past->past_StopNow )
+ {
+ past->past_IsActive = 0; /* Will cause thread to return. */
+ }
+ /* Has someone asked us to stop by calling Pa_StopStream()
+ * OR has a user callback returned '1' to indicate finished.
+ */
+ else if( past->past_StopSoon )
+ {
+ // FIXME - pretend all done
+ past->past_IsActive = 0; /* Will cause thread to return. */
+ }
+ else
+ {
+ /* use time stamp from CoreAudio if valid */
+ if( inOutputTime->mFlags & kAudioTimeStampSampleTimeValid)
+ {
+ past->past_FrameCount = inOutputTime->mSampleTime;
+ }
+
+ /* Process full input buffer and fill up empty output buffers. */
+ if( (result = Pa_TimeSlice( past, inInputData, outOutputData )) != 0)
+ {
+ /* User callback has asked us to stop. */
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "Pa_OutputThreadProc: TimeSlice() returned ", result );
+#endif
+ past->past_StopSoon = 1; /* Request that audio play out then stop. */
+ result = paNoError;
+ }
+ }
+
+ // FIXME PaHost_UpdateStreamTime( pahsc );
+
+ return result;
+}
+
+#if 0
+static int PaHost_CalcTimeOut( internalPortAudioStream *past )
+{
+ /* Calculate timeOut longer than longest time it could take to play all buffers. */
+ int timeOut = (UInt32) (1500.0 * PaHost_GetTotalBufferFrames( past ) / past->past_SampleRate);
+ if( timeOut < MIN_TIMEOUT_MSEC ) timeOut = MIN_TIMEOUT_MSEC;
+ return timeOut;
+}
+#endif
+
+
+/*******************************************************************/
+/* Attempt to set device sample rate. */
+static PaError PaHost_SetSampleRate( AudioDeviceID devID, Boolean isInput, double sampleRate )
+{
+ AudioStreamBasicDescription formatDesc;
+ OSStatus err;
+ memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) );
+ formatDesc.mSampleRate = sampleRate;
+ err = AudioDeviceSetProperty( devID, 0, 0,
+ isInput, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc);
+
+ if (err != kAudioHardwareNoError) return paInvalidSampleRate;
+ else return paNoError;
+}
+
+/*******************************************************************/
+PaError PaHost_OpenInputStream( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ const PaHostDeviceInfo *hostDeviceInfo;
+ PaError result = paNoError;
+ UInt32 bytesPerHostBuffer;
+ UInt32 dataSize;
+ OSStatus err = noErr;
+ int bytesPerInputFrame;
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_InputDeviceID));
+ if( (past->past_InputDeviceID < LOWEST_INPUT_DEVID) ||
+ (past->past_InputDeviceID > HIGHEST_INPUT_DEVID) )
+ {
+ return paInvalidDeviceId;
+ }
+ hostDeviceInfo = &sDeviceInfos[past->past_InputDeviceID];
+
+ /* Try to set sample rate. */
+ result = PaHost_SetSampleRate( hostDeviceInfo->audioDeviceID, IS_INPUT, past->past_SampleRate );
+ if( result != paNoError ) return result;
+
+ if( past->past_NumInputChannels != hostDeviceInfo->paInfo.maxInputChannels )
+ {
+#if 1
+ return paInvalidChannelCount; // FIXME - how support mono?
+#else
+FIXME - should this be set on a stream basis? Is it possible to change?
+ /* Attempt to set number of channels. */
+ AudioStreamBasicDescription formatDesc;
+ OSStatus err;
+ memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) );
+ formatDesc.mChannelsPerFrame = past->past_NumInputChannels;
+
+ err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0,
+ IS_INPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc);
+ if (err != kAudioHardwareNoError)
+ {
+ result = paInvalidChannelCount;
+ goto error;
+ }
+#endif
+ }
+
+ // calculate native buffer sizes in bytes
+ bytesPerInputFrame = Pa_GetSampleSize(paFloat32) * past->past_NumInputChannels;
+ pahsc->pahsc_BytesPerUserNativeInputBuffer = past->past_FramesPerUserBuffer * bytesPerInputFrame;
+ bytesPerHostBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerInputFrame;
+
+ // Change the bufferSize of the device! Is this per device or just for our stream?
+ dataSize = sizeof(UInt32);
+ err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_INPUT,
+ kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer);
+ if( err != noErr )
+ {
+ ERR_RPT(("Could not force buffer size!"));
+ result = paHostError;
+ goto error;
+ }
+
+ // setup conversion procedure
+ result = PaConvert_SetupInput( past, paFloat32 );
+
+ return result;
+
+error:
+ return result;
+}
+
+/*******************************************************************/
+PaError PaHost_OpenOutputStream( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ const PaHostDeviceInfo *hostDeviceInfo;
+ PaError result = paNoError;
+ UInt32 bytesPerHostBuffer;
+ UInt32 dataSize;
+ OSStatus err = noErr;
+ int bytesPerOutputFrame;
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID));
+ if( (past->past_OutputDeviceID < LOWEST_OUTPUT_DEVID) ||
+ (past->past_OutputDeviceID > HIGHEST_OUTPUT_DEVID) )
+ {
+ return paInvalidDeviceId;
+ }
+ hostDeviceInfo = &sDeviceInfos[past->past_OutputDeviceID];
+
+ /* Try to set sample rate. */
+ result = PaHost_SetSampleRate( hostDeviceInfo->audioDeviceID, IS_OUTPUT, past->past_SampleRate );
+ if( result != paNoError ) return result;
+
+ if( past->past_NumOutputChannels != hostDeviceInfo->paInfo.maxOutputChannels )
+ {
+#if 1
+ return paInvalidChannelCount; // FIXME - how support mono?
+#else
+ /* Attempt to set number of channels. */
+ AudioStreamBasicDescription formatDesc;
+ OSStatus err;
+ memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) );
+ formatDesc.mChannelsPerFrame = past->past_NumOutputChannels;
+ err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0,
+ IS_OUTPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc);
+
+ if (err != kAudioHardwareNoError)
+ {
+ result = paInvalidChannelCount;
+ goto error;
+ }
+#endif
+ }
+
+ // calculate buffer sizes in bytes
+ bytesPerOutputFrame = Pa_GetSampleSize(paFloat32) * past->past_NumOutputChannels;
+ pahsc->pahsc_BytesPerUserNativeOutputBuffer = past->past_FramesPerUserBuffer * bytesPerOutputFrame;
+ bytesPerHostBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerOutputFrame;
+
+ // Change the bufferSize of the device! Is this per device or just for our stream?
+ dataSize = sizeof(bytesPerHostBuffer);
+ err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_OUTPUT,
+ kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer);
+ if( err != noErr )
+ {
+ ERR_RPT(("Could not force buffer size!"));
+ result = paHostError;
+ goto error;
+ }
+
+ // setup conversion procedure
+ result = PaConvert_SetupOutput( past, paFloat32 );
+
+ return result;
+
+error:
+ return result;
+}
+
+/*******************************************************************/
+PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ return pahsc->pahsc_FramesPerHostBuffer;
+}
+
+
+/*******************************************************************
+* Determine how many User Buffers we can put into our CoreAudio stream buffer.
+* Uses:
+* past->past_FramesPerUserBuffer, etc.
+* Sets:
+* past->past_NumUserBuffers
+* pahsc->pahsc_UserBuffersPerHostBuffer
+* pahsc->pahsc_FramesPerHostBuffer
+*/
+static void PaHost_CalcHostBufferSize( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = ( PaHostSoundControl *)past->past_DeviceData;
+ unsigned int minNumUserBuffers;
+
+ // Determine number of user buffers based on minimum latency.
+ minNumUserBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
+ // Compare to user requested number in user wants more than the minimum.
+ past->past_NumUserBuffers = ( minNumUserBuffers > past->past_NumUserBuffers ) ?
+ minNumUserBuffers : past->past_NumUserBuffers;
+ DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
+
+ // For CoreAudio, we only have one Host buffer, so...
+ pahsc->pahsc_UserBuffersPerHostBuffer = past->past_NumUserBuffers;
+ // Calculate size of CoreAudio buffer.
+ pahsc->pahsc_FramesPerHostBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers;
+
+ DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer ));
+ DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer ));
+}
+
+/*******************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+
+ /* Allocate and initialize host data. */
+ pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl));
+ if( pahsc == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ memset( pahsc, 0, sizeof(PaHostSoundControl) );
+ past->past_DeviceData = (void *) pahsc;
+
+ // If we are using both input and out, then they must be on the same CoreAudio device,
+ // until we implement a FIFO between two devices.
+ if( (past->past_OutputDeviceID != paNoDevice) && (past->past_InputDeviceID != paNoDevice) )
+ {
+ AudioDeviceID inputID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID;
+ AudioDeviceID outputID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID;
+ if( inputID != outputID )
+ {
+ ERR_RPT(("PortAudio: input and output must use same CoreAudio device!\n"));
+ return paInvalidDeviceId;
+ }
+ }
+
+ PaHost_CalcHostBufferSize( past );
+
+ {
+ int msecLatency = (int) ((PaHost_GetTotalBufferFrames(past) * 1000) / past->past_SampleRate);
+ PRINT(("PortAudio on OS X - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(past), msecLatency ));
+ }
+
+ /* Setup constants for CPU load measurement. */
+ pahsc->pahsc_InverseMicrosPerHostBuffer = past->past_SampleRate / (1000000.0 * pahsc->pahsc_FramesPerHostBuffer);
+
+ /* ------------------ OUTPUT */
+ if( (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0) )
+ {
+ pahsc->pahsc_AudioDeviceID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID;
+ result = PaHost_OpenOutputStream( past );
+ if( result < 0 ) goto error;
+ }
+
+ /* ------------------ INPUT */
+ if( (past->past_InputDeviceID != paNoDevice) && (past->past_NumInputChannels > 0) )
+ {
+ pahsc->pahsc_AudioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID;
+ result = PaHost_OpenInputStream( past );
+ if( result < 0 ) goto error;
+ }
+
+ return result;
+
+error:
+ PaHost_CloseStream( past );
+ return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+ return 0;
+}
+
+/*************************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+ return 0;
+}
+
+/*************************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+ OSStatus err = noErr;
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ past->past_StopSoon = 0;
+ past->past_StopNow = 0;
+ past->past_IsActive = 1;
+
+ // Associate an IO proc with the device and pass a pointer to the audio data context
+ err = AudioDeviceAddIOProc(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc, past);
+ if (err != noErr) goto error;
+
+ // start playing sound through the device
+ err = AudioDeviceStart(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc);
+ if (err != noErr) goto error;
+ return result;
+
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "PaHost_StartEngine: TimeSlice() returned ", result );
+#endif
+
+error:
+ return paHostError; // FIXME - save host error
+}
+
+/*************************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+ OSStatus err = noErr;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+ (void) abort;
+
+ /* Tell background thread to stop generating more data and to let current data play out. */
+ past->past_StopSoon = 1;
+ /* If aborting, tell background thread to stop NOW! */
+ if( abort ) past->past_StopNow = 1;
+ past->past_IsActive = 0;
+
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "PaHost_StopOutput: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut );
+#endif
+
+ // FIXME - we should ask proc to stop instead of stopping abruptly
+ err = AudioDeviceStop(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc);
+ if (err != noErr) goto Bail;
+
+ err = AudioDeviceRemoveIOProc(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc);
+ if (err != noErr) goto Bail;
+
+ return paNoError;
+
+Bail:
+ return paHostError; // FIXME - save err
+}
+
+/*************************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+ return paNoError;
+}
+
+/*******************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut );
+#endif
+
+ free( pahsc );
+ past->past_DeviceData = NULL;
+
+ return paNoError;
+}
+
+/*************************************************************************
+** Determine minimum number of buffers required for this host based
+** on minimum latency. Latency can be optionally set by user by setting
+** an environment variable. For example, to set latency to 200 msec, put:
+**
+** set PA_MIN_LATENCY_MSEC=200
+**
+** in the cshrc file.
+*/
+#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
+
+#if 1
+int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond )
+{
+ int minBuffers;
+ int denominator;
+ int minLatencyMsec = PA_MIN_LATENCY_MSEC;
+ char *minLatencyText = getenv(PA_LATENCY_ENV_NAME);
+ if( minLatencyText != NULL )
+ {
+ PRINT(("PA_MIN_LATENCY_MSEC = %s\n", minLatencyText ));
+ minLatencyMsec = atoi( minLatencyText );
+ if( minLatencyMsec < 1 ) minLatencyMsec = 1;
+ else if( minLatencyMsec > 5000 ) minLatencyMsec = 5000;
+ }
+
+ denominator = 1000.0 * framesPerBuffer;
+ minBuffers = (int) (((minLatencyMsec * framesPerSecond) + denominator - 1) / denominator );
+ if( minBuffers < 1 ) minBuffers = 1;
+ return minBuffers;
+}
+#else
+/*************************************************************************
+** Determine minimum number of buffers required for this host based
+** on minimum latency.
+*/
+int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate )
+{
+ int minUserBuffers;
+ int minFramesPerHostBuffer;
+
+ // Calculate minimum and maximum sizes based on timing and sample rate.
+ minFramesPerHostBuffer = (int) (PA_MIN_LATENCY_MSEC * sampleRate / 1000.0);
+ // round up to nearest multiple of 8
+ minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7;
+ DBUG(("Pa_GetMinNumBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer ));
+
+ minUserBuffers = (minFramesPerHostBuffer + framesPerUserBuffer - 1) / framesPerUserBuffer;
+ if( minUserBuffers < 1 ) minUserBuffers = 1;
+
+ return minUserBuffers;
+}
+#endif
+
+/*************************************************************************
+** Cleanup device info.
+*/
+PaError PaHost_Term( void )
+{
+ int i;
+
+ if( sDeviceInfos != NULL )
+ {
+ for( i=0; i<sNumPaDevices; i++ )
+ {
+ if( sDeviceInfos[i].paInfo.name != NULL ) free( (char*)sDeviceInfos[i].paInfo.name );
+ if( sDeviceInfos[i].paInfo.sampleRates != NULL ) free( (void*)sDeviceInfos[i].paInfo.sampleRates );
+ }
+ free( sDeviceInfos );
+ sDeviceInfos = NULL;
+ }
+
+ sNumPaDevices = 0;
+ return paNoError;
+}
+
+/*************************************************************************/
+void Pa_Sleep( long msec )
+{
+#if 0
+ struct timeval timeout;
+ timeout.tv_sec = msec / 1000;
+ timeout.tv_usec = (msec % 1000) * 1000;
+ select( 0, NULL, NULL, NULL, &timeout );
+#else
+ usleep( msec * 1000 );
+#endif
+}
+
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+ void *addr = malloc( numBytes ); /* FIXME - do we need physical memory? */
+ if( addr != NULL ) memset( addr, 0, numBytes );
+ return addr;
+}
+
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+ if( addr != NULL ) free( addr );
+}
+
+
+/***********************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+ return (PaError) past->past_IsActive;
+}
+
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ AudioTimeStamp timeStamp;
+ PaTimestamp streamTime;
+ PaHostSoundControl *pahsc;
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ AudioDeviceGetCurrentTime(pahsc->pahsc_AudioDeviceID, &timeStamp);
+
+ streamTime = ( timeStamp.mFlags & kAudioTimeStampSampleTimeValid) ?
+ timeStamp.mSampleTime : past->past_FrameCount;
+
+ return streamTime;
+}
diff --git a/pd/portaudio/pablio/README.txt b/pd/portaudio/pablio/README.txt
new file mode 100644
index 00000000..99c7d146
--- /dev/null
+++ b/pd/portaudio/pablio/README.txt
@@ -0,0 +1,39 @@
+README for PABLIO
+Portable Audio Blocking I/O Library
+Author: Phil Burk
+
+PABLIO is a simplified interface to PortAudio that provide
+read/write style blocking I/O.
+
+Please see the .DOC file for documentation.
+
+/*
+ * More information on PortAudio at: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+
diff --git a/pd/portaudio/pablio/pablio.c b/pd/portaudio/pablio/pablio.c
new file mode 100644
index 00000000..53dec058
--- /dev/null
+++ b/pd/portaudio/pablio/pablio.c
@@ -0,0 +1,307 @@
+/*
+ * $Id: pablio.c,v 1.1.1.1 2002-07-29 17:06:16 ggeiger Exp $
+ * pablio.c
+ * Portable Audio Blocking Input/Output utility.
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#include "ringbuffer.h"
+#include "pablio.h"
+#include <string.h>
+
+/************************************************************************/
+/******** Constants *****************************************************/
+/************************************************************************/
+
+#define FRAMES_PER_BUFFER (256)
+
+/************************************************************************/
+/******** Prototypes ****************************************************/
+/************************************************************************/
+
+static int blockingIOCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame );
+static PaError PABLIO_TermFIFO( RingBuffer *rbuf );
+
+/************************************************************************/
+/******** Functions *****************************************************/
+/************************************************************************/
+
+/* Called from PortAudio.
+ * Read and write data only if there is room in FIFOs.
+ */
+static int blockingIOCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ PABLIO_Stream *data = (PABLIO_Stream*)userData;
+ long numBytes = data->bytesPerFrame * framesPerBuffer;
+ (void) outTime;
+
+ /* This may get called with NULL inputBuffer during initial setup. */
+ if( inputBuffer != NULL )
+ {
+ RingBuffer_Write( &data->inFIFO, inputBuffer, numBytes );
+ }
+ if( outputBuffer != NULL )
+ {
+ int i;
+ int numRead = RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes );
+ /* Zero out remainder of buffer if we run out of data. */
+ for( i=numRead; i<numBytes; i++ )
+ {
+ ((char *)outputBuffer)[i] = 0;
+ }
+ }
+
+ return 0;
+}
+
+/* Allocate buffer. */
+static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame )
+{
+ long numBytes = numFrames * bytesPerFrame;
+ char *buffer = (char *) malloc( numBytes );
+ if( buffer == NULL ) return paInsufficientMemory;
+ memset( buffer, 0, numBytes );
+ return (PaError) RingBuffer_Init( rbuf, numBytes, buffer );
+}
+
+/* Free buffer. */
+static PaError PABLIO_TermFIFO( RingBuffer *rbuf )
+{
+ if( rbuf->buffer ) free( rbuf->buffer );
+ rbuf->buffer = NULL;
+ return paNoError;
+}
+
+/************************************************************
+ * Write data to ring buffer.
+ * Will not return until all the data has been written.
+ */
+long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
+{
+ long bytesWritten;
+ char *p = (char *) data;
+ long numBytes = aStream->bytesPerFrame * numFrames;
+ while( numBytes > 0)
+ {
+ bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes );
+ numBytes -= bytesWritten;
+ p += bytesWritten;
+ if( numBytes > 0) Pa_Sleep(10);
+ }
+ return numFrames;
+}
+
+/************************************************************
+ * Read data from ring buffer.
+ * Will not return until all the data has been read.
+ */
+long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
+{
+ long bytesRead;
+ char *p = (char *) data;
+ long numBytes = aStream->bytesPerFrame * numFrames;
+ while( numBytes > 0)
+ {
+ bytesRead = RingBuffer_Read( &aStream->inFIFO, p, numBytes );
+ numBytes -= bytesRead;
+ p += bytesRead;
+ if( numBytes > 0) Pa_Sleep(10);
+ }
+ return numFrames;
+}
+
+/************************************************************
+ * Return the number of frames that could be written to the stream without
+ * having to wait.
+ */
+long GetAudioStreamWriteable( PABLIO_Stream *aStream )
+{
+ int bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+ return bytesEmpty / aStream->bytesPerFrame;
+}
+
+/************************************************************
+ * Return the number of frames that are available to be read from the
+ * stream without having to wait.
+ */
+long GetAudioStreamReadable( PABLIO_Stream *aStream )
+{
+ int bytesFull = RingBuffer_GetReadAvailable( &aStream->inFIFO );
+ return bytesFull / aStream->bytesPerFrame;
+}
+
+/************************************************************/
+static unsigned long RoundUpToNextPowerOf2( unsigned long n )
+{
+ long numBits = 0;
+ if( ((n-1) & n) == 0) return n; /* Already Power of two. */
+ while( n > 0 )
+ {
+ n= n>>1;
+ numBits++;
+ }
+ return (1<<numBits);
+}
+
+/************************************************************
+ * Opens a PortAudio stream with default characteristics.
+ * Allocates PABLIO_Stream structure.
+ *
+ * flags parameter can be an ORed combination of:
+ * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
+ * and either PABLIO_MONO or PABLIO_STEREO
+ */
+PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate,
+ PaSampleFormat format, long flags )
+{
+ long bytesPerSample;
+ long doRead = 0;
+ long doWrite = 0;
+ PaError err;
+ PABLIO_Stream *aStream;
+ long minNumBuffers;
+ long numFrames;
+
+ /* Allocate PABLIO_Stream structure for caller. */
+ aStream = (PABLIO_Stream *) malloc( sizeof(PABLIO_Stream) );
+ if( aStream == NULL ) return paInsufficientMemory;
+ memset( aStream, 0, sizeof(PABLIO_Stream) );
+
+ /* Determine size of a sample. */
+ bytesPerSample = Pa_GetSampleSize( format );
+ if( bytesPerSample < 0 )
+ {
+ err = (PaError) bytesPerSample;
+ goto error;
+ }
+ aStream->samplesPerFrame = ((flags&PABLIO_MONO) != 0) ? 1 : 2;
+ aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame;
+
+ /* Initialize PortAudio */
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+
+ /* Warning: numFrames must be larger than amount of data processed per interrupt
+ * inside PA to prevent glitches. Just to be safe, adjust size upwards.
+ */
+ minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
+ numFrames = minNumBuffers * FRAMES_PER_BUFFER;
+ numFrames = RoundUpToNextPowerOf2( numFrames );
+
+ /* Initialize Ring Buffers */
+ doRead = ((flags & PABLIO_READ) != 0);
+ doWrite = ((flags & PABLIO_WRITE) != 0);
+ if(doRead)
+ {
+ err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->bytesPerFrame );
+ if( err != paNoError ) goto error;
+ }
+ if(doWrite)
+ {
+ long numBytes;
+ err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame );
+ if( err != paNoError ) goto error;
+ /* Make Write FIFO appear full initially. */
+ numBytes = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+ RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes );
+ }
+
+ /* Open a PortAudio stream that we will use to communicate with the underlying
+ * audio drivers. */
+ err = Pa_OpenStream(
+ &aStream->stream,
+ (doRead ? Pa_GetDefaultInputDeviceID() : paNoDevice),
+ (doRead ? aStream->samplesPerFrame : 0 ),
+ format,
+ NULL,
+ (doWrite ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
+ (doWrite ? aStream->samplesPerFrame : 0 ),
+ format,
+ NULL,
+ sampleRate,
+ FRAMES_PER_BUFFER,
+ minNumBuffers,
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ blockingIOCallback,
+ aStream );
+ if( err != paNoError ) goto error;
+
+ err = Pa_StartStream( aStream->stream );
+ if( err != paNoError ) goto error;
+
+ *rwblPtr = aStream;
+ return paNoError;
+
+error:
+ CloseAudioStream( aStream );
+ *rwblPtr = NULL;
+ return err;
+}
+
+/************************************************************/
+PaError CloseAudioStream( PABLIO_Stream *aStream )
+{
+ PaError err;
+ int bytesEmpty;
+ int byteSize = aStream->outFIFO.bufferSize;
+
+ /* If we are writing data, make sure we play everything written. */
+ if( byteSize > 0 )
+ {
+ bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+ while( bytesEmpty < byteSize )
+ {
+ Pa_Sleep( 10 );
+ bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+ }
+ }
+
+ err = Pa_StopStream( aStream->stream );
+ if( err != paNoError ) goto error;
+ err = Pa_CloseStream( aStream->stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+
+error:
+ PABLIO_TermFIFO( &aStream->inFIFO );
+ PABLIO_TermFIFO( &aStream->outFIFO );
+ free( aStream );
+ return err;
+}
diff --git a/pd/portaudio/pablio/pablio.def b/pd/portaudio/pablio/pablio.def
new file mode 100644
index 00000000..a10f9529
--- /dev/null
+++ b/pd/portaudio/pablio/pablio.def
@@ -0,0 +1,35 @@
+LIBRARY PABLIO
+DESCRIPTION 'PABLIO Portable Audio Blocking I/O'
+
+EXPORTS
+ ; Explicit exports can go here
+ Pa_Initialize @1
+ Pa_Terminate @2
+ Pa_GetHostError @3
+ Pa_GetErrorText @4
+ Pa_CountDevices @5
+ Pa_GetDefaultInputDeviceID @6
+ Pa_GetDefaultOutputDeviceID @7
+ Pa_GetDeviceInfo @8
+ Pa_OpenStream @9
+ Pa_OpenDefaultStream @10
+ Pa_CloseStream @11
+ Pa_StartStream @12
+ Pa_StopStream @13
+ Pa_StreamActive @14
+ Pa_StreamTime @15
+ Pa_GetCPULoad @16
+ Pa_GetMinNumBuffers @17
+ Pa_Sleep @18
+
+ OpenAudioStream @19
+ CloseAudioStream @20
+ WriteAudioStream @21
+ ReadAudioStream @22
+
+ Pa_GetSampleSize @23
+
+ ;123456789012345678901234567890123456
+ ;000000000111111111122222222223333333
+
+
diff --git a/pd/portaudio/pablio/pablio.h b/pd/portaudio/pablio/pablio.h
new file mode 100644
index 00000000..9060c560
--- /dev/null
+++ b/pd/portaudio/pablio/pablio.h
@@ -0,0 +1,108 @@
+#ifndef _PABLIO_H
+#define _PABLIO_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * $Id: pablio.h,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $
+ * PABLIO.h
+ * Portable Audio Blocking read/write utility.
+ *
+ * Author: Phil Burk, http://www.softsynth.com/portaudio/
+ *
+ * Include file for PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#include "ringbuffer.h"
+#include <string.h>
+
+typedef struct
+{
+ RingBuffer inFIFO;
+ RingBuffer outFIFO;
+ PortAudioStream *stream;
+ int bytesPerFrame;
+ int samplesPerFrame;
+}
+PABLIO_Stream;
+
+/* Values for flags for OpenAudioStream(). */
+#define PABLIO_READ (1<<0)
+#define PABLIO_WRITE (1<<1)
+#define PABLIO_READ_WRITE (PABLIO_READ|PABLIO_WRITE)
+#define PABLIO_MONO (1<<2)
+#define PABLIO_STEREO (1<<3)
+
+/************************************************************
+ * Write data to ring buffer.
+ * Will not return until all the data has been written.
+ */
+long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );
+
+/************************************************************
+ * Read data from ring buffer.
+ * Will not return until all the data has been read.
+ */
+long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );
+
+/************************************************************
+ * Return the number of frames that could be written to the stream without
+ * having to wait.
+ */
+long GetAudioStreamWriteable( PABLIO_Stream *aStream );
+
+/************************************************************
+ * Return the number of frames that are available to be read from the
+ * stream without having to wait.
+ */
+long GetAudioStreamReadable( PABLIO_Stream *aStream );
+
+/************************************************************
+ * Opens a PortAudio stream with default characteristics.
+ * Allocates PABLIO_Stream structure.
+ *
+ * flags parameter can be an ORed combination of:
+ * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
+ */
+PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate,
+ PaSampleFormat format, long flags );
+
+PaError CloseAudioStream( PABLIO_Stream *aStream );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* _PABLIO_H */
diff --git a/pd/portaudio/pablio/pablio_pd.c b/pd/portaudio/pablio/pablio_pd.c
new file mode 100644
index 00000000..e7105e9b
--- /dev/null
+++ b/pd/portaudio/pablio/pablio_pd.c
@@ -0,0 +1,335 @@
+/*
+ * $Id: pablio_pd.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $
+ * pablio.c
+ * Portable Audio Blocking Input/Output utility.
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+ /* changes by Miller Puckette to support Pd: device selection,
+ settable audio buffer size, and settable number of channels.
+ LATER also fix it to poll for input and output fifo fill points. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#include "ringbuffer.h"
+#include "pablio_pd.h" /* MSP */
+#include <string.h>
+
+ /* MSP -- FRAMES_PER_BUFFER constant removed */
+
+/************************************************************************/
+/******** Prototypes ****************************************************/
+/************************************************************************/
+
+static int blockingIOCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame );
+static PaError PABLIO_TermFIFO( RingBuffer *rbuf );
+
+/************************************************************************/
+/******** Functions *****************************************************/
+/************************************************************************/
+
+/* Called from PortAudio.
+ * Read and write data only if there is room in FIFOs.
+ */
+static int blockingIOCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ PABLIO_Stream *data = (PABLIO_Stream*)userData;
+ long numBytes = data->bytesPerFrame * framesPerBuffer;
+ (void) outTime;
+
+ /* This may get called with NULL inputBuffer during initial setup. */
+ if( inputBuffer != NULL )
+ {
+ RingBuffer_Write( &data->inFIFO, inputBuffer, numBytes );
+ }
+ if( outputBuffer != NULL )
+ {
+ int i;
+ int numRead = RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes );
+ /* Zero out remainder of buffer if we run out of data. */
+ for( i=numRead; i<numBytes; i++ )
+ {
+ ((char *)outputBuffer)[i] = 0;
+ }
+ }
+
+ return 0;
+}
+
+/* Allocate buffer. */
+static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame )
+{
+ long numBytes = numFrames * bytesPerFrame;
+ char *buffer = (char *) malloc( numBytes );
+ if( buffer == NULL ) return paInsufficientMemory;
+ memset( buffer, 0, numBytes );
+ return (PaError) RingBuffer_Init( rbuf, numBytes, buffer );
+}
+
+/* Free buffer. */
+static PaError PABLIO_TermFIFO( RingBuffer *rbuf )
+{
+ if( rbuf->buffer ) free( rbuf->buffer );
+ rbuf->buffer = NULL;
+ return paNoError;
+}
+
+/************************************************************
+ * Write data to ring buffer.
+ * Will not return until all the data has been written.
+ */
+long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
+{
+ long bytesWritten;
+ char *p = (char *) data;
+ long numBytes = aStream->bytesPerFrame * numFrames;
+ while( numBytes > 0)
+ {
+ bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes );
+ numBytes -= bytesWritten;
+ p += bytesWritten;
+ if( numBytes > 0) Pa_Sleep(10);
+ }
+ return numFrames;
+}
+
+/************************************************************
+ * Read data from ring buffer.
+ * Will not return until all the data has been read.
+ */
+long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
+{
+ long bytesRead;
+ char *p = (char *) data;
+ long numBytes = aStream->bytesPerFrame * numFrames;
+ while( numBytes > 0)
+ {
+ bytesRead = RingBuffer_Read( &aStream->inFIFO, p, numBytes );
+ numBytes -= bytesRead;
+ p += bytesRead;
+ if( numBytes > 0) Pa_Sleep(10);
+ }
+ return numFrames;
+}
+
+/************************************************************
+ * Return the number of frames that could be written to the stream without
+ * having to wait.
+ */
+long GetAudioStreamWriteable( PABLIO_Stream *aStream )
+{
+ int bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+ return bytesEmpty / aStream->bytesPerFrame;
+}
+
+/************************************************************
+ * Return the number of frames that are available to be read from the
+ * stream without having to wait.
+ */
+long GetAudioStreamReadable( PABLIO_Stream *aStream )
+{
+ int bytesFull = RingBuffer_GetReadAvailable( &aStream->inFIFO );
+ return bytesFull / aStream->bytesPerFrame;
+}
+
+/************************************************************/
+static unsigned long RoundUpToNextPowerOf2( unsigned long n )
+{
+ long numBits = 0;
+ if( ((n-1) & n) == 0) return n; /* Already Power of two. */
+ while( n > 0 )
+ {
+ n= n>>1;
+ numBits++;
+ }
+ return (1<<numBits);
+}
+
+/************************************************************
+ * Opens a PortAudio stream with default characteristics.
+ * Allocates PABLIO_Stream structure.
+ *
+ * flags parameter can be an ORed combination of:
+ * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE
+ */
+PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate,
+ PaSampleFormat format, long flags, int nchannels,
+ int framesperbuf, int nbuffers,
+ int indeviceno, int outdeviceno) /* MSP */
+{
+ long bytesPerSample;
+ long doRead = 0;
+ long doWrite = 0;
+ PaError err;
+ PABLIO_Stream *aStream;
+ long minNumBuffers;
+ long numFrames;
+
+ /* fprintf(stderr,
+ "open %lf fmt %d flags %d ch: %d fperbuf: %d nbuf: %d devs: %d %d\n",
+ sampleRate, format, flags, nchannels,
+ framesperbuf, nbuffers, indeviceno, outdeviceno); */
+
+ if (indeviceno < 0) /* MSP... */
+ {
+ indeviceno = Pa_GetDefaultInputDeviceID();
+ fprintf(stderr, "using default input device number: %d\n", indeviceno);
+ }
+ if (outdeviceno < 0)
+ {
+ outdeviceno = Pa_GetDefaultOutputDeviceID();
+ fprintf(stderr, "using default output device number: %d\n", outdeviceno);
+ }
+ nbuffers = RoundUpToNextPowerOf2(nbuffers);
+ fprintf(stderr, "nchan %d, flags %d, bufs %d, framesperbuf %d\n",
+ nchannels, flags, nbuffers, framesperbuf);
+ /* ...MSP */
+
+ /* Allocate PABLIO_Stream structure for caller. */
+ aStream = (PABLIO_Stream *) malloc( sizeof(PABLIO_Stream) );
+ if( aStream == NULL ) return paInsufficientMemory;
+ memset( aStream, 0, sizeof(PABLIO_Stream) );
+
+ /* Determine size of a sample. */
+ bytesPerSample = Pa_GetSampleSize( format );
+ if( bytesPerSample < 0 )
+ {
+ err = (PaError) bytesPerSample;
+ goto error;
+ }
+ aStream->samplesPerFrame = nchannels; /* MSP */
+ aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame;
+
+ /* Initialize PortAudio */
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+
+/* Warning: numFrames must be larger than amount of data processed per
+ interrupt inside PA to prevent glitches. */ /* MSP */
+ minNumBuffers = Pa_GetMinNumBuffers(framesperbuf, sampleRate);
+ if (minNumBuffers > nbuffers)
+ fprintf(stderr,
+ "warning: number of buffers %d less than recommended minimum %d\n",
+ (int)nbuffers, (int)minNumBuffers);
+ numFrames = nbuffers * framesperbuf;
+
+ /* Initialize Ring Buffers */
+ doRead = ((flags & PABLIO_READ) != 0);
+ doWrite = ((flags & PABLIO_WRITE) != 0);
+ if(doRead)
+ {
+ err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->bytesPerFrame );
+ if( err != paNoError ) goto error;
+ }
+ if(doWrite)
+ {
+ long numBytes;
+ err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame );
+ if( err != paNoError ) goto error;
+ /* Make Write FIFO appear full initially. */
+ numBytes = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+ RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes );
+ }
+
+ /* Open a PortAudio stream that we will use to communicate with the underlying
+ * audio drivers. */
+ err = Pa_OpenStream(
+ &aStream->stream,
+ (doRead ? indeviceno : paNoDevice), /* MSP */
+ (doRead ? aStream->samplesPerFrame : 0 ),
+ format,
+ NULL,
+ (doWrite ? outdeviceno : paNoDevice), /* MSP */
+ (doWrite ? aStream->samplesPerFrame : 0 ),
+ format,
+ NULL,
+ sampleRate,
+ framesperbuf, /* MSP */
+ nbuffers, /* MSP */
+ paNoFlag, /* MSP -- portaudio will clip for us */
+ blockingIOCallback,
+ aStream );
+ if( err != paNoError ) goto error;
+
+ err = Pa_StartStream( aStream->stream );
+ if( err != paNoError ) /* MSP */
+ {
+ fprintf(stderr, "Pa_StartStream failed; closing audio stream...\n");
+ CloseAudioStream( aStream );
+ goto error;
+ }
+
+ *rwblPtr = aStream;
+ return paNoError;
+
+error:
+ *rwblPtr = NULL;
+ return err;
+}
+
+/************************************************************/
+PaError CloseAudioStream( PABLIO_Stream *aStream )
+{
+ PaError err;
+ int bytesEmpty;
+ int byteSize = aStream->outFIFO.bufferSize;
+
+ /* If we are writing data, make sure we play everything written. */
+ if( byteSize > 0 )
+ {
+ bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+ while( bytesEmpty < byteSize )
+ {
+ Pa_Sleep( 10 );
+ bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+ }
+ }
+
+ err = Pa_StopStream( aStream->stream );
+ if( err != paNoError ) goto error;
+ err = Pa_CloseStream( aStream->stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+
+error:
+ PABLIO_TermFIFO( &aStream->inFIFO );
+ PABLIO_TermFIFO( &aStream->outFIFO );
+ free( aStream );
+ return err;
+}
diff --git a/pd/portaudio/pablio/pablio_pd.h b/pd/portaudio/pablio/pablio_pd.h
new file mode 100644
index 00000000..b87b9f5a
--- /dev/null
+++ b/pd/portaudio/pablio/pablio_pd.h
@@ -0,0 +1,110 @@
+#ifndef _PABLIO_H
+#define _PABLIO_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * $Id: pablio_pd.h,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $
+ * PABLIO.h
+ * Portable Audio Blocking read/write utility.
+ *
+ * Author: Phil Burk, http://www.softsynth.com/portaudio/
+ *
+ * Include file for PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#include "ringbuffer.h"
+#include <string.h>
+
+typedef struct
+{
+ RingBuffer inFIFO;
+ RingBuffer outFIFO;
+ PortAudioStream *stream;
+ int bytesPerFrame;
+ int samplesPerFrame;
+}
+PABLIO_Stream;
+
+/* Values for flags for OpenAudioStream(). */
+#define PABLIO_READ (1<<0)
+#define PABLIO_WRITE (1<<1)
+#define PABLIO_READ_WRITE (PABLIO_READ|PABLIO_WRITE)
+#define PABLIO_MONO (1<<2)
+#define PABLIO_STEREO (1<<3)
+
+/************************************************************
+ * Write data to ring buffer.
+ * Will not return until all the data has been written.
+ */
+long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );
+
+/************************************************************
+ * Read data from ring buffer.
+ * Will not return until all the data has been read.
+ */
+long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );
+
+/************************************************************
+ * Return the number of frames that could be written to the stream without
+ * having to wait.
+ */
+long GetAudioStreamWriteable( PABLIO_Stream *aStream );
+
+/************************************************************
+ * Return the number of frames that are available to be read from the
+ * stream without having to wait.
+ */
+long GetAudioStreamReadable( PABLIO_Stream *aStream );
+
+/************************************************************
+ * Opens a PortAudio stream with default characteristics.
+ * Allocates PABLIO_Stream structure.
+ *
+ * flags parameter can be an ORed combination of:
+ * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
+ */
+PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate,
+ PaSampleFormat format, long flags, int nchannels,
+ int framesperbuf, int nbuffers,
+ int indeviceno, int outdeviceno); /* MSP */
+
+PaError CloseAudioStream( PABLIO_Stream *aStream );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* _PABLIO_H */
diff --git a/pd/portaudio/pablio/ringbuffer.c b/pd/portaudio/pablio/ringbuffer.c
new file mode 100644
index 00000000..b8cc691f
--- /dev/null
+++ b/pd/portaudio/pablio/ringbuffer.c
@@ -0,0 +1,199 @@
+/*
+ * $Id: ringbuffer.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $
+ * ringbuffer.c
+ * Ring Buffer utility..
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "ringbuffer.h"
+#include <string.h>
+
+/***************************************************************************
+ * Initialize FIFO.
+ * numBytes must be power of 2, returns -1 if not.
+ */
+long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr )
+{
+ if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */
+ rbuf->bufferSize = numBytes;
+ rbuf->buffer = (char *)dataPtr;
+ RingBuffer_Flush( rbuf );
+ rbuf->bigMask = (numBytes*2)-1;
+ rbuf->smallMask = (numBytes)-1;
+ return 0;
+}
+/***************************************************************************
+** Return number of bytes available for reading. */
+long RingBuffer_GetReadAvailable( RingBuffer *rbuf )
+{
+ return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
+}
+/***************************************************************************
+** Return number of bytes available for writing. */
+long RingBuffer_GetWriteAvailable( RingBuffer *rbuf )
+{
+ return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf));
+}
+
+/***************************************************************************
+** Clear buffer. Should only be called when buffer is NOT being read. */
+void RingBuffer_Flush( RingBuffer *rbuf )
+{
+ rbuf->writeIndex = rbuf->readIndex = 0;
+}
+
+/***************************************************************************
+** Get address of region(s) to which we can write data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 )
+{
+ long index;
+ long available = RingBuffer_GetWriteAvailable( rbuf );
+ if( numBytes > available ) numBytes = available;
+ /* Check to see if write is not contiguous. */
+ index = rbuf->writeIndex & rbuf->smallMask;
+ if( (index + numBytes) > rbuf->bufferSize )
+ {
+ /* Write data in two blocks that wrap the buffer. */
+ long firstHalf = rbuf->bufferSize - index;
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = firstHalf;
+ *dataPtr2 = &rbuf->buffer[0];
+ *sizePtr2 = numBytes - firstHalf;
+ }
+ else
+ {
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = numBytes;
+ *dataPtr2 = NULL;
+ *sizePtr2 = 0;
+ }
+ return numBytes;
+}
+
+
+/***************************************************************************
+*/
+long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes )
+{
+ return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;
+}
+
+/***************************************************************************
+** Get address of region(s) from which we can read data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 )
+{
+ long index;
+ long available = RingBuffer_GetReadAvailable( rbuf );
+ if( numBytes > available ) numBytes = available;
+ /* Check to see if read is not contiguous. */
+ index = rbuf->readIndex & rbuf->smallMask;
+ if( (index + numBytes) > rbuf->bufferSize )
+ {
+ /* Write data in two blocks that wrap the buffer. */
+ long firstHalf = rbuf->bufferSize - index;
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = firstHalf;
+ *dataPtr2 = &rbuf->buffer[0];
+ *sizePtr2 = numBytes - firstHalf;
+ }
+ else
+ {
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = numBytes;
+ *dataPtr2 = NULL;
+ *sizePtr2 = 0;
+ }
+ return numBytes;
+}
+/***************************************************************************
+*/
+long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes )
+{
+ return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;
+}
+
+/***************************************************************************
+** Return bytes written. */
+long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes )
+{
+ long size1, size2, numWritten;
+ void *data1, *data2;
+ numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
+ if( size2 > 0 )
+ {
+
+ memcpy( data1, data, size1 );
+ data = ((char *)data) + size1;
+ memcpy( data2, data, size2 );
+ }
+ else
+ {
+ memcpy( data1, data, size1 );
+ }
+ RingBuffer_AdvanceWriteIndex( rbuf, numWritten );
+ return numWritten;
+}
+
+/***************************************************************************
+** Return bytes read. */
+long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes )
+{
+ long size1, size2, numRead;
+ void *data1, *data2;
+ numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
+ if( size2 > 0 )
+ {
+ memcpy( data, data1, size1 );
+ data = ((char *)data) + size1;
+ memcpy( data, data2, size2 );
+ }
+ else
+ {
+ memcpy( data, data1, size1 );
+ }
+ RingBuffer_AdvanceReadIndex( rbuf, numRead );
+ return numRead;
+}
diff --git a/pd/portaudio/pablio/ringbuffer.h b/pd/portaudio/pablio/ringbuffer.h
new file mode 100644
index 00000000..1bf78e3a
--- /dev/null
+++ b/pd/portaudio/pablio/ringbuffer.h
@@ -0,0 +1,101 @@
+#ifndef _RINGBUFFER_H
+#define _RINGBUFFER_H
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * $Id: ringbuffer.h,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $
+ * ringbuffer.h
+ * Ring Buffer utility..
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program is distributed with the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "ringbuffer.h"
+#include <string.h>
+
+typedef struct
+{
+ long bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */
+ long writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */
+ long readIndex; /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */
+ long bigMask; /* Used for wrapping indices with extra bit to distinguish full/empty. */
+ long smallMask; /* Used for fitting indices to buffer. */
+ char *buffer;
+}
+RingBuffer;
+/*
+ * Initialize Ring Buffer.
+ * numBytes must be power of 2, returns -1 if not.
+ */
+long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr );
+
+/* Clear buffer. Should only be called when buffer is NOT being read. */
+void RingBuffer_Flush( RingBuffer *rbuf );
+
+/* Return number of bytes available for writing. */
+long RingBuffer_GetWriteAvailable( RingBuffer *rbuf );
+/* Return number of bytes available for read. */
+long RingBuffer_GetReadAvailable( RingBuffer *rbuf );
+/* Return bytes written. */
+long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes );
+/* Return bytes read. */
+long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes );
+
+/* Get address of region(s) to which we can write data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 );
+long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes );
+
+/* Get address of region(s) from which we can read data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 );
+
+long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* _RINGBUFFER_H */
diff --git a/pd/portaudio/pablio/ringbuffer_pd.c b/pd/portaudio/pablio/ringbuffer_pd.c
new file mode 100644
index 00000000..16890d65
--- /dev/null
+++ b/pd/portaudio/pablio/ringbuffer_pd.c
@@ -0,0 +1,214 @@
+/*
+ * $Id: ringbuffer_pd.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $
+ * ringbuffer.c
+ * Ring Buffer utility..
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+/*
+ * modified 2002/07/13 by olaf.matthes@gmx.de to allow any number if channels
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "ringbuffer.h"
+#include <string.h>
+
+/***************************************************************************
+ * Initialize FIFO.
+ */
+long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr )
+{
+ rbuf->bufferSize = numBytes;
+ rbuf->buffer = (char *)dataPtr;
+ RingBuffer_Flush( rbuf );
+ return 0;
+}
+/***************************************************************************
+** Return number of bytes available for reading. */
+long RingBuffer_GetReadAvailable( RingBuffer *rbuf )
+{
+ long ret = (rbuf->writeIndex - rbuf->readIndex) + rbuf->bufferSize;
+ if (ret >= 2 * rbuf->bufferSize)
+ ret -= 2 * rbuf->bufferSize;
+ return ( ret );
+}
+/***************************************************************************
+** Return number of bytes available for writing. */
+long RingBuffer_GetWriteAvailable( RingBuffer *rbuf )
+{
+ return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf));
+}
+
+/***************************************************************************
+** Clear buffer. Should only be called when buffer is NOT being read. */
+void RingBuffer_Flush( RingBuffer *rbuf )
+{
+ rbuf->writeIndex = rbuf->readIndex = 0;
+}
+
+/***************************************************************************
+** Get address of region(s) to which we can write data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 )
+{
+ long index;
+ long available = RingBuffer_GetWriteAvailable( rbuf );
+ if( numBytes > available ) numBytes = available;
+ /* Check to see if write is not contiguous. */
+ index = rbuf->writeIndex;
+ while (index >= rbuf->bufferSize)
+ index -= rbuf->bufferSize;
+ if( (index + numBytes) > rbuf->bufferSize )
+ {
+ /* Write data in two blocks that wrap the buffer. */
+ long firstHalf = rbuf->bufferSize - index;
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = firstHalf;
+ *dataPtr2 = &rbuf->buffer[0];
+ *sizePtr2 = numBytes - firstHalf;
+ }
+ else
+ {
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = numBytes;
+ *dataPtr2 = NULL;
+ *sizePtr2 = 0;
+ }
+ return numBytes;
+}
+
+
+/***************************************************************************
+*/
+long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes )
+{
+ long ret = (rbuf->writeIndex + numBytes);
+ if ( ret > 2 * rbuf->bufferSize)
+ ret -= 2 * rbuf->bufferSize; /* check for end of buffer */
+ return rbuf->writeIndex = ret;
+}
+
+/***************************************************************************
+** Get address of region(s) from which we can read data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 )
+{
+ long index;
+ long available = RingBuffer_GetReadAvailable( rbuf );
+ if( numBytes > available ) numBytes = available;
+ /* Check to see if read is not contiguous. */
+ index = rbuf->readIndex;
+ while (index > rbuf->bufferSize)
+ index -= rbuf->bufferSize;
+
+ if( (index + numBytes) > rbuf->bufferSize )
+ {
+ /* Write data in two blocks that wrap the buffer. */
+ long firstHalf = rbuf->bufferSize - index;
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = firstHalf;
+ *dataPtr2 = &rbuf->buffer[0];
+ *sizePtr2 = numBytes - firstHalf;
+ }
+ else
+ {
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = numBytes;
+ *dataPtr2 = NULL;
+ *sizePtr2 = 0;
+ }
+ return numBytes;
+}
+/***************************************************************************
+*/
+long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes )
+{
+ long ret = (rbuf->readIndex + numBytes);
+ if( ret > 2 * rbuf->bufferSize)
+ ret -= 2 * rbuf->bufferSize;
+ return rbuf->readIndex = ret;
+}
+
+/***************************************************************************
+** Return bytes written. */
+long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes )
+{
+ long size1, size2, numWritten;
+ void *data1, *data2;
+ numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
+ if( size2 > 0 )
+ {
+
+ memcpy( data1, data, size1 );
+ data = ((char *)data) + size1;
+ memcpy( data2, data, size2 );
+ }
+ else
+ {
+ memcpy( data1, data, size1 );
+ }
+ RingBuffer_AdvanceWriteIndex( rbuf, numWritten );
+ return numWritten;
+}
+
+/***************************************************************************
+** Return bytes read. */
+long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes )
+{
+ long size1, size2, numRead;
+ void *data1, *data2;
+ numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
+ if( size2 > 0 )
+ {
+ memcpy( data, data1, size1 );
+ data = ((char *)data) + size1;
+ memcpy( data, data2, size2 );
+ }
+ else
+ {
+ memcpy( data, data1, size1 );
+ }
+ RingBuffer_AdvanceReadIndex( rbuf, numRead );
+ return numRead;
+}
diff --git a/pd/portaudio/pablio/test_rw.c b/pd/portaudio/pablio/test_rw.c
new file mode 100644
index 00000000..cf54427d
--- /dev/null
+++ b/pd/portaudio/pablio/test_rw.c
@@ -0,0 +1,99 @@
+/*
+ * $Id: test_rw.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $
+ * test_rw.c
+ * Read input from one stream and write it to another.
+ *
+ * Author: Phil Burk, http://www.softsynth.com/portaudio/
+ *
+ * This program uses PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "pablio.h"
+
+/*
+** Note that many of the older ISA sound cards on PCs do NOT support
+** full duplex audio (simultaneous record and playback).
+** And some only support full duplex at lower sample rates.
+*/
+#define SAMPLE_RATE (44100)
+#define NUM_SECONDS (5)
+#define SAMPLES_PER_FRAME (2)
+#define FRAMES_PER_BLOCK (64)
+
+/* Select whether we will use floats or shorts. */
+#if 1
+#define SAMPLE_TYPE paFloat32
+typedef float SAMPLE;
+#else
+#define SAMPLE_TYPE paInt16
+typedef short SAMPLE;
+#endif
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ int i;
+ SAMPLE samples[SAMPLES_PER_FRAME * FRAMES_PER_BLOCK];
+ PaError err;
+ PABLIO_Stream *aStream;
+
+ printf("Full duplex sound test using PortAudio and RingBuffers\n");
+ fflush(stdout);
+
+ /* Open simplified blocking I/O layer on top of PortAudio. */
+ err = OpenAudioStream( &aStream, SAMPLE_RATE, SAMPLE_TYPE,
+ (PABLIO_READ_WRITE | PABLIO_STEREO) );
+ if( err != paNoError ) goto error;
+
+ /* Process samples in the foreground. */
+ for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK )
+ {
+ /* Read one block of data into sample array from audio input. */
+ ReadAudioStream( aStream, samples, FRAMES_PER_BLOCK );
+ /* Write that same block of data to output. */
+ WriteAudioStream( aStream, samples, FRAMES_PER_BLOCK );
+ }
+
+ CloseAudioStream( aStream );
+
+ printf("Full duplex sound test complete.\n" );
+ fflush(stdout);
+ return 0;
+
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return -1;
+}
diff --git a/pd/portaudio/pablio/test_rw_echo.c b/pd/portaudio/pablio/test_rw_echo.c
new file mode 100644
index 00000000..9b27e3c2
--- /dev/null
+++ b/pd/portaudio/pablio/test_rw_echo.c
@@ -0,0 +1,123 @@
+/*
+ * $Id: test_rw_echo.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $
+ * test_rw_echo.c
+ * Echo delayed input to output.
+ *
+ * Author: Phil Burk, http://www.softsynth.com/portaudio/
+ *
+ * This program uses PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ *
+ * Note that if you need low latency, you should not use PABLIO.
+ * Use the PA_OpenStream callback technique which is lower level
+ * than PABLIO.
+ *
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "pablio.h"
+#include <string.h>
+
+/*
+** Note that many of the older ISA sound cards on PCs do NOT support
+** full duplex audio (simultaneous record and playback).
+** And some only support full duplex at lower sample rates.
+*/
+#define SAMPLE_RATE (22050)
+#define NUM_SECONDS (20)
+#define SAMPLES_PER_FRAME (2)
+
+/* Select whether we will use floats or shorts. */
+#if 1
+#define SAMPLE_TYPE paFloat32
+typedef float SAMPLE;
+#else
+#define SAMPLE_TYPE paInt16
+typedef short SAMPLE;
+#endif
+
+#define NUM_ECHO_FRAMES (2*SAMPLE_RATE)
+SAMPLE samples[NUM_ECHO_FRAMES][SAMPLES_PER_FRAME] = {0.0};
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ int i;
+ PaError err;
+ PABLIO_Stream *aInStream;
+ PABLIO_Stream *aOutStream;
+ int index;
+
+ printf("Full duplex sound test using PABLIO\n");
+ fflush(stdout);
+
+ /* Open simplified blocking I/O layer on top of PortAudio. */
+ /* Open input first so it can start to fill buffers. */
+ err = OpenAudioStream( &aInStream, SAMPLE_RATE, SAMPLE_TYPE,
+ (PABLIO_READ | PABLIO_STEREO) );
+ if( err != paNoError ) goto error;
+ /* printf("opened input\n"); fflush(stdout); /**/
+
+ err = OpenAudioStream( &aOutStream, SAMPLE_RATE, SAMPLE_TYPE,
+ (PABLIO_WRITE | PABLIO_STEREO) );
+ if( err != paNoError ) goto error;
+ /* printf("opened output\n"); fflush(stdout); /**/
+
+ /* Process samples in the foreground. */
+ index = 0;
+ for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i++ )
+ {
+ /* Write old frame of data to output. */
+ /* samples[index][1] = (i&256) * (1.0f/256.0f); /* sawtooth */
+ WriteAudioStream( aOutStream, &samples[index][0], 1 );
+
+ /* Read one frame of data into sample array for later output. */
+ ReadAudioStream( aInStream, &samples[index][0], 1 );
+ index += 1;
+ if( index >= NUM_ECHO_FRAMES ) index = 0;
+
+ if( (i & 0xFFFF) == 0 ) printf("i = %d\n", i ); fflush(stdout); /**/
+ }
+
+ CloseAudioStream( aOutStream );
+ CloseAudioStream( aInStream );
+
+ printf("R/W echo sound test complete.\n" );
+ fflush(stdout);
+ return 0;
+
+error:
+ fprintf( stderr, "An error occured while using PortAudio\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return -1;
+}
diff --git a/pd/portaudio/pablio/test_w_saw.c b/pd/portaudio/pablio/test_w_saw.c
new file mode 100644
index 00000000..b8e3e71a
--- /dev/null
+++ b/pd/portaudio/pablio/test_w_saw.c
@@ -0,0 +1,108 @@
+/*
+ * $Id: test_w_saw.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $
+ * test_w_saw.c
+ * Generate stereo sawtooth waveforms.
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ *
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "pablio.h"
+#include <string.h>
+
+#define SAMPLE_RATE (44100)
+#define NUM_SECONDS (6)
+#define SAMPLES_PER_FRAME (2)
+
+#define FREQUENCY (220.0f)
+#define PHASE_INCREMENT (2.0f * FREQUENCY / SAMPLE_RATE)
+#define FRAMES_PER_BLOCK (100)
+
+float samples[FRAMES_PER_BLOCK][SAMPLES_PER_FRAME];
+float phases[SAMPLES_PER_FRAME];
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ int i,j;
+ PaError err;
+ PABLIO_Stream *aOutStream;
+
+ printf("Generate sawtooth waves using PABLIO.\n");
+ fflush(stdout);
+
+ /* Open simplified blocking I/O layer on top of PortAudio. */
+ err = OpenAudioStream( &aOutStream, SAMPLE_RATE, paFloat32,
+ (PABLIO_WRITE | PABLIO_STEREO) );
+ if( err != paNoError ) goto error;
+
+ /* Initialize oscillator phases. */
+ phases[0] = 0.0;
+ phases[1] = 0.0;
+
+ for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK )
+ {
+ /* Generate sawtooth waveforms in a block for efficiency. */
+ for( j=0; j<FRAMES_PER_BLOCK; j++ )
+ {
+ /* Generate a sawtooth wave by incrementing a variable. */
+ phases[0] += PHASE_INCREMENT;
+ /* The signal range is -1.0 to +1.0 so wrap around if we go over. */
+ if( phases[0] > 1.0f ) phases[0] -= 2.0f;
+ samples[j][0] = phases[0];
+
+ /* On the second channel, generate a sawtooth wave a fifth higher. */
+ phases[1] += PHASE_INCREMENT * (3.0f / 2.0f);
+ if( phases[1] > 1.0f ) phases[1] -= 2.0f;
+ samples[j][1] = phases[1];
+ }
+
+ /* Write samples to output. */
+ WriteAudioStream( aOutStream, samples, FRAMES_PER_BLOCK );
+ }
+
+ CloseAudioStream( aOutStream );
+
+ printf("Sawtooth sound test complete.\n" );
+ fflush(stdout);
+ return 0;
+
+error:
+ fprintf( stderr, "An error occured while using PABLIO\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return -1;
+}
diff --git a/pd/portaudio/pablio/test_w_saw8.c b/pd/portaudio/pablio/test_w_saw8.c
new file mode 100644
index 00000000..b876bd9f
--- /dev/null
+++ b/pd/portaudio/pablio/test_w_saw8.c
@@ -0,0 +1,106 @@
+/*
+ * $Id: test_w_saw8.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $
+ * test_w_saw8.c
+ * Generate stereo 8 bit sawtooth waveforms.
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ *
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "pablio.h"
+#include <string.h>
+
+#define SAMPLE_RATE (22050)
+#define NUM_SECONDS (6)
+#define SAMPLES_PER_FRAME (2)
+
+
+#define FRAMES_PER_BLOCK (100)
+
+unsigned char samples[FRAMES_PER_BLOCK][SAMPLES_PER_FRAME];
+unsigned char phases[SAMPLES_PER_FRAME];
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ int i,j;
+ PaError err;
+ PABLIO_Stream *aOutStream;
+
+ printf("Generate unsigned 8 bit sawtooth waves using PABLIO.\n");
+ fflush(stdout);
+
+ /* Open simplified blocking I/O layer on top of PortAudio. */
+ err = OpenAudioStream( &aOutStream, SAMPLE_RATE, paUInt8,
+ (PABLIO_WRITE | PABLIO_STEREO) );
+ if( err != paNoError ) goto error;
+
+ /* Initialize oscillator phases to "ground" level for paUInt8. */
+ phases[0] = 128;
+ phases[1] = 128;
+
+ for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK )
+ {
+ /* Generate sawtooth waveforms in a block for efficiency. */
+ for( j=0; j<FRAMES_PER_BLOCK; j++ )
+ {
+ /* Generate a sawtooth wave by incrementing a variable. */
+ phases[0] += 1;
+ /* We don't have to do anything special to wrap when using paUint8 because
+ * 8 bit arithmetic automatically wraps. */
+ samples[j][0] = phases[0];
+
+ /* On the second channel, generate a higher sawtooth wave. */
+ phases[1] += 3;
+ samples[j][1] = phases[1];
+ }
+
+ /* Write samples to output. */
+ WriteAudioStream( aOutStream, samples, FRAMES_PER_BLOCK );
+ }
+
+ CloseAudioStream( aOutStream );
+
+ printf("Sawtooth sound test complete.\n" );
+ fflush(stdout);
+ return 0;
+
+error:
+ fprintf( stderr, "An error occured while using PABLIO\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return -1;
+}
diff --git a/pd/portaudio/pablio/test_w_saw_pd.c b/pd/portaudio/pablio/test_w_saw_pd.c
new file mode 100644
index 00000000..be95d245
--- /dev/null
+++ b/pd/portaudio/pablio/test_w_saw_pd.c
@@ -0,0 +1,108 @@
+/*
+ * $Id: test_w_saw_pd.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $
+ * test_w_saw.c
+ * Generate stereo sawtooth waveforms.
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ *
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "pablio_pd.h"
+#include <string.h>
+
+#define SAMPLE_RATE (44100)
+#define NUM_SECONDS (6)
+#define SAMPLES_PER_FRAME (2)
+
+#define FREQUENCY (220.0f)
+#define PHASE_INCREMENT (2.0f * FREQUENCY / SAMPLE_RATE)
+#define FRAMES_PER_BLOCK (100)
+
+float samples[FRAMES_PER_BLOCK][SAMPLES_PER_FRAME];
+float phases[SAMPLES_PER_FRAME];
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ int i,j;
+ PaError err;
+ PABLIO_Stream *aOutStream;
+
+ printf("Generate sawtooth waves using PABLIO.\n");
+ fflush(stdout);
+
+ /* Open simplified blocking I/O layer on top of PortAudio. */
+ err = OpenAudioStream( &aOutStream, SAMPLE_RATE, paFloat32,
+ PABLIO_WRITE, 2, 512, 8, -1, -1 );
+ if( err != paNoError ) goto error;
+
+ /* Initialize oscillator phases. */
+ phases[0] = 0.0;
+ phases[1] = 0.0;
+
+ for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK )
+ {
+ /* Generate sawtooth waveforms in a block for efficiency. */
+ for( j=0; j<FRAMES_PER_BLOCK; j++ )
+ {
+ /* Generate a sawtooth wave by incrementing a variable. */
+ phases[0] += PHASE_INCREMENT;
+ /* The signal range is -1.0 to +1.0 so wrap around if we go over. */
+ if( phases[0] > 1.0f ) phases[0] -= 2.0f;
+ samples[j][0] = phases[0];
+
+ /* On the second channel, generate a sawtooth wave a fifth higher. */
+ phases[1] += PHASE_INCREMENT * (3.0f / 2.0f);
+ if( phases[1] > 1.0f ) phases[1] -= 2.0f;
+ samples[j][1] = phases[1];
+ }
+
+ /* Write samples to output. */
+ WriteAudioStream( aOutStream, samples, FRAMES_PER_BLOCK );
+ }
+
+ CloseAudioStream( aOutStream );
+
+ printf("Sawtooth sound test complete.\n" );
+ fflush(stdout);
+ return 0;
+
+error:
+ fprintf( stderr, "An error occured while using PABLIO\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return -1;
+}
diff --git a/pd/portaudio/portmidi-macosx/Makefile b/pd/portaudio/portmidi-macosx/Makefile
new file mode 100644
index 00000000..d8667355
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/Makefile
@@ -0,0 +1,24 @@
+CC = cc
+CFLAGS = -Wmost
+LDFLAGS = -framework Carbon -framework CoreMIDI
+OBJS = ptdarwin.o pmutil.o pmmacosx.o pmdarwin.o portmidi.o
+LIBS =
+
+all: libportmidi.a pmtest
+
+libportmidi.a: portmidi.h porttime.h pminternal.h $(OBJS)
+ rm -f libportmidi.a
+ ar rv libportmidi.a $(OBJS)
+ ranlib libportmidi.a
+
+pmtest: pmtest.c libportmidi.a
+ $(CC) $(CFLAGS) pmtest.c $(OBJS) -o pmtest $(LDFLAGS) $(LIBS)
+
+pmmacosx.o: pmmacosx.c portmidi.h pminternal.h pmmacosx.h porttime.h
+pmdarwin.o: pmdarwin.c portmidi.h pmmacosx.h
+pmutil.o: pmutil.c portmidi.h pmutil.h pminternal.h
+portmidi.o: portmidi.c portmidi.h pminternal.h
+ptdarwin.o: ptdarwin.c porttime.h portmidi.h
+
+clean:
+ rm -f pmtest *.o
diff --git a/pd/portaudio/portmidi-macosx/README b/pd/portaudio/portmidi-macosx/README
new file mode 100644
index 00000000..89c0e6fa
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/README
@@ -0,0 +1,12 @@
+PortMidi for MacOS X / Darwin
+Jon Parise <jparise@cmu.edu>
+$Date: 2002-07-29 17:06:16 $
+
+This is the MacOS X / Darwin port of the PortMidi library from the Carnegie
+Mellon Computer Music Group. It is based on the Apple CoreAudio MIDI
+interface.
+
+This port was finished in early 2002. At this point, I consider the code
+base complete.
+
+- Jon
diff --git a/pd/portaudio/portmidi-macosx/pmdarwin.c b/pd/portaudio/portmidi-macosx/pmdarwin.c
new file mode 100644
index 00000000..510339c3
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmdarwin.c
@@ -0,0 +1,36 @@
+/*
+ * PortMidi OS-dependent interface for Darwin (MacOS X)
+ * Jon Parise <jparise@cmu.edu>
+ *
+ * $Id: pmdarwin.c,v 1.1.1.1 2002-07-29 17:06:16 ggeiger Exp $
+ */
+
+/*
+ * This file only needs to implement pm_init(), which calls various
+ * routines to register the available midi devices. This file must
+ * be separate from the main portmidi.c file because it is system
+ * dependent, and it is separate from, say, pmwinmm.c, because it
+ * might need to register devices for winmm, directx, and others.
+ */
+
+#include <stdlib.h>
+#include "portmidi.h"
+#include "pmmacosx.h"
+
+PmError pm_init()
+{
+ return pm_macosx_init();
+}
+
+PmError pm_term()
+{
+ return pm_macosx_term();
+}
+
+PmDeviceID Pm_GetDefaultInputDeviceID() { return 0; };
+PmDeviceID Pm_GetDefaultOutputDeviceID() { return 0; };
+
+void *pm_alloc(size_t s) { return malloc(s); }
+
+void pm_free(void *ptr) { free(ptr); }
+
diff --git a/pd/portaudio/portmidi-macosx/pminternal.h b/pd/portaudio/portmidi-macosx/pminternal.h
new file mode 100644
index 00000000..2a92e16d
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pminternal.h
@@ -0,0 +1,100 @@
+/* pminternal.h -- header for interface implementations */
+
+/* this file is included by files that implement library internals */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/* these are defined in system-specific file */
+void *pm_alloc(size_t s);
+void pm_free(void *ptr);
+
+struct pm_internal_struct;
+
+/* these do not use PmInternal because it is not defined yet... */
+typedef PmError (*pm_write_fn)(struct pm_internal_struct *midi,
+ PmEvent *buffer, long length);
+typedef PmError (*pm_open_fn)(struct pm_internal_struct *midi,
+ void *driverInfo);
+typedef PmError (*pm_abort_fn)(struct pm_internal_struct *midi);
+typedef PmError (*pm_close_fn)(struct pm_internal_struct *midi);
+
+typedef struct {
+ pm_write_fn write;
+ pm_open_fn open;
+ pm_abort_fn abort;
+ pm_close_fn close;
+} pm_fns_node, *pm_fns_type;
+
+/* when open fails, the dictionary gets this set of functions: */
+extern pm_fns_node pm_none_dictionary;
+
+typedef struct {
+ PmDeviceInfo pub;
+ void *descriptor; /* system-specific data to open device */
+ pm_fns_type dictionary;
+} descriptor_node, *descriptor_type;
+
+
+#define pm_descriptor_max 32
+extern descriptor_node descriptors[pm_descriptor_max];
+extern int descriptor_index;
+
+
+typedef unsigned long (*time_get_proc_type)(void *time_info);
+
+typedef struct pm_internal_struct {
+ short write_flag; /* MIDI_IN, or MIDI_OUT */
+ int device_id; /* which device is open (index to descriptors) */
+ PmTimeProcPtr time_proc; /* where to get the time */
+ void *time_info; /* pass this to get_time() */
+ PmEvent *buffer; /* input or output buffer */
+ long buffer_len; /* how big is the buffer */
+ long latency; /* time delay in ms between timestamps and actual output */
+ /* set to zero to get immediate, simple blocking output */
+ /* if latency is zero, timestamps will be ignored */
+ int overflow; /* set to non-zero if input is dropped */
+ int flush; /* flag to drop incoming sysex data because of overflow */
+ int sysex_in_progress; /* use for overflow management */
+ struct pm_internal_struct *thru;
+ PmTimestamp last_msg_time; /* timestamp of last message */
+ long head;
+ long tail;
+ pm_fns_type dictionary; /* implementation functions */
+ void *descriptor; /* system-dependent state */
+} PmInternal;
+
+
+typedef struct {
+ long head;
+ long tail;
+ long len;
+ long msg_size;
+ long overflow;
+ char *buffer;
+} PmQueueRep;
+
+
+PmError pm_init(void); /* defined in a system-specific file */
+PmError pm_term(void); /* defined in a system-specific file */
+int pm_in_device(int n, char *interf, char *device);
+int pm_out_device(int n, char *interf, char *device);
+PmError none_write(PmInternal *midi, PmEvent *buffer, long length);
+PmError pm_success_fn(PmInternal *midi);
+PmError pm_fail_fn(PmInternal *midi);
+long pm_in_poll(PmInternal *midi);
+long pm_out_poll(PmInternal *midi);
+
+PmError pm_add_device(char *interf, char *name, int input, void *descriptor,
+ pm_fns_type dictionary);
+
+void pm_enqueue(PmInternal *midi, PmEvent *event);
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/pd/portaudio/portmidi-macosx/pmmacosx.c b/pd/portaudio/portmidi-macosx/pmmacosx.c
new file mode 100644
index 00000000..0aafcf7f
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmmacosx.c
@@ -0,0 +1,336 @@
+/*
+ * Platform interface to the MacOS X CoreMIDI framework
+ *
+ * Jon Parise <jparise@cmu.edu>
+ *
+ * $Id: pmmacosx.c,v 1.1.1.1 2002-07-29 17:06:16 ggeiger Exp $
+ */
+
+#include "portmidi.h"
+#include "pminternal.h"
+#include "porttime.h"
+#include "pmmacosx.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <CoreServices/CoreServices.h>
+#include <CoreMIDI/MIDIServices.h>
+
+#define PACKET_BUFFER_SIZE 1024
+
+static MIDIClientRef client = NULL; /* Client handle to the MIDI server */
+static MIDIPortRef portIn = NULL; /* Input port handle */
+static MIDIPortRef portOut = NULL; /* Output port handle */
+
+extern pm_fns_node pm_macosx_in_dictionary;
+extern pm_fns_node pm_macosx_out_dictionary;
+
+static int
+midi_length(long msg)
+{
+ int status, high, low;
+ static int high_lengths[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, /* 0x00 through 0x70 */
+ 3, 3, 3, 3, 2, 2, 3, 1 /* 0x80 through 0xf0 */
+ };
+ static int low_lengths[] = {
+ 1, 1, 3, 2, 1, 1, 1, 1, /* 0xf0 through 0xf8 */
+ 1, 1, 1, 1, 1, 1, 1, 1 /* 0xf9 through 0xff */
+ };
+
+ status = msg & 0xFF;
+ high = status >> 4;
+ low = status & 15;
+
+ return (high != 0xF0) ? high_lengths[high] : low_lengths[low];
+}
+
+static PmTimestamp
+get_timestamp(PmInternal *midi)
+{
+ PmTimeProcPtr time_proc;
+
+ /* Set the time procedure accordingly */
+ time_proc = midi->time_proc;
+ if (time_proc == NULL) {
+ time_proc = Pt_Time;
+ }
+
+ return (*time_proc)(midi->time_info);
+}
+
+/* called when MIDI packets are received */
+static void
+readProc(const MIDIPacketList *newPackets, void *refCon, void *connRefCon)
+{
+ PmInternal *midi;
+ PmEvent event;
+ MIDIPacket *packet;
+ unsigned int packetIndex;
+
+ /* Retrieve the context for this connection */
+ midi = (PmInternal *) connRefCon;
+
+ packet = (MIDIPacket *) &newPackets->packet[0];
+ for (packetIndex = 0; packetIndex < newPackets->numPackets; packetIndex++) {
+
+ /* Build the PmMessage for the PmEvent structure */
+ switch (packet->length) {
+ case 1:
+ event.message = Pm_Message(packet->data[0], 0, 0);
+ break;
+ case 2:
+ event.message = Pm_Message(packet->data[0], packet->data[1], 0);
+ break;
+ case 3:
+ event.message = Pm_Message(packet->data[0], packet->data[1],
+ packet->data[2]);
+ break;
+ default:
+ /* Skip packets that are too large to fit in a PmMessage */
+ continue;
+ }
+
+ /* Set the timestamp and dispatch this message */
+ event.timestamp = get_timestamp(midi);
+ pm_enqueue(midi, &event);
+
+ /* Advance to the next packet in the packet list */
+ packet = MIDIPacketNext(packet);
+ }
+}
+
+static PmError
+midi_in_open(PmInternal *midi, void *driverInfo)
+{
+ MIDIEndpointRef endpoint;
+
+ endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor;
+ if (endpoint == NULL) {
+ return pmInvalidDeviceId;
+ }
+
+ if (MIDIPortConnectSource(portIn, endpoint, midi) != noErr) {
+ return pmHostError;
+ }
+
+ return pmNoError;
+}
+
+static PmError
+midi_in_close(PmInternal *midi)
+{
+ MIDIEndpointRef endpoint;
+
+ endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor;
+ if (endpoint == NULL) {
+ return pmInvalidDeviceId;
+ }
+
+ if (MIDIPortDisconnectSource(portIn, endpoint) != noErr) {
+ return pmHostError;
+ }
+
+ return pmNoError;
+}
+
+static PmError
+midi_out_open(PmInternal *midi, void *driverInfo)
+{
+ /*
+ * MIDISent() only requires an output port (portOut) and a valid MIDI
+ * endpoint (which we've already created and stored in the PmInternal
+ * structure). Therefore, no additional work needs to be done here to
+ * open the device for output.
+ */
+
+ return pmNoError;
+}
+
+static PmError
+midi_out_close(PmInternal *midi)
+{
+ return pmNoError;
+}
+
+static PmError
+midi_abort(PmInternal *midi)
+{
+ return pmNoError;
+}
+
+static PmError
+midi_write(PmInternal *midi, PmEvent *events, long length)
+{
+ Byte packetBuffer[PACKET_BUFFER_SIZE];
+ MIDIEndpointRef endpoint;
+ MIDIPacketList *packetList;
+ MIDIPacket *packet;
+ MIDITimeStamp timestamp;
+ PmTimeProcPtr time_proc;
+ PmEvent event;
+ unsigned int pm_time;
+ unsigned int eventIndex;
+ unsigned int messageLength;
+ Byte message[3];
+
+ endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor;
+ if (endpoint == NULL) {
+ return pmInvalidDeviceId;
+ }
+
+ /* Make sure the packetBuffer is large enough */
+ if (length > PACKET_BUFFER_SIZE) {
+ return pmHostError;
+ }
+
+ /*
+ * Initialize the packet list. Each packet contains bytes that are to
+ * be played at the same time.
+ */
+ packetList = (MIDIPacketList *) packetBuffer;
+ if ((packet = MIDIPacketListInit(packetList)) == NULL) {
+ return pmHostError;
+ }
+
+ /* Set the time procedure accordingly */
+ time_proc = midi->time_proc;
+ if (time_proc == NULL) {
+ time_proc = Pt_Time;
+ }
+
+ /* Extract the event data and pack it into the message buffer */
+ for (eventIndex = 0; eventIndex < length; eventIndex++) {
+ event = events[eventIndex];
+
+ /* Compute the timestamp */
+ pm_time = (*time_proc)(midi->time_info);
+ timestamp = pm_time + midi->latency;
+
+ messageLength = midi_length(event.message);
+ message[0] = Pm_MessageStatus(event.message);
+ message[1] = Pm_MessageData1(event.message);
+ message[2] = Pm_MessageData2(event.message);
+
+ /* Add this message to the packet list */
+ packet = MIDIPacketListAdd(packetList, sizeof(packetBuffer), packet,
+ timestamp, messageLength, message);
+ if (packet == NULL) {
+ return pmHostError;
+ }
+ }
+
+ if (MIDISend(portOut, endpoint, packetList) != noErr) {
+ return pmHostError;
+ }
+
+ return pmNoError;
+}
+
+pm_fns_node pm_macosx_in_dictionary = {
+ none_write,
+ midi_in_open,
+ midi_abort,
+ midi_in_close
+};
+
+pm_fns_node pm_macosx_out_dictionary = {
+ midi_write,
+ midi_out_open,
+ midi_abort,
+ midi_out_close
+};
+
+PmError
+pm_macosx_init(void)
+{
+ OSStatus status;
+ ItemCount numDevices, numInputs, numOutputs;
+ MIDIEndpointRef endpoint;
+ CFStringEncoding defaultEncoding;
+ CFStringRef deviceName;
+ char nameBuf[256];
+ int i;
+
+ /* Determine the number of MIDI devices on the system */
+ numDevices = MIDIGetNumberOfDevices();
+ numInputs = MIDIGetNumberOfSources();
+ numOutputs = MIDIGetNumberOfDestinations();
+
+ /* Return prematurely if no devices exist on the system */
+ if (numDevices <= 0) {
+ return pmHostError;
+ }
+
+ /* Determine the default system character encording */
+ defaultEncoding = CFStringGetSystemEncoding();
+
+ /* Iterate over the MIDI input devices */
+ for (i = 0; i < numInputs; i++) {
+ endpoint = MIDIGetSource(i);
+ if (endpoint == NULL) {
+ continue;
+ }
+
+ /* Get the name of this device */
+ MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &deviceName);
+ CFStringGetCString(deviceName, nameBuf, 256, defaultEncoding);
+ CFRelease(deviceName);
+
+ /* Register this device with PortMidi */
+ pm_add_device("CoreMIDI", nameBuf, TRUE, (void *)endpoint,
+ &pm_macosx_in_dictionary);
+ }
+
+ /* Iterate over the MIDI output devices */
+ for (i = 0; i < numOutputs; i++) {
+ endpoint = MIDIGetDestination(i);
+ if (endpoint == NULL) {
+ continue;
+ }
+
+ /* Get the name of this device */
+ MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &deviceName);
+ CFStringGetCString(deviceName, nameBuf, 256, defaultEncoding);
+ CFRelease(deviceName);
+
+ /* Register this device with PortMidi */
+ pm_add_device("CoreMIDI", nameBuf, FALSE, (void *)endpoint,
+ &pm_macosx_out_dictionary);
+ }
+
+ /* Initialize the client handle */
+ status = MIDIClientCreate(CFSTR("PortMidi"), NULL, NULL, &client);
+ if (status != noErr) {
+ fprintf(stderr, "Could not initialize client: %d\n", (int)status);
+ return pmHostError;
+ }
+
+ /* Create the input port */
+ status = MIDIInputPortCreate(client, CFSTR("Input port"), readProc, NULL,
+ &portIn);
+ if (status != noErr) {
+ fprintf(stderr, "Could not create input port: %d\n", (int)status);
+ return pmHostError;
+ }
+
+ /* Create the output port */
+ status = MIDIOutputPortCreate(client, CFSTR("Output port"), &portOut);
+ if (status != noErr) {
+ fprintf(stderr, "Could not create output port: %d\n", (int)status);
+ return pmHostError;
+ }
+
+ return pmNoError;
+}
+
+PmError
+pm_macosx_term(void)
+{
+ if (client != NULL) MIDIClientDispose(client);
+ if (portIn != NULL) MIDIPortDispose(portIn);
+ if (portOut != NULL) MIDIPortDispose(portOut);
+
+ return pmNoError;
+}
diff --git a/pd/portaudio/portmidi-macosx/pmmacosx.h b/pd/portaudio/portmidi-macosx/pmmacosx.h
new file mode 100644
index 00000000..15e9551d
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmmacosx.h
@@ -0,0 +1,4 @@
+/* system-specific definitions */
+
+PmError pm_macosx_init(void);
+PmError pm_macosx_term(void);
diff --git a/pd/portaudio/portmidi-macosx/pmtest b/pd/portaudio/portmidi-macosx/pmtest
new file mode 100644
index 00000000..8adc5334
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmtest
Binary files differ
diff --git a/pd/portaudio/portmidi-macosx/pmtest.c b/pd/portaudio/portmidi-macosx/pmtest.c
new file mode 100644
index 00000000..5628d25e
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmtest.c
@@ -0,0 +1,136 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "portmidi.h"
+#include "porttime.h"
+#include "pminternal.h"
+
+#define LATENCY 0
+#define NUM_ECHOES 10
+
+int
+main()
+{
+ int i = 0;
+ int n = 0;
+ PmStream *midi_in;
+ PmStream *midi_out;
+ PmError err;
+ char line[80];
+ PmEvent buffer[NUM_ECHOES];
+ int transpose;
+ int delay;
+ int status, data1, data2;
+ int statusprefix;
+
+
+
+ /* always start the timer before you start midi */
+ Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */
+
+
+ for (i = 0; i < Pm_CountDevices(); i++) {
+ const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
+ printf("%d: %s, %s", i, info->interf, info->name);
+ if (info->input) printf(" (input)");
+ if (info->output) printf(" (output)");
+ printf("\n");
+ }
+
+ /* OPEN INPUT DEVICE */
+
+ printf("Type input number: ");
+ while (n != 1) {
+ n = scanf("%d", &i);
+ gets(line);
+ }
+
+ err = Pm_OpenInput(&midi_in, i, NULL, 100, NULL, NULL, NULL);
+ if (err) {
+ printf("could not open midi device: %s\n", Pm_GetErrorText(err));
+ exit(1);
+ }
+ printf("Midi Input opened.\n");
+
+ /* OPEN OUTPUT DEVICE */
+
+ printf("Type output number: ");
+ n = 0;
+ while (n != 1) {
+ n = scanf("%d", &i);
+ gets(line);
+ }
+
+ err = Pm_OpenOutput(&midi_out, i, NULL, 0, NULL, NULL, LATENCY);
+ if (err) {
+ printf("could not open midi device: %s\n", Pm_GetErrorText(err));
+ exit(1);
+ }
+ printf("Midi Output opened with %d ms latency.\n", LATENCY);
+
+
+
+ /* Get input from user for parameters */
+ printf("Type number of milliseconds for echoes: ");
+ n = 0;
+ while (n != 1) {
+ n = scanf("%d", &delay);
+ gets(line);
+ }
+
+ printf("Type number of semitones to transpose up: ");
+ n = 0;
+ while (n != 1) {
+ n = scanf("%d", &transpose);
+ gets(line);
+ }
+
+
+
+ /* loop, echoing input back transposed with multiple taps */
+
+ printf("Press C2 on the keyboard (2 octaves below middle C) to quit.\nWaiting for MIDI input...\n");
+
+ do {
+ err = Pm_Read(midi_in, buffer, 1);
+ if (err == 0) continue; /* no bytes read. */
+
+ /* print a hash mark for each event read. */
+ printf("#");
+ fflush(stdout);
+
+ status = Pm_MessageStatus(buffer[0].message);
+ data1 = Pm_MessageData1(buffer[0].message);
+ data2 = Pm_MessageData2(buffer[0].message);
+ statusprefix = status >> 4;
+
+ /* ignore messages other than key-down and key-up */
+ if ((statusprefix != 0x9) && (statusprefix != 0x8)) continue;
+
+ printf("\nReceived key message = %X %X %X, at time %ld\n", status, data1, data2, buffer[0].timestamp);
+ fflush(stdout);
+
+ /* immediately send the echoes to PortMIDI */
+ for (i = 1; i < NUM_ECHOES; i++) {
+ buffer[i].message = Pm_Message(status, data1 + transpose, data2 >> i);
+ buffer[i].timestamp = buffer[0].timestamp + (i * delay);
+ }
+ Pm_Write(midi_out, buffer, NUM_ECHOES);
+ } while (data1 != 36); /* quit when C2 is pressed */
+
+ printf("Key C2 pressed. Exiting...\n");
+ fflush(stdout);
+
+ /* Give the echoes time to finish before quitting. */
+ sleep(((NUM_ECHOES * delay) / 1000) + 1);
+
+ Pm_Close(midi_in);
+ Pm_Close(midi_out);
+
+ printf("Done.\n");
+ return 0;
+}
+
+
+
diff --git a/pd/portaudio/portmidi-macosx/pmutil.c b/pd/portaudio/portmidi-macosx/pmutil.c
new file mode 100644
index 00000000..f3582a42
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmutil.c
@@ -0,0 +1,86 @@
+/* pmutil.c -- some helpful utilities for building midi
+ applications that use PortMidi
+ */
+#include "stdlib.h"
+#include "memory.h"
+#include "portmidi.h"
+#include "pmutil.h"
+#include "pminternal.h"
+
+
+PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg)
+{
+ PmQueueRep *queue = (PmQueueRep *) malloc(sizeof(PmQueueRep));
+ if (!queue) return NULL;
+ queue->len = num_msgs * bytes_per_msg;
+ queue->buffer = malloc(queue->len);
+ if (!queue->buffer) {
+ free(queue);
+ return NULL;
+ }
+ queue->head = 0;
+ queue->tail = 0;
+ queue->msg_size = bytes_per_msg;
+ queue->overflow = FALSE;
+ return queue;
+}
+
+
+PmError Pm_QueueDestroy(PmQueue *q)
+{
+ PmQueueRep *queue = (PmQueueRep *) q;
+ if (!queue || !queue->buffer) return pmBadPtr;
+ free(queue->buffer);
+ free(queue);
+ return pmNoError;
+}
+
+
+PmError Pm_Dequeue(PmQueue *q, void *msg)
+{
+ long head;
+ PmQueueRep *queue = (PmQueueRep *) q;
+ if (queue->overflow) {
+ queue->overflow = FALSE;
+ return pmBufferOverflow;
+ }
+ head = queue->head; /* make sure this is written after access */
+ if (head == queue->tail) return 0;
+ memcpy(msg, queue->buffer + head, queue->msg_size);
+ head += queue->msg_size;
+ if (head == queue->len) head = 0;
+ queue->head = head;
+ return 1; /* success */
+}
+
+
+/* source should not enqueue data if overflow is set */
+/**/
+PmError Pm_Enqueue(PmQueue *q, void *msg)
+{
+ PmQueueRep *queue = (PmQueueRep *) q;
+ long tail = queue->tail;
+ memcpy(queue->buffer + tail, msg, queue->msg_size);
+ tail += queue->msg_size;
+ if (tail == queue->len) tail = 0;
+ if (tail == queue->head) {
+ queue->overflow = TRUE;
+ /* do not update tail, so message is lost */
+ return pmBufferOverflow;
+ }
+ queue->tail = tail;
+ return pmNoError;
+}
+
+
+int Pm_QueueFull(PmQueue *q)
+{
+ PmQueueRep *queue = (PmQueueRep *) q;
+ long tail = queue->tail;
+ tail += queue->msg_size;
+ if (tail == queue->len) {
+ tail = 0;
+ }
+ return (tail == queue->head);
+}
+
diff --git a/pd/portaudio/portmidi-macosx/pmutil.h b/pd/portaudio/portmidi-macosx/pmutil.h
new file mode 100644
index 00000000..b6268ed3
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmutil.h
@@ -0,0 +1,44 @@
+/* pmutil.h -- some helpful utilities for building midi
+ applications that use PortMidi
+ */
+
+typedef void PmQueue;
+
+/*
+ A single-reader, single-writer queue is created by
+ Pm_QueueCreate(), which takes the number of messages and
+ the message size as parameters. The queue only accepts
+ fixed sized messages. Returns NULL if memory cannot be allocated.
+
+ Pm_QueueDestroy() destroys the queue and frees its storage.
+ */
+
+PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg);
+PmError Pm_QueueDestroy(PmQueue *queue);
+
+/*
+ Pm_Dequeue() removes one item from the queue, copying it into msg.
+ Returns 1 if successful, and 0 if the queue is empty.
+ Returns pmBufferOverflow and clears the overflow flag if
+ the flag is set.
+ */
+PmError Pm_Dequeue(PmQueue *queue, void *msg);
+
+
+/*
+ Pm_Enqueue() inserts one item into the queue, copying it from msg.
+ Returns pmNoError if successful and pmBufferOverflow if the queue was
+ already full. If pmBufferOverflow is returned, the overflow flag is set.
+ */
+PmError Pm_Enqueue(PmQueue *queue, void *msg);
+
+
+/*
+ Pm_QueueFull() returns non-zero if the queue is full
+ Pm_QueueEmpty() returns non-zero if the queue is empty
+
+ Either condition may change immediately because a parallel
+ enqueue or dequeue operation could be in progress.
+ */
+int Pm_QueueFull(PmQueue *queue);
+#define Pm_QueueEmpty(m) (m->head == m->tail)
diff --git a/pd/portaudio/portmidi-macosx/portmidi.c b/pd/portaudio/portmidi-macosx/portmidi.c
new file mode 100644
index 00000000..c2a32ae7
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/portmidi.c
@@ -0,0 +1,358 @@
+#include "stdlib.h"
+#include "portmidi.h"
+#include "pminternal.h"
+
+#define is_empty(midi) ((midi)->tail == (midi)->head)
+
+static int pm_initialized = FALSE;
+
+int descriptor_index = 0;
+descriptor_node descriptors[pm_descriptor_max];
+
+
+/* pm_add_device -- describe interface/device pair to library
+ *
+ * This is called at intialization time, once for each
+ * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1)
+ * The strings are retained but NOT COPIED, so do not destroy them!
+ *
+ * returns pmInvalidDeviceId if device memory is exceeded
+ * otherwise returns pmNoError
+ */
+PmError pm_add_device(char *interf, char *name, int input,
+ void *descriptor, pm_fns_type dictionary)
+{
+ if (descriptor_index >= pm_descriptor_max) {
+ return pmInvalidDeviceId;
+ }
+ descriptors[descriptor_index].pub.interf = interf;
+ descriptors[descriptor_index].pub.name = name;
+ descriptors[descriptor_index].pub.input = input;
+ descriptors[descriptor_index].pub.output = !input;
+ descriptors[descriptor_index].descriptor = descriptor;
+ descriptors[descriptor_index].dictionary = dictionary;
+ descriptor_index++;
+ return pmNoError;
+}
+
+
+PmError Pm_Initialize( void )
+{
+ if (!pm_initialized) {
+ PmError err = pm_init(); /* defined by implementation specific file */
+ if (err) return err;
+ pm_initialized = TRUE;
+ }
+ return pmNoError;
+}
+
+
+PmError Pm_Terminate( void )
+{
+ PmError err = pmNoError;
+ if (pm_initialized) {
+ err = pm_term(); /* defined by implementation specific file */
+ /* note that even when pm_term() fails, we mark portmidi as
+ not initialized */
+ pm_initialized = FALSE;
+ }
+ return err;
+}
+
+
+int Pm_CountDevices( void )
+{
+ PmError err = Pm_Initialize();
+ if (err) return err;
+
+ return descriptor_index;
+}
+
+
+const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id )
+{
+ PmError err = Pm_Initialize();
+ if (err) return NULL;
+
+ if (id >= 0 && id < descriptor_index) {
+ return &descriptors[id].pub;
+ }
+ return NULL;
+}
+
+
+/* failure_fn -- "noop" function pointer */
+/**/
+PmError failure_fn(PmInternal *midi)
+{
+ return pmBadPtr;
+}
+
+
+/* pm_success_fn -- "noop" function pointer */
+/**/
+PmError pm_success_fn(PmInternal *midi)
+{
+ return pmNoError;
+}
+
+
+PmError none_write(PmInternal *midi, PmEvent *buffer, long length)
+{
+ return length; /* if we return 0, caller might get into a loop */
+}
+
+PmError pm_fail_fn(PmInternal *midi)
+{
+ return pmBadPtr;
+}
+
+static PmError none_open(PmInternal *midi, void *driverInfo)
+{
+ return pmBadPtr;
+}
+
+#define none_abort pm_fail_fn
+
+#define none_close pm_fail_fn
+
+
+pm_fns_node pm_none_dictionary = {
+ none_write, none_open,
+ none_abort, none_close };
+
+
+/* Pm_Read -- read up to length longs from source into buffer */
+/*
+ * returns number of longs actually read, or error code
+ When the reader wants data:
+ if overflow_flag:
+ do not get anything
+ empty the buffer (read_ptr = write_ptr)
+ clear overflow_flag
+ return pmBufferOverflow
+ get data
+ return number of messages
+
+
+ */
+PmError Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length)
+{
+ PmInternal *midi = (PmInternal *) stream;
+ int n = 0;
+ long head = midi->head;
+ while (head != midi->tail && n < length) {
+ *buffer++ = midi->buffer[head++];
+ if (head == midi->buffer_len) head = 0;
+ n++;
+ }
+ midi->head = head;
+ if (midi->overflow) {
+ midi->head = midi->tail;
+ midi->overflow = FALSE;
+ return pmBufferOverflow;
+ }
+ return n;
+}
+
+
+PmError Pm_Poll( PortMidiStream *stream )
+{
+ PmInternal *midi = (PmInternal *) stream;
+ return midi->head != midi->tail;
+}
+
+
+PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length)
+{
+ PmInternal *midi = (PmInternal *) stream;
+ return (*midi->dictionary->write)(midi, buffer, length);
+}
+
+
+PmError Pm_WriteShort( PortMidiStream *stream, long when, long msg)
+{
+ PmEvent event;
+ event.timestamp = when;
+ event.message = msg;
+ return Pm_Write(stream, &event, 1);
+}
+
+
+PmError Pm_OpenInput( PortMidiStream** stream,
+ PmDeviceID inputDevice,
+ void *inputDriverInfo,
+ long bufferSize,
+ PmTimeProcPtr time_proc,
+ void *time_info,
+ PmStream *thru)
+{
+ PmInternal *midi;
+
+ PmError err = Pm_Initialize();
+ if (err) return err;
+
+ if (inputDevice < 0 || inputDevice >= descriptor_index) {
+ return pmInvalidDeviceId;
+ }
+
+ if (!descriptors[inputDevice].pub.input) {
+ return pmInvalidDeviceId;
+ }
+
+ midi = (PmInternal *) malloc(sizeof(PmInternal));
+ *stream = midi;
+ if (!midi) return pmInsufficientMemory;
+
+ midi->head = 0;
+ midi->tail = 0;
+ midi->dictionary = &pm_none_dictionary;
+ midi->overflow = FALSE;
+ midi->flush = FALSE;
+ midi->sysex_in_progress = FALSE;
+ midi->buffer_len = bufferSize;
+ midi->buffer = (PmEvent *) pm_alloc(sizeof(PmEvent) * midi->buffer_len);
+ if (!midi->buffer) return pmInsufficientMemory;
+ midi->latency = 0;
+ midi->thru = thru;
+ midi->time_proc = time_proc;
+ midi->time_info = time_info;
+ midi->device_id = inputDevice;
+ midi->dictionary = descriptors[inputDevice].dictionary;
+ midi->write_flag = FALSE;
+ err = (*midi->dictionary->open)(midi, inputDriverInfo);
+ if (err) {
+ pm_free(midi->buffer);
+ *stream = NULL;
+ }
+ return err;
+}
+
+
+PmError Pm_OpenOutput( PortMidiStream** stream,
+ PmDeviceID outputDevice,
+ void *outputDriverInfo,
+ long bufferSize,
+ PmTimeProcPtr time_proc,
+ void *time_info,
+ long latency )
+{
+ PmInternal *midi;
+
+ PmError err = Pm_Initialize();
+ if (err) return err;
+
+ if (outputDevice < 0 || outputDevice >= descriptor_index) {
+ return pmInvalidDeviceId;
+ }
+
+ if (!descriptors[outputDevice].pub.output) {
+ return pmInvalidDeviceId;
+ }
+
+ midi = (PmInternal *) pm_alloc(sizeof(PmInternal));
+ *stream = midi;
+ if (!midi) return pmInsufficientMemory;
+
+ midi->head = 0;
+ midi->tail = 0;
+ midi->buffer_len = bufferSize;
+ midi->buffer = NULL;
+ midi->device_id = outputDevice;
+ midi->dictionary = descriptors[outputDevice].dictionary;
+ midi->time_proc = time_proc;
+ midi->time_info = time_info;
+ midi->latency = latency;
+ midi->write_flag = TRUE;
+ err = (*midi->dictionary->open)(midi, outputDriverInfo);
+ if (err) {
+ *stream = NULL;
+ pm_free(midi); // Fixed by Ning Hu, Sep.2001
+ }
+ return err;
+}
+
+
+PmError Pm_Abort( PortMidiStream* stream )
+{
+ PmInternal *midi = (PmInternal *) stream;
+ return (*midi->dictionary->abort)(midi);
+}
+
+
+PmError Pm_Close( PortMidiStream *stream )
+{
+ PmInternal *midi = (PmInternal *) stream;
+ return (*midi->dictionary->close)(midi);
+}
+
+
+const char *Pm_GetErrorText( PmError errnum )
+{
+ const char *msg;
+
+ switch(errnum)
+ {
+ case pmNoError: msg = "Success"; break;
+ case pmHostError: msg = "Host error."; break;
+ case pmInvalidDeviceId: msg = "Invalid device ID."; break;
+ case pmInsufficientMemory: msg = "Insufficient memory."; break;
+ case pmBufferTooSmall: msg = "Buffer too small."; break;
+ case pmBadPtr: msg = "Bad pointer."; break;
+ case pmInternalError: msg = "Internal PortMidi Error."; break;
+ default: msg = "Illegal error number."; break;
+ }
+ return msg;
+}
+
+
+long pm_next_time(PmInternal *midi)
+{
+ return midi->buffer[midi->head].timestamp;
+}
+
+
+/* source should not enqueue data if overflow is set */
+/*
+ When producer has data to enqueue:
+ if buffer is full:
+ set overflow_flag and flush_flag
+ return
+ else if overflow_flag:
+ return
+ else if flush_flag:
+ if sysex message is in progress:
+ return
+ else:
+ clear flush_flag
+ // fall through to enqueue data
+ enqueue the data
+
+ */
+void pm_enqueue(PmInternal *midi, PmEvent *event)
+{
+ long tail = midi->tail;
+ midi->buffer[tail++] = *event;
+ if (tail == midi->buffer_len) tail = 0;
+ if (tail == midi->head || midi->overflow) {
+ midi->overflow = TRUE;
+ midi->flush = TRUE;
+ return;
+ }
+ if (midi->flush) {
+ if (midi->sysex_in_progress) return;
+ else midi->flush = FALSE;
+ }
+ midi->tail = tail;
+}
+
+
+int pm_queue_full(PmInternal *midi)
+{
+ long tail = midi->tail + 1;
+ if (tail == midi->buffer_len) tail = 0;
+ return tail == midi->head;
+}
+
+
+
diff --git a/pd/portaudio/portmidi-macosx/portmidi.h b/pd/portaudio/portmidi-macosx/portmidi.h
new file mode 100644
index 00000000..3e648c90
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/portmidi.h
@@ -0,0 +1,338 @@
+#ifndef PORT_MIDI_H
+#define PORT_MIDI_H
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * PortMidi Portable Real-Time Audio Library
+ * PortMidi API Header File
+ * Latest version available at: http://www.cs.cmu.edu/~music/portmidi/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ * Copyright (c) 2001 Roger B. Dannenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* CHANGELOG FOR PORTMIDI -- THIS VERSION IS 1.0
+ *
+ * 21Jan02 RBD Added tests in Pm_OpenInput() and Pm_OpenOutput() to
+ * prevent opening an input as output and vice versa.
+ * Added comments and documentation.
+ * Implemented Pm_Terminate().
+ */
+
+#ifndef FALSE
+ #define FALSE 0
+#endif
+#ifndef TRUE
+ #define TRUE 1
+#endif
+
+
+typedef enum {
+ pmNoError = 0,
+
+ pmHostError = -10000,
+ pmInvalidDeviceId, /* out of range or
+ output device when input is requested or
+ input device when output is requested */
+ //pmInvalidFlag,
+ pmInsufficientMemory,
+ pmBufferTooSmall,
+ pmBufferOverflow,
+ pmBadPtr,
+ pmInternalError
+} PmError;
+
+/*
+ Pm_Initialize() is the library initialisation function - call this before
+ using the library.
+*/
+
+PmError Pm_Initialize( void );
+
+/*
+ Pm_Terminate() is the library termination function - call this after
+ using the library.
+*/
+
+PmError Pm_Terminate( void );
+
+/*
+ Return host specific error number. All host-specific errors are translated
+ to the single error class pmHostError. To find out the original error
+ number, call Pm_GetHostError().
+ This can be called after a function returns a PmError equal to pmHostError.
+*/
+int Pm_GetHostError();
+
+/*
+ Translate the error number into a human readable message.
+*/
+const char *Pm_GetErrorText( PmError errnum );
+
+
+/*
+ Device enumeration mechanism.
+
+ Device ids range from 0 to Pm_CountDevices()-1.
+
+ Devices may support input, output or both. Device 0 is always the "default"
+ device. Other platform specific devices are specified by positive device
+ ids.
+*/
+
+typedef int PmDeviceID;
+#define pmNoDevice -1
+
+typedef struct {
+ int structVersion;
+ const char *interf;
+ const char *name;
+ int input; /* true iff input is available */
+ int output; /* true iff output is available */
+} PmDeviceInfo;
+
+
+int Pm_CountDevices( void );
+/*
+ Pm_GetDefaultInputDeviceID(), Pm_GetDefaultOutputDeviceID()
+
+ Return the default device ID or pmNoDevice if there is no devices.
+ The result can be passed to Pm_OpenMidi().
+
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+
+ set PM_RECOMMENDED_OUTPUT_DEVICE=1
+
+ The user should first determine the available device ID by using
+ the supplied application "pm_devs".
+*/
+PmDeviceID Pm_GetDefaultInputDeviceID( void );
+PmDeviceID Pm_GetDefaultOutputDeviceID( void );
+
+/*
+ PmTimestamp is used to represent a millisecond clock with arbitrary
+ start time. The type is used for all MIDI timestampes and clocks.
+*/
+
+typedef long PmTimestamp;
+
+/* TRUE if t1 before t2? */
+#define PmBefore(t1,t2) ((t1-t2) < 0)
+
+
+/*
+ Pm_GetDeviceInfo() returns a pointer to a PmDeviceInfo structure
+ referring to the device specified by id.
+ If id is out of range the function returns NULL.
+
+ The returned structure is owned by the PortMidi implementation and must
+ not be manipulated or freed. The pointer is guaranteed to be valid
+ between calls to Pm_Initialize() and Pm_Terminate().
+*/
+
+const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id );
+
+
+/*
+ A single PortMidiStream is a descriptor for an open MIDI device.
+*/
+
+typedef void PortMidiStream;
+#define PmStream PortMidiStream
+
+typedef PmTimestamp (*PmTimeProcPtr)(void *time_info);
+
+
+/*
+ Pm_Open() opens a device; for either input or output.
+
+ Port is the address of a PortMidiStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ inputDevice is the id of the device used for input (see PmDeviceID above.)
+
+ inputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or handle processing.
+ inputDriverInfo is never required for correct operation. If not used
+ inputDriverInfo should be NULL.
+
+ outputDevice is the id of the device used for output (see PmDeviceID above.)
+
+ outputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or handle processing.
+ outputDriverInfo is never required for correct operation. If not used
+ outputDriverInfo should be NULL.
+
+ latency is the delay in milliseconds applied to timestamps to determine
+ when the output should actually occur.
+
+ time_proc is a pointer to a procedure that returns time in milliseconds. It
+ may be NULL, in which case a default millisecond timebase is used.
+
+ time_info is a pointer passed to time_proc.
+
+ thru points to a PmMidi descriptor opened for output; Midi input will be
+ copied to this output. To disable Midi thru, use NULL.
+
+ return value:
+ Upon success Pm_Open() returns PmNoError and places a pointer to a
+ valid PortMidiStream in the stream argument.
+ If a call to Pm_Open() fails a nonzero error code is returned (see
+ PMError above) and the value of port is invalid.
+
+*/
+
+PmError Pm_OpenInput( PortMidiStream** stream,
+ PmDeviceID inputDevice,
+ void *inputDriverInfo,
+ long bufferSize,
+ PmTimeProcPtr time_proc,
+ void *time_info,
+ PmStream* thru );
+
+
+PmError Pm_OpenOutput( PortMidiStream** stream,
+ PmDeviceID outputDevice,
+ void *outputDriverInfo,
+ long bufferSize,
+ PmTimeProcPtr time_proc,
+ void *time_info,
+ long latency );
+
+
+/*
+ Pm_Abort() terminates outgoing messages immediately
+ */
+PmError Pm_Abort( PortMidiStream* stream );
+
+/*
+ Pm_Close() closes a midi stream, flushing any pending buffers.
+*/
+
+PmError Pm_Close( PortMidiStream* stream );
+
+
+/*
+ Pm_Message() encodes a short Midi message into a long word. If data1
+ and/or data2 are not present, use zero. The port parameter is the
+ index of the Midi port if the device supports more than one.
+
+ Pm_MessagePort(), Pm_MessageStatus(), Pm_MessageData1(), and
+ Pm_MessageData2() extract fields from a long-encoded midi message.
+*/
+
+#define Pm_Message(status, data1, data2) \
+ ((((data2) << 16) & 0xFF0000) | \
+ (((data1) << 8) & 0xFF00) | \
+ ((status) & 0xFF))
+
+#define Pm_MessageStatus(msg) ((msg) & 0xFF)
+#define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF)
+#define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF)
+
+/* All midi data comes in the form of PmEvent structures. A sysex
+ message is encoded as a sequence of PmEvent structures, with each
+ structure carrying 4 bytes of the message, i.e. only the first
+ PmEvent carries the status byte.
+
+ When receiving sysex messages, the sysex message is terminated
+ by either an EOX status byte (anywhere in the 4 byte message) or
+ by a non-real-time status byte in the low order byte of message.
+ If you get a non-real-time status byte, it means the sysex message
+ was somehow truncated. It is permissible to interleave real-time
+ messages within sysex messages.
+ */
+
+typedef long PmMessage;
+
+typedef struct {
+ PmMessage message;
+ PmTimestamp timestamp;
+} PmEvent;
+
+
+/*
+ Pm_Read() retrieves midi data into a buffer, and returns the number
+ of events read. Result is a non-negative number unless an error occurs,
+ in which case a PmError value will be returned.
+
+ Buffer Overflow
+
+ The problem: if an input overflow occurs, data will be lost, ultimately
+ because there is no flow control all the way back to the data source.
+ When data is lost, the receiver should be notified and some sort of
+ graceful recovery should take place, e.g. you shouldn't resume receiving
+ in the middle of a long sysex message.
+
+ With a lock-free fifo, which is pretty much what we're stuck with to
+ enable portability to the Mac, it's tricky for the producer and consumer
+ to synchronously reset the buffer and resume normal operation.
+
+ Solution: the buffer managed by PortMidi will be flushed when an overflow
+ occurs. The consumer (Pm_Read()) gets an error message (pmBufferOverflow)
+ and ordinary processing resumes as soon as a new message arrives. The
+ remainder of a partial sysex message is not considered to be a "new
+ message" and will be flushed as well.
+
+*/
+
+PmError Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length );
+
+/*
+ Pm_Poll() tests whether input is available, returning TRUE, FALSE, or
+ an error value.
+*/
+
+PmError Pm_Poll( PortMidiStream *stream);
+
+/*
+ Pm_Write() writes midi data from a buffer. This may contain short
+ messages or sysex messages that are converted into a sequence of PmEvent
+ structures. Use Pm_WriteSysEx() to write a sysex message stored as a
+ contiguous array of bytes.
+*/
+
+PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length );
+
+/*
+ Pm_WriteShort() writes a timestamped non-system-exclusive midi message.
+*/
+
+PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, long msg);
+
+/*
+ Pm_WriteSysEx() writes a timestamped system-exclusive midi message.
+*/
+PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, char *msg);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORT_MIDI_H */
diff --git a/pd/portaudio/portmidi-macosx/porttime.h b/pd/portaudio/portmidi-macosx/porttime.h
new file mode 100644
index 00000000..8592106d
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/porttime.h
@@ -0,0 +1,30 @@
+/* porttime.h -- portable interface to millisecond timer */
+
+/* Should there be a way to choose the source of time here? */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef enum {
+ ptNoError = 0,
+ ptHostError = -10000,
+ ptAlreadyStarted,
+ ptAlreadyStopped
+} PtError;
+
+
+typedef long PtTimestamp;
+
+typedef int (PtCallback)( PtTimestamp timestamp, void *userData );
+
+
+PtError Pt_Start(int resolution, PtCallback *callback, void *userData);
+PtError Pt_Stop();
+int Pt_Started();
+PtTimestamp Pt_Time();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/pd/portaudio/portmidi-macosx/ptdarwin.c b/pd/portaudio/portmidi-macosx/ptdarwin.c
new file mode 100644
index 00000000..51cf5fde
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/ptdarwin.c
@@ -0,0 +1,58 @@
+/*
+ * Portable timer implementation for Darwin / MacOS X
+ *
+ * Jon Parise <jparise@cmu.edu>
+ *
+ * $Id: ptdarwin.c,v 1.1.1.1 2002-07-29 17:06:16 ggeiger Exp $
+ */
+
+#include <stdio.h>
+#include <sys/time.h>
+#include "porttime.h"
+
+#define TRUE 1
+#define FALSE 0
+
+static int time_started_flag = FALSE;
+static struct timeval time_offset;
+
+PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
+{
+ struct timezone tz;
+
+ if (callback) printf("error in porttime: callbacks not implemented\n");
+ time_started_flag = TRUE;
+ gettimeofday(&time_offset, &tz);
+
+ return ptNoError;
+}
+
+
+PtError Pt_Stop()
+{
+ time_started_flag = FALSE;
+ return ptNoError;
+}
+
+
+int Pt_Started()
+{
+ return time_started_flag;
+}
+
+
+PtTimestamp Pt_Time()
+{
+ long seconds, milliseconds;
+ struct timeval now;
+ struct timezone tz;
+
+ gettimeofday(&now, &tz);
+ seconds = now.tv_sec - time_offset.tv_sec;
+ milliseconds = (now.tv_usec - time_offset.tv_usec) / 1000;
+
+ return (seconds * 1000 + milliseconds);
+}
+
+
+
diff --git a/pd/src/configure b/pd/src/configure
new file mode 100755
index 00000000..88e26a18
--- /dev/null
+++ b/pd/src/configure
@@ -0,0 +1,3021 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+ --enable-alsa compile ALSA support"
+ac_help="$ac_help
+ --enable-old-alsa ALSA 0.5x support"
+ac_help="$ac_help
+ --enable-rme compile RME support"
+ac_help="$ac_help
+ --enable-debug debugging support"
+ac_help="$ac_help
+ --with-x use the X Window System"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.13"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=d_arithmetic.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --enable-alsa or --disable-alsa was given.
+if test "${enable_alsa+set}" = set; then
+ enableval="$enable_alsa"
+ alsa="yes"
+fi
+
+# Check whether --enable-old-alsa or --disable-old-alsa was given.
+if test "${enable_old_alsa+set}" = set; then
+ enableval="$enable_old_alsa"
+ alsa="old"
+fi
+
+# Check whether --enable-rme or --disable-rme was given.
+if test "${enable_rme+set}" = set; then
+ enableval="$enable_rme"
+ rme="yes"
+fi
+
+# Check whether --enable-debug or --disable-debug was given.
+if test "${enable_debug+set}" = set; then
+ enableval="$enable_debug"
+ USE_OPT_CFLAGS="NO"
+else
+ USE_OPT_CFLAGS="YES"
+fi
+
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:583: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:613: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_prog_rejected=no
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ set dummy "$ac_dir/$ac_word" "$@"
+ shift
+ ac_cv_prog_CC="$@"
+ fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test -z "$CC"; then
+ case "`uname -s`" in
+ *win32* | *WIN32*)
+ # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:664: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="cl"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+ ;;
+ esac
+ fi
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:696: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 707 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:712: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ ac_cv_prog_cc_works=yes
+ # If we can't run a trivial program, we are probably using a cross compiler.
+ if (./conftest; exit) 2>/dev/null; then
+ ac_cv_prog_cc_cross=no
+ else
+ ac_cv_prog_cc_cross=yes
+ fi
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+ { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:738: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:743: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:752: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:771: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_g=yes
+else
+ ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:833: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":"
+ for ac_dir in $PATH; do
+ # Account for people who put trailing slashes in PATH elements.
+ case "$ac_dir/" in
+ /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_IFS"
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL="$ac_cv_path_install"
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL="$ac_install_sh"
+ fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:886: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftestmake <<\EOF
+all:
+ @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+ eval ac_cv_prog_make_${ac_make}_set=yes
+else
+ eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ SET_MAKE=
+else
+ echo "$ac_t""no" 1>&6
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:913: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 928 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:934: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 945 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:951: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -nologo -E"
+ cat > conftest.$ac_ext <<EOF
+#line 962 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:968: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+
+echo $ac_n "checking for working const""... $ac_c" 1>&6
+echo "configure:994: checking for working const" >&5
+if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 999 "configure"
+#include "confdefs.h"
+
+int main() {
+
+/* Ultrix mips cc rejects this. */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this. */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this. */
+struct point {int x, y;};
+static struct point const zero = {0,0};
+/* AIX XL C 1.02.0.0 rejects this.
+ It does not let you subtract one const X* pointer from another in an arm
+ of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this. */
+ char *t;
+ char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+ *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
+ int x[] = {25, 17};
+ const int *foo = &x[0];
+ ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+ typedef const int *iptr;
+ iptr p = 0;
+ ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+ struct s { int j; const int *ap[3]; };
+ struct s *b; b->j = 5;
+}
+{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+ const int foo = 10;
+}
+
+; return 0; }
+EOF
+if { (eval echo configure:1048: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_c_const=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_c_const=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_c_const" 1>&6
+if test $ac_cv_c_const = no; then
+ cat >> confdefs.h <<\EOF
+#define const
+EOF
+
+fi
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1069: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1074 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1082: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ ac_cv_header_stdc=yes
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1099 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "memchr" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1117 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "free" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+ :
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1138 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1149: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ :
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+ cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking for pid_t""... $ac_c" 1>&6
+echo "configure:1173: checking for pid_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1178 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_pid_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_pid_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_pid_t" 1>&6
+if test $ac_cv_type_pid_t = no; then
+ cat >> confdefs.h <<\EOF
+#define pid_t int
+EOF
+
+fi
+
+echo $ac_n "checking for size_t""... $ac_c" 1>&6
+echo "configure:1206: checking for size_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1211 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_size_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_size_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_size_t" 1>&6
+if test $ac_cv_type_size_t = no; then
+ cat >> confdefs.h <<\EOF
+#define size_t unsigned
+EOF
+
+fi
+
+echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6
+echo "configure:1239: checking whether time.h and sys/time.h may both be included" >&5
+if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1244 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+int main() {
+struct tm *tp;
+; return 0; }
+EOF
+if { (eval echo configure:1253: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_header_time=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_time=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_time" 1>&6
+if test $ac_cv_header_time = yes; then
+ cat >> confdefs.h <<\EOF
+#define TIME_WITH_SYS_TIME 1
+EOF
+
+fi
+
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1275: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1280 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1288: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ ac_cv_header_stdc=yes
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1305 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "memchr" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1323 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "free" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+ :
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1344 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1355: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ :
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+ cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+for ac_hdr in fcntl.h limits.h malloc.h sys/ioctl.h sys/time.h unistd.h bstring.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1382: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1387 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1392: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+if test $ac_cv_prog_gcc = yes; then
+ echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6
+echo "configure:1421: checking whether ${CC-cc} needs -traditional" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_pattern="Autoconf.*'x'"
+ cat > conftest.$ac_ext <<EOF
+#line 1427 "configure"
+#include "confdefs.h"
+#include <sgtty.h>
+Autoconf TIOCGETP
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "$ac_pattern" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=yes
+else
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+ if test $ac_cv_prog_gcc_traditional = no; then
+ cat > conftest.$ac_ext <<EOF
+#line 1445 "configure"
+#include "confdefs.h"
+#include <termio.h>
+Autoconf TCGETA
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "$ac_pattern" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+ fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6
+ if test $ac_cv_prog_gcc_traditional = yes; then
+ CC="$CC -traditional"
+ fi
+fi
+
+echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6
+echo "configure:1467: checking return type of signal handlers" >&5
+if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1472 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int main() {
+int i;
+; return 0; }
+EOF
+if { (eval echo configure:1489: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_type_signal=void
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_type_signal=int
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_type_signal" 1>&6
+cat >> confdefs.h <<EOF
+#define RETSIGTYPE $ac_cv_type_signal
+EOF
+
+
+echo $ac_n "checking for vprintf""... $ac_c" 1>&6
+echo "configure:1508: checking for vprintf" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1513 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char vprintf(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char vprintf();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_vprintf) || defined (__stub___vprintf)
+choke me
+#else
+vprintf();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1536: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_vprintf=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_vprintf=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_VPRINTF 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test "$ac_cv_func_vprintf" != yes; then
+echo $ac_n "checking for _doprnt""... $ac_c" 1>&6
+echo "configure:1560: checking for _doprnt" >&5
+if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1565 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char _doprnt(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char _doprnt();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub__doprnt) || defined (__stub____doprnt)
+choke me
+#else
+_doprnt();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1588: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func__doprnt=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func__doprnt=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_DOPRNT 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+for ac_func in gettimeofday select socket strerror
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1615: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1620 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1643: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+
+echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6
+echo "configure:1670: checking for dlopen in -ldl" >&5
+ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ldl $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1678 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dlopen();
+
+int main() {
+dlopen()
+; return 0; }
+EOF
+if { (eval echo configure:1689: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ PDLIB="$PDLIB -ldl"
+else
+ echo "$ac_t""no" 1>&6
+echo "dynamic link support required" || exit 1
+fi
+
+
+echo $ac_n "checking for sin in -lffm""... $ac_c" 1>&6
+echo "configure:1712: checking for sin in -lffm" >&5
+ac_lib_var=`echo ffm'_'sin | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lffm $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1720 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char sin();
+
+int main() {
+sin()
+; return 0; }
+EOF
+if { (eval echo configure:1731: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ PDLIB="$PDLIB -lffm"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+echo $ac_n "checking for sin in -lm""... $ac_c" 1>&6
+echo "configure:1753: checking for sin in -lm" >&5
+ac_lib_var=`echo m'_'sin | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lm $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1761 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char sin();
+
+int main() {
+sin()
+; return 0; }
+EOF
+if { (eval echo configure:1772: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ PDLIB="$PDLIB -lm"
+else
+ echo "$ac_t""no" 1>&6
+echo "math library required" || exit 1
+fi
+
+
+echo $ac_n "checking for pthread_create in -lpthread""... $ac_c" 1>&6
+echo "configure:1795: checking for pthread_create in -lpthread" >&5
+ac_lib_var=`echo pthread'_'pthread_create | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lpthread $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1803 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char pthread_create();
+
+int main() {
+pthread_create()
+; return 0; }
+EOF
+if { (eval echo configure:1814: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ PDLIB="$PDLIB -lpthread"
+else
+ echo "$ac_t""no" 1>&6
+echo "pthreads required" || exit 1
+fi
+
+
+if test "$alsa" = yes; then
+ echo $ac_n "checking for snd_pcm_info in -lasound""... $ac_c" 1>&6
+echo "configure:1838: checking for snd_pcm_info in -lasound" >&5
+ac_lib_var=`echo asound'_'snd_pcm_info | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lasound $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1846 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char snd_pcm_info();
+
+int main() {
+snd_pcm_info()
+; return 0; }
+EOF
+if { (eval echo configure:1857: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ PDLIB="$PDLIB -lasound"
+else
+ echo "$ac_t""no" 1>&6
+alsa=""
+fi
+
+elif test "$alsa" = old; then
+ echo $ac_n "checking for snd_pcm_info in -lasound""... $ac_c" 1>&6
+echo "configure:1880: checking for snd_pcm_info in -lasound" >&5
+ac_lib_var=`echo asound'_'snd_pcm_info | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lasound $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1888 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char snd_pcm_info();
+
+int main() {
+snd_pcm_info()
+; return 0; }
+EOF
+if { (eval echo configure:1899: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ PDLIB="$PDLIB -lasound"
+else
+ echo "$ac_t""no" 1>&6
+alsa=""
+fi
+
+fi
+
+# If we find X, set shell vars x_includes and x_libraries to the
+# paths, otherwise set no_x=yes.
+# Uses ac_ vars as temps to allow command line to override cache and checks.
+# --without-x overrides everything else, but does not touch the cache.
+echo $ac_n "checking for X""... $ac_c" 1>&6
+echo "configure:1927: checking for X" >&5
+
+# Check whether --with-x or --without-x was given.
+if test "${with_x+set}" = set; then
+ withval="$with_x"
+ :
+fi
+
+# $have_x is `yes', `no', `disabled', or empty when we do not yet know.
+if test "x$with_x" = xno; then
+ # The user explicitly disabled X.
+ have_x=disabled
+else
+ if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then
+ # Both variables are already set.
+ have_x=yes
+ else
+if eval "test \"`echo '$''{'ac_cv_have_x'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # One or both of the vars are not set, and there is no cached value.
+ac_x_includes=NO ac_x_libraries=NO
+rm -fr conftestdir
+if mkdir conftestdir; then
+ cd conftestdir
+ # Make sure to not put "make" in the Imakefile rules, since we grep it out.
+ cat > Imakefile <<'EOF'
+acfindx:
+ @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"'
+EOF
+ if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then
+ # GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+ eval `${MAKE-make} acfindx 2>/dev/null | grep -v make`
+ # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR.
+ for ac_extension in a so sl; do
+ if test ! -f $ac_im_usrlibdir/libX11.$ac_extension &&
+ test -f $ac_im_libdir/libX11.$ac_extension; then
+ ac_im_usrlibdir=$ac_im_libdir; break
+ fi
+ done
+ # Screen out bogus values from the imake configuration. They are
+ # bogus both because they are the default anyway, and because
+ # using them would break gcc on systems where it needs fixed includes.
+ case "$ac_im_incroot" in
+ /usr/include) ;;
+ *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes="$ac_im_incroot" ;;
+ esac
+ case "$ac_im_usrlibdir" in
+ /usr/lib | /lib) ;;
+ *) test -d "$ac_im_usrlibdir" && ac_x_libraries="$ac_im_usrlibdir" ;;
+ esac
+ fi
+ cd ..
+ rm -fr conftestdir
+fi
+
+if test "$ac_x_includes" = NO; then
+ # Guess where to find include files, by looking for this one X11 .h file.
+ test -z "$x_direct_test_include" && x_direct_test_include=X11/Intrinsic.h
+
+ # First, try using that file with no special directory specified.
+cat > conftest.$ac_ext <<EOF
+#line 1989 "configure"
+#include "confdefs.h"
+#include <$x_direct_test_include>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1994: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ # We can compile using X headers with no special include directory.
+ac_x_includes=
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ # Look for the header file in a standard set of common directories.
+# Check X11 before X11Rn because it is often a symlink to the current release.
+ for ac_dir in \
+ /usr/X11/include \
+ /usr/X11R6/include \
+ /usr/X11R5/include \
+ /usr/X11R4/include \
+ \
+ /usr/include/X11 \
+ /usr/include/X11R6 \
+ /usr/include/X11R5 \
+ /usr/include/X11R4 \
+ \
+ /usr/local/X11/include \
+ /usr/local/X11R6/include \
+ /usr/local/X11R5/include \
+ /usr/local/X11R4/include \
+ \
+ /usr/local/include/X11 \
+ /usr/local/include/X11R6 \
+ /usr/local/include/X11R5 \
+ /usr/local/include/X11R4 \
+ \
+ /usr/X386/include \
+ /usr/x386/include \
+ /usr/XFree86/include/X11 \
+ \
+ /usr/include \
+ /usr/local/include \
+ /usr/unsupported/include \
+ /usr/athena/include \
+ /usr/local/x11r5/include \
+ /usr/lpp/Xamples/include \
+ \
+ /usr/openwin/include \
+ /usr/openwin/share/include \
+ ; \
+ do
+ if test -r "$ac_dir/$x_direct_test_include"; then
+ ac_x_includes=$ac_dir
+ break
+ fi
+ done
+fi
+rm -f conftest*
+fi # $ac_x_includes = NO
+
+if test "$ac_x_libraries" = NO; then
+ # Check for the libraries.
+
+ test -z "$x_direct_test_library" && x_direct_test_library=Xt
+ test -z "$x_direct_test_function" && x_direct_test_function=XtMalloc
+
+ # See if we find them without any special options.
+ # Don't add to $LIBS permanently.
+ ac_save_LIBS="$LIBS"
+ LIBS="-l$x_direct_test_library $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2063 "configure"
+#include "confdefs.h"
+
+int main() {
+${x_direct_test_function}()
+; return 0; }
+EOF
+if { (eval echo configure:2070: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ LIBS="$ac_save_LIBS"
+# We can link X programs with no special library path.
+ac_x_libraries=
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ LIBS="$ac_save_LIBS"
+# First see if replacing the include by lib works.
+# Check X11 before X11Rn because it is often a symlink to the current release.
+for ac_dir in `echo "$ac_x_includes" | sed s/include/lib/` \
+ /usr/X11/lib \
+ /usr/X11R6/lib \
+ /usr/X11R5/lib \
+ /usr/X11R4/lib \
+ \
+ /usr/lib/X11 \
+ /usr/lib/X11R6 \
+ /usr/lib/X11R5 \
+ /usr/lib/X11R4 \
+ \
+ /usr/local/X11/lib \
+ /usr/local/X11R6/lib \
+ /usr/local/X11R5/lib \
+ /usr/local/X11R4/lib \
+ \
+ /usr/local/lib/X11 \
+ /usr/local/lib/X11R6 \
+ /usr/local/lib/X11R5 \
+ /usr/local/lib/X11R4 \
+ \
+ /usr/X386/lib \
+ /usr/x386/lib \
+ /usr/XFree86/lib/X11 \
+ \
+ /usr/lib \
+ /usr/local/lib \
+ /usr/unsupported/lib \
+ /usr/athena/lib \
+ /usr/local/x11r5/lib \
+ /usr/lpp/Xamples/lib \
+ /lib/usr/lib/X11 \
+ \
+ /usr/openwin/lib \
+ /usr/openwin/share/lib \
+ ; \
+do
+ for ac_extension in a so sl; do
+ if test -r $ac_dir/lib${x_direct_test_library}.$ac_extension; then
+ ac_x_libraries=$ac_dir
+ break 2
+ fi
+ done
+done
+fi
+rm -f conftest*
+fi # $ac_x_libraries = NO
+
+if test "$ac_x_includes" = NO || test "$ac_x_libraries" = NO; then
+ # Didn't find X anywhere. Cache the known absence of X.
+ ac_cv_have_x="have_x=no"
+else
+ # Record where we found X for the cache.
+ ac_cv_have_x="have_x=yes \
+ ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries"
+fi
+fi
+ fi
+ eval "$ac_cv_have_x"
+fi # $with_x != no
+
+if test "$have_x" != yes; then
+ echo "$ac_t""$have_x" 1>&6
+ no_x=yes
+else
+ # If each of the values was on the command line, it overrides each guess.
+ test "x$x_includes" = xNONE && x_includes=$ac_x_includes
+ test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries
+ # Update the cache value to reflect the command line values.
+ ac_cv_have_x="have_x=yes \
+ ac_x_includes=$x_includes ac_x_libraries=$x_libraries"
+ echo "$ac_t""libraries $x_libraries, headers $x_includes" 1>&6
+fi
+
+echo $ac_n "checking for XCreateWindow in -lX11""... $ac_c" 1>&6
+echo "configure:2157: checking for XCreateWindow in -lX11" >&5
+ac_lib_var=`echo X11'_'XCreateWindow | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lX11 -L$x_libraries $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2165 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char XCreateWindow();
+
+int main() {
+XCreateWindow()
+; return 0; }
+EOF
+if { (eval echo configure:2176: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ LIBS="$LIBS -lX11 -L$x_libraries"
+else
+ echo "$ac_t""no" 1>&6
+echo "no X11 found" || exit 1
+fi
+
+
+ac_safe=`echo "tcl.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for tcl.h""... $ac_c" 1>&6
+echo "configure:2200: checking for tcl.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2205 "configure"
+#include "confdefs.h"
+#include <tcl.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2210: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+ac_safe=`echo "tcl8.1/tcl.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for tcl8.1/tcl.h""... $ac_c" 1>&6
+echo "configure:2231: checking for tcl8.1/tcl.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2236 "configure"
+#include "confdefs.h"
+#include <tcl8.1/tcl.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2241: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.1"
+else
+ echo "$ac_t""no" 1>&6
+ac_safe=`echo "tcl8.2/tcl.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for tcl8.2/tcl.h""... $ac_c" 1>&6
+echo "configure:2262: checking for tcl8.2/tcl.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2267 "configure"
+#include "confdefs.h"
+#include <tcl8.2/tcl.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2272: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.2"
+else
+ echo "$ac_t""no" 1>&6
+ac_safe=`echo "tcl8.3/tcl.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for tcl8.3/tcl.h""... $ac_c" 1>&6
+echo "configure:2293: checking for tcl8.3/tcl.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2298 "configure"
+#include "confdefs.h"
+#include <tcl8.3/tcl.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2303: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.3"
+else
+ echo "$ac_t""no" 1>&6
+ac_safe=`echo "tcl8.4/tcl.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for tcl8.4/tcl.h""... $ac_c" 1>&6
+echo "configure:2324: checking for tcl8.4/tcl.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2329 "configure"
+#include "confdefs.h"
+#include <tcl8.4/tcl.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2334: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.4"
+else
+ echo "$ac_t""no" 1>&6
+ac_safe=`echo "tcl8.5/tcl.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for tcl8.5/tcl.h""... $ac_c" 1>&6
+echo "configure:2355: checking for tcl8.5/tcl.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2360 "configure"
+#include "confdefs.h"
+#include <tcl8.5/tcl.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2365: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.5"
+else
+ echo "$ac_t""no" 1>&6
+echo "no tcl/tk header found" || exit 1
+fi
+
+fi
+
+fi
+
+fi
+
+fi
+
+fi
+
+
+echo $ac_n "checking for main in -ltcl8.3""... $ac_c" 1>&6
+echo "configure:2399: checking for main in -ltcl8.3" >&5
+ac_lib_var=`echo tcl8.3'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ltcl8.3 $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2407 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:2414: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo tcl8.3 | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-ltcl8.3 $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for main in -ltcl8.2""... $ac_c" 1>&6
+echo "configure:2440: checking for main in -ltcl8.2" >&5
+ac_lib_var=`echo tcl8.2'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ltcl8.2 $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2448 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:2455: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo tcl8.2 | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-ltcl8.2 $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for main in -ltcl8.0""... $ac_c" 1>&6
+echo "configure:2481: checking for main in -ltcl8.0" >&5
+ac_lib_var=`echo tcl8.0'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ltcl8.0 $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2489 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:2496: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo tcl8.0 | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-ltcl8.0 $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+fi
+
+
+echo $ac_n "checking for main in -ltk8.3""... $ac_c" 1>&6
+echo "configure:2529: checking for main in -ltk8.3" >&5
+ac_lib_var=`echo tk8.3'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ltk8.3 $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2537 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:2544: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo tk8.3 | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-ltk8.3 $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for main in -ltk8.2""... $ac_c" 1>&6
+echo "configure:2570: checking for main in -ltk8.2" >&5
+ac_lib_var=`echo tk8.2'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ltk8.2 $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2578 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:2585: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo tk8.2 | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-ltk8.2 $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for main in -ltk8.0""... $ac_c" 1>&6
+echo "configure:2611: checking for main in -ltk8.0" >&5
+ac_lib_var=`echo tk8.0'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ltk8.0 $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2619 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:2626: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo tk8.0 | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-ltk8.0 $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+fi
+
+
+
+
+if test `uname -s` = FreeBSD;
+then
+ LDFLAGS="-Wl,-export-dynamic"
+ EXT=pd_freebsd
+ MORECFLAGS=-DDL_OPEN
+ SYSSRC=s_freebsd.c
+ STRIPFLAG=-s
+ GUINAME="pd-gui"
+ OSNUMBER=0
+fi
+
+if test `uname -s` = Linux;
+then
+ LDFLAGS="-Wl,-export-dynamic"
+ EXT=pd_linux
+ MORECFLAGS=-DDL_OPEN
+ SYSSRC=s_linux.c
+ STRIPFLAG=-s
+ GUINAME="pd-gui"
+ if test $USE_OPT_CFLAGS == "YES";
+ then
+ OPT_CFLAGS="-O6 -funroll-loops -fomit-frame-pointer"
+ else
+ OPT_CFLAGS="-g"
+ fi
+ OSNUMBER=0
+fi
+
+if test `uname -s` = IRIX64;
+then
+ LDFLAGS="-n32 -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \
+ -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \
+ -shared -rdata_shared"
+ EXT=pd_irix6
+ MORECFLAGS=-DDL_OPEN
+ SYSSRC=s_sgi.c
+ STRIPFLAG=-s
+ GUINAME="pd-gui"
+ OSNUMBER=0
+fi
+
+if test `uname -s` = IRIX32;
+then
+ LDFLAGS="-o32 -DUNIX -DIRIX -O2 -shared -rdata_shared"
+ EXT=pd_irix5
+ MORECFLAGS=-DDL_OPEN
+ SYSSRC=s_sgi.c
+ STRIPFLAG=-s
+ GUINAME="pd-gui"
+ OSNUMBER=0
+fi
+
+if test `uname -s` = Darwin;
+then
+ LDFLAGS="-Wl -framework Tcl -framework Tk -framework CoreAudio -framework Carbon -framework CoreMIDI"
+ EXT=pd_darwin
+ MORECFLAGS="-DMACOSX -I/usr/X11R6/include -I../portaudio/pa_common \
+ -I../portaudio/pablio -I../portaudio/portmidi-macosx -Wno-error"
+ SYSSRC="s_mac.c s_portaudio.c ../portaudio/pa_common/pa_lib.c \
+ ../portaudio/pa_common/pa_trace.c \
+ ../portaudio/pa_common/pa_convert.c \
+ ../portaudio/pablio/pablio_pd.c \
+ ../portaudio/pablio/ringbuffer_pd.c \
+ ../portaudio/pa_mac_core/pa_mac_core.c \
+ ../portaudio/portmidi-macosx/pmdarwin.c \
+ ../portaudio/portmidi-macosx/pmmacosx.c \
+ ../portaudio/portmidi-macosx/pmutil.c \
+ ../portaudio/portmidi-macosx/portmidi.c \
+ ../portaudio/portmidi-macosx/ptdarwin.c "
+ STRIPFLAG=""
+ GUINAME="pdtcl"
+ GUIFLAGS="-framework Tcl -framework Tk \
+ -I/Library/Frameworks/Tk.framework/Versions/Current/Headers \
+ -I/Library/Frameworks/Tcl.framework/Versions/Current/Headers \
+ -I/Library/Frameworks/Tcl.framework/Versions/8.4/PrivateHeaders"
+ if test $USE_OPT_CFLAGS == "YES";
+ then
+ OPT_CFLAGS="-O2"
+ else
+ OPT_CFLAGS="-g"
+ fi
+ OSNUMBER=2
+ EXTERNTARGET=pd_darwin
+fi
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote substitution
+ # turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+cat > conftest.defs <<\EOF
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g
+s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g
+s%\[%\\&%g
+s%\]%\\&%g
+s%\$%$$%g
+EOF
+DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '`
+rm -f conftest.defs
+
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.13"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "makefile" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@alsa@%$alsa%g
+s%@rme@%$rme%g
+s%@PDLIB@%$PDLIB%g
+s%@DEFINES@%$DEFINES%g
+s%@MORECFLAGS@%$MORECFLAGS%g
+s%@EXT@%$EXT%g
+s%@OPT_CFLAGS@%$OPT_CFLAGS%g
+s%@USE_OPT_CFLAGS@%$USE_OPT_CFLAGS%g
+s%@SYSSRC@%$SYSSRC%g
+s%@STRIPFLAG@%$STRIPFLAG%g
+s%@GUINAME@%$GUINAME%g
+s%@GUIFLAGS@%$GUIFLAGS%g
+s%@OSNUMBER@%$OSNUMBER%g
+s%@EXTERNTARGET@%$EXTERNTARGET%g
+s%@CC@%$CC%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@CPP@%$CPP%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
+
diff --git a/pd/src/configure.in b/pd/src/configure.in
new file mode 100644
index 00000000..ad1eba66
--- /dev/null
+++ b/pd/src/configure.in
@@ -0,0 +1,192 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(d_arithmetic.c)
+
+AC_SUBST(alsa)
+AC_SUBST(rme)
+AC_SUBST(PDLIB)
+AC_SUBST(DEFINES)
+AC_SUBST(MORECFLAGS)
+AC_SUBST(EXT)
+AC_SUBST(OPT_CFLAGS)
+AC_SUBST(USE_OPT_CFLAGS)
+AC_SUBST(SYSSRC)
+AC_SUBST(STRIPFLAG)
+AC_SUBST(GUINAME)
+AC_SUBST(GUIFLAGS)
+AC_SUBST(OSNUMBER)
+AC_SUBST(EXTERNTARGET)
+
+dnl other defaults
+
+dnl check for features
+
+AC_ARG_ENABLE(alsa, [ --enable-alsa compile ALSA support],
+ alsa="yes")
+AC_ARG_ENABLE(old-alsa,[ --enable-old-alsa ALSA 0.5x support],
+ alsa="old")
+AC_ARG_ENABLE(rme, [ --enable-rme compile RME support],
+ rme="yes")
+AC_ARG_ENABLE(debug, [ --enable-debug debugging support],
+ USE_OPT_CFLAGS="NO", USE_OPT_CFLAGS="YES")
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+AC_PROG_CPP
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_HEADER_TIME
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h limits.h malloc.h sys/ioctl.h sys/time.h unistd.h bstring.h)
+
+dnl Checks for library functions.
+AC_PROG_GCC_TRADITIONAL
+AC_TYPE_SIGNAL
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS(gettimeofday select socket strerror)
+
+
+dnl Checks for libraries.
+dnl Checking for `dlopen' function in -ldl:
+AC_CHECK_LIB(dl, dlopen,PDLIB="$PDLIB -ldl",
+ echo "dynamic link support required" || exit 1)
+
+dnl Checking for `sin' function in -lffm:
+dnl ffm is the fast math library on the alpha
+AC_CHECK_LIB(ffm, sin,PDLIB="$PDLIB -lffm")
+
+dnl Checking for `sin' function in -lm:
+AC_CHECK_LIB(m, sin,PDLIB="$PDLIB -lm",
+ echo "math library required" || exit 1)
+
+dnl Checking for `pthread_create' function in -pthread
+AC_CHECK_LIB(pthread, pthread_create,PDLIB="$PDLIB -lpthread",
+ echo "pthreads required" || exit 1)
+
+dnl This should be fixed so Pd can use ALSA shared libraries where appropriate.
+if test "$alsa" = yes; then
+ AC_CHECK_LIB(asound,snd_pcm_info,PDLIB="$PDLIB -lasound",alsa="")
+elif test "$alsa" = old; then
+ AC_CHECK_LIB(asound,snd_pcm_info,PDLIB="$PDLIB -lasound",alsa="")
+fi
+
+dnl Find paths to includes and libraries for X11
+AC_PATH_X
+dnl Checking for `XCreateWindow' function in -lX11:
+AC_CHECK_LIB(X11, XCreateWindow, LIBS="$LIBS -lX11 -L$x_libraries",
+ echo "no X11 found" || exit 1, -L$x_libraries)
+
+AC_CHECK_HEADER(tcl.h,,
+ AC_CHECK_HEADER(tcl8.1/tcl.h,GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.1",
+ AC_CHECK_HEADER(tcl8.2/tcl.h,GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.2",
+ AC_CHECK_HEADER(tcl8.3/tcl.h,GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.3",
+ AC_CHECK_HEADER(tcl8.4/tcl.h,GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.4",
+ AC_CHECK_HEADER(tcl8.5/tcl.h,GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.5",
+ echo "no tcl/tk header found" || exit 1))))))
+
+AC_CHECK_LIB(tcl8.3, main,,
+ AC_CHECK_LIB(tcl8.2, main,,
+ AC_CHECK_LIB(tcl8.0, main)))
+
+AC_CHECK_LIB(tk8.3, main,,
+ AC_CHECK_LIB(tk8.2, main,,
+ AC_CHECK_LIB(tk8.0, main)))
+
+dnl Checking for tk.h or tkstep.h - not used at the moment
+dnl AC_CHECK_HEADER(tk.h,DEFINES="$DEFINES -DTKINC=\\\"tk.h\\\"")
+dnl AC_CHECK_HEADER(tkstep.h,DEFINES="$DEFINES -DTKINC=\\\"tkstep.h\\\"")
+
+
+if test `uname -s` = FreeBSD;
+then
+ LDFLAGS="-Wl,-export-dynamic"
+ EXT=pd_freebsd
+ MORECFLAGS=-DDL_OPEN
+ SYSSRC=s_freebsd.c
+ STRIPFLAG=-s
+ GUINAME="pd-gui"
+ OSNUMBER=0
+fi
+
+if test `uname -s` = Linux;
+then
+ LDFLAGS="-Wl,-export-dynamic"
+ EXT=pd_linux
+ MORECFLAGS=-DDL_OPEN
+ SYSSRC=s_linux.c
+ STRIPFLAG=-s
+ GUINAME="pd-gui"
+ if test $USE_OPT_CFLAGS == "YES";
+ then
+ OPT_CFLAGS="-O6 -funroll-loops -fomit-frame-pointer"
+ else
+ OPT_CFLAGS="-g"
+ fi
+ OSNUMBER=0
+fi
+
+if test `uname -s` = IRIX64;
+then
+ LDFLAGS="-n32 -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \
+ -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \
+ -shared -rdata_shared"
+ EXT=pd_irix6
+ MORECFLAGS=-DDL_OPEN
+ SYSSRC=s_sgi.c
+ STRIPFLAG=-s
+ GUINAME="pd-gui"
+ OSNUMBER=0
+fi
+
+if test `uname -s` = IRIX32;
+then
+ LDFLAGS="-o32 -DUNIX -DIRIX -O2 -shared -rdata_shared"
+ EXT=pd_irix5
+ MORECFLAGS=-DDL_OPEN
+ SYSSRC=s_sgi.c
+ STRIPFLAG=-s
+ GUINAME="pd-gui"
+ OSNUMBER=0
+fi
+
+if test `uname -s` = Darwin;
+then
+ LDFLAGS="-Wl -framework Tcl -framework Tk -framework CoreAudio -framework Carbon -framework CoreMIDI"
+ EXT=pd_darwin
+ MORECFLAGS="-DMACOSX -I/usr/X11R6/include -I../portaudio/pa_common \
+ -I../portaudio/pablio -I../portaudio/portmidi-macosx -Wno-error"
+ SYSSRC="s_mac.c s_portaudio.c ../portaudio/pa_common/pa_lib.c \
+ ../portaudio/pa_common/pa_trace.c \
+ ../portaudio/pa_common/pa_convert.c \
+ ../portaudio/pablio/pablio_pd.c \
+ ../portaudio/pablio/ringbuffer_pd.c \
+ ../portaudio/pa_mac_core/pa_mac_core.c \
+ ../portaudio/portmidi-macosx/pmdarwin.c \
+ ../portaudio/portmidi-macosx/pmmacosx.c \
+ ../portaudio/portmidi-macosx/pmutil.c \
+ ../portaudio/portmidi-macosx/portmidi.c \
+ ../portaudio/portmidi-macosx/ptdarwin.c "
+ STRIPFLAG=""
+ GUINAME="pdtcl"
+ GUIFLAGS="-framework Tcl -framework Tk \
+ -I/Library/Frameworks/Tk.framework/Versions/Current/Headers \
+ -I/Library/Frameworks/Tcl.framework/Versions/Current/Headers \
+ -I/Library/Frameworks/Tcl.framework/Versions/8.4/PrivateHeaders"
+ if test $USE_OPT_CFLAGS == "YES";
+ then
+ OPT_CFLAGS="-O2"
+ else
+ OPT_CFLAGS="-g"
+ fi
+ OSNUMBER=2
+ EXTERNTARGET=pd_darwin
+fi
+
+AC_OUTPUT(makefile)
+
diff --git a/pd/src/d_arithmetic.c b/pd/src/d_arithmetic.c
new file mode 100644
index 00000000..0ca4e846
--- /dev/null
+++ b/pd/src/d_arithmetic.c
@@ -0,0 +1,842 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* arithmetic binops (+, -, *, /).
+If no creation argument is given, there are two signal inlets for vector/vector
+operation; otherwise it's vector/scalar and the second inlet takes a float
+to reset the value.
+*/
+
+#include "m_pd.h"
+
+/* ----------------------------- plus ----------------------------- */
+static t_class *plus_class, *scalarplus_class;
+
+typedef struct _plus
+{
+ t_object x_obj;
+ float x_f;
+} t_plus;
+
+typedef struct _scalarplus
+{
+ t_object x_obj;
+ float x_f;
+ t_float x_g; /* inlet value */
+} t_scalarplus;
+
+static void *plus_new(t_symbol *s, int argc, t_atom *argv)
+{
+ if (argc > 1) post("+~: extra arguments ignored");
+ if (argc)
+ {
+ t_scalarplus *x = (t_scalarplus *)pd_new(scalarplus_class);
+ floatinlet_new(&x->x_obj, &x->x_g);
+ x->x_g = atom_getfloatarg(0, argc, argv);
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_f = 0;
+ return (x);
+ }
+ else
+ {
+ t_plus *x = (t_plus *)pd_new(plus_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_f = 0;
+ return (x);
+ }
+}
+
+t_int *plus_perform(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *in2 = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ while (n--) *out++ = *in1++ + *in2++;
+ return (w+5);
+}
+
+t_int *plus_perf8(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *in2 = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ for (; n; n -= 8, in1 += 8, in2 += 8, out += 8)
+ {
+ float f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3];
+ float f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7];
+
+ float g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3];
+ float g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7];
+
+ out[0] = f0 + g0; out[1] = f1 + g1; out[2] = f2 + g2; out[3] = f3 + g3;
+ out[4] = f4 + g4; out[5] = f5 + g5; out[6] = f6 + g6; out[7] = f7 + g7;
+ }
+ return (w+5);
+}
+
+t_int *scalarplus_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float f = *(t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ while (n--) *out++ = *in++ + f;
+ return (w+5);
+}
+
+t_int *scalarplus_perf8(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float g = *(t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ for (; n; n -= 8, in += 8, out += 8)
+ {
+ float f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];
+ float f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];
+
+ out[0] = f0 + g; out[1] = f1 + g; out[2] = f2 + g; out[3] = f3 + g;
+ out[4] = f4 + g; out[5] = f5 + g; out[6] = f6 + g; out[7] = f7 + g;
+ }
+ return (w+5);
+}
+
+void dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n)
+{
+ if (n&7)
+ dsp_add(plus_perform, 4, in1, in2, out, n);
+ else
+ dsp_add(plus_perf8, 4, in1, in2, out, n);
+}
+
+static void plus_dsp(t_plus *x, t_signal **sp)
+{
+ dsp_add_plus(sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
+}
+
+static void scalarplus_dsp(t_scalarplus *x, t_signal **sp)
+{
+ if (sp[0]->s_n&7)
+ dsp_add(scalarplus_perform, 4, sp[0]->s_vec, &x->x_g,
+ sp[1]->s_vec, sp[0]->s_n);
+ else
+ dsp_add(scalarplus_perf8, 4, sp[0]->s_vec, &x->x_g,
+ sp[1]->s_vec, sp[0]->s_n);
+}
+
+static void plus_setup(void)
+{
+ plus_class = class_new(gensym("+~"), (t_newmethod)plus_new, 0,
+ sizeof(t_plus), 0, A_GIMME, 0);
+ class_addmethod(plus_class, (t_method)plus_dsp, gensym("dsp"), 0);
+ CLASS_MAINSIGNALIN(plus_class, t_plus, x_f);
+ class_sethelpsymbol(plus_class, gensym("sigbinops"));
+ scalarplus_class = class_new(gensym("+~"), 0, 0,
+ sizeof(t_scalarplus), 0, 0);
+ CLASS_MAINSIGNALIN(scalarplus_class, t_scalarplus, x_f);
+ class_addmethod(scalarplus_class, (t_method)scalarplus_dsp, gensym("dsp"),
+ 0);
+ class_sethelpsymbol(scalarplus_class, gensym("sigbinops"));
+}
+
+/* ----------------------------- minus ----------------------------- */
+static t_class *minus_class, *scalarminus_class;
+
+typedef struct _minus
+{
+ t_object x_obj;
+ float x_f;
+} t_minus;
+
+typedef struct _scalarminus
+{
+ t_object x_obj;
+ float x_f;
+ t_float x_g;
+} t_scalarminus;
+
+static void *minus_new(t_symbol *s, int argc, t_atom *argv)
+{
+ if (argc > 1) post("-~: extra arguments ignored");
+ if (argc)
+ {
+ t_scalarminus *x = (t_scalarminus *)pd_new(scalarminus_class);
+ floatinlet_new(&x->x_obj, &x->x_g);
+ x->x_g = atom_getfloatarg(0, argc, argv);
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_f = 0;
+ return (x);
+ }
+ else
+ {
+ t_minus *x = (t_minus *)pd_new(minus_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_f = 0;
+ return (x);
+ }
+}
+
+t_int *minus_perform(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *in2 = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ while (n--) *out++ = *in1++ - *in2++;
+ return (w+5);
+}
+
+t_int *minus_perf8(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *in2 = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ for (; n; n -= 8, in1 += 8, in2 += 8, out += 8)
+ {
+ float f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3];
+ float f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7];
+
+ float g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3];
+ float g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7];
+
+ out[0] = f0 - g0; out[1] = f1 - g1; out[2] = f2 - g2; out[3] = f3 - g3;
+ out[4] = f4 - g4; out[5] = f5 - g5; out[6] = f6 - g6; out[7] = f7 - g7;
+ }
+ return (w+5);
+}
+
+t_int *scalarminus_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float f = *(t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ while (n--) *out++ = *in++ - f;
+ return (w+5);
+}
+
+t_int *scalarminus_perf8(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float g = *(t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ for (; n; n -= 8, in += 8, out += 8)
+ {
+ float f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];
+ float f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];
+
+ out[0] = f0 - g; out[1] = f1 - g; out[2] = f2 - g; out[3] = f3 - g;
+ out[4] = f4 - g; out[5] = f5 - g; out[6] = f6 - g; out[7] = f7 - g;
+ }
+ return (w+5);
+}
+
+static void minus_dsp(t_minus *x, t_signal **sp)
+{
+ if (sp[0]->s_n&7)
+ dsp_add(minus_perform, 4,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
+ else
+ dsp_add(minus_perf8, 4,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
+}
+
+static void scalarminus_dsp(t_scalarminus *x, t_signal **sp)
+{
+ if (sp[0]->s_n&7)
+ dsp_add(scalarminus_perform, 4, sp[0]->s_vec, &x->x_g,
+ sp[1]->s_vec, sp[0]->s_n);
+ else
+ dsp_add(scalarminus_perf8, 4, sp[0]->s_vec, &x->x_g,
+ sp[1]->s_vec, sp[0]->s_n);
+}
+
+static void minus_setup(void)
+{
+ minus_class = class_new(gensym("-~"), (t_newmethod)minus_new, 0,
+ sizeof(t_minus), 0, A_GIMME, 0);
+ CLASS_MAINSIGNALIN(minus_class, t_minus, x_f);
+ class_addmethod(minus_class, (t_method)minus_dsp, gensym("dsp"), 0);
+ class_sethelpsymbol(minus_class, gensym("sigbinops"));
+ scalarminus_class = class_new(gensym("-~"), 0, 0,
+ sizeof(t_scalarminus), 0, 0);
+ CLASS_MAINSIGNALIN(scalarminus_class, t_scalarminus, x_f);
+ class_addmethod(scalarminus_class, (t_method)scalarminus_dsp, gensym("dsp"),
+ 0);
+ class_sethelpsymbol(scalarminus_class, gensym("sigbinops"));
+}
+
+/* ----------------------------- times ----------------------------- */
+
+static t_class *times_class, *scalartimes_class;
+
+typedef struct _times
+{
+ t_object x_obj;
+ float x_f;
+} t_times;
+
+typedef struct _scalartimes
+{
+ t_object x_obj;
+ float x_f;
+ t_float x_g;
+} t_scalartimes;
+
+static void *times_new(t_symbol *s, int argc, t_atom *argv)
+{
+ if (argc > 1) post("*~: extra arguments ignored");
+ if (argc)
+ {
+ t_scalartimes *x = (t_scalartimes *)pd_new(scalartimes_class);
+ floatinlet_new(&x->x_obj, &x->x_g);
+ x->x_g = atom_getfloatarg(0, argc, argv);
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_f = 0;
+ return (x);
+ }
+ else
+ {
+ t_times *x = (t_times *)pd_new(times_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_f = 0;
+ return (x);
+ }
+}
+
+t_int *times_perform(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *in2 = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ while (n--) *out++ = *in1++ * *in2++;
+ return (w+5);
+}
+
+t_int *times_perf8(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *in2 = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ for (; n; n -= 8, in1 += 8, in2 += 8, out += 8)
+ {
+ float f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3];
+ float f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7];
+
+ float g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3];
+ float g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7];
+
+ out[0] = f0 * g0; out[1] = f1 * g1; out[2] = f2 * g2; out[3] = f3 * g3;
+ out[4] = f4 * g4; out[5] = f5 * g5; out[6] = f6 * g6; out[7] = f7 * g7;
+ }
+ return (w+5);
+}
+
+t_int *scalartimes_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float f = *(t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ while (n--) *out++ = *in++ * f;
+ return (w+5);
+}
+
+t_int *scalartimes_perf8(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float g = *(t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ for (; n; n -= 8, in += 8, out += 8)
+ {
+ float f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];
+ float f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];
+
+ out[0] = f0 * g; out[1] = f1 * g; out[2] = f2 * g; out[3] = f3 * g;
+ out[4] = f4 * g; out[5] = f5 * g; out[6] = f6 * g; out[7] = f7 * g;
+ }
+ return (w+5);
+}
+
+static void times_dsp(t_times *x, t_signal **sp)
+{
+ if (sp[0]->s_n&7)
+ dsp_add(times_perform, 4,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
+ else
+ dsp_add(times_perf8, 4,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
+}
+
+static void scalartimes_dsp(t_scalartimes *x, t_signal **sp)
+{
+ if (sp[0]->s_n&7)
+ dsp_add(scalartimes_perform, 4, sp[0]->s_vec, &x->x_g,
+ sp[1]->s_vec, sp[0]->s_n);
+ else
+ dsp_add(scalartimes_perf8, 4, sp[0]->s_vec, &x->x_g,
+ sp[1]->s_vec, sp[0]->s_n);
+}
+
+static void times_setup(void)
+{
+ times_class = class_new(gensym("*~"), (t_newmethod)times_new, 0,
+ sizeof(t_times), 0, A_GIMME, 0);
+ CLASS_MAINSIGNALIN(times_class, t_times, x_f);
+ class_addmethod(times_class, (t_method)times_dsp, gensym("dsp"), 0);
+ class_sethelpsymbol(times_class, gensym("sigbinops"));
+ scalartimes_class = class_new(gensym("*~"), 0, 0,
+ sizeof(t_scalartimes), 0, 0);
+ CLASS_MAINSIGNALIN(scalartimes_class, t_scalartimes, x_f);
+ class_addmethod(scalartimes_class, (t_method)scalartimes_dsp, gensym("dsp"),
+ 0);
+ class_sethelpsymbol(scalartimes_class, gensym("sigbinops"));
+}
+
+/* ----------------------------- over ----------------------------- */
+static t_class *over_class, *scalarover_class;
+
+typedef struct _over
+{
+ t_object x_obj;
+ float x_f;
+} t_over;
+
+typedef struct _scalarover
+{
+ t_object x_obj;
+ float x_f;
+ t_float x_g;
+} t_scalarover;
+
+static void *over_new(t_symbol *s, int argc, t_atom *argv)
+{
+ if (argc > 1) post("/~: extra arguments ignored");
+ if (argc)
+ {
+ t_scalarover *x = (t_scalarover *)pd_new(scalarover_class);
+ floatinlet_new(&x->x_obj, &x->x_g);
+ x->x_g = atom_getfloatarg(0, argc, argv);
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_f = 0;
+ return (x);
+ }
+ else
+ {
+ t_over *x = (t_over *)pd_new(over_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_f = 0;
+ return (x);
+ }
+}
+
+t_int *over_perform(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *in2 = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ while (n--)
+ {
+ float g = *in2++;
+ *out++ = (g ? *in1++ / g : 0);
+ }
+ return (w+5);
+}
+
+t_int *over_perf8(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *in2 = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ for (; n; n -= 8, in1 += 8, in2 += 8, out += 8)
+ {
+ float f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3];
+ float f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7];
+
+ float g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3];
+ float g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7];
+
+ out[0] = (g0? f0 / g0 : 0);
+ out[1] = (g1? f1 / g1 : 0);
+ out[2] = (g2? f2 / g2 : 0);
+ out[3] = (g3? f3 / g3 : 0);
+ out[4] = (g4? f4 / g4 : 0);
+ out[5] = (g5? f5 / g5 : 0);
+ out[6] = (g6? f6 / g6 : 0);
+ out[7] = (g7? f7 / g7 : 0);
+ }
+ return (w+5);
+}
+
+t_int *scalarover_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float f = 1. / *(t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ while (n--) *out++ = *in++ * f;
+ return (w+5);
+}
+
+t_int *scalarover_perf8(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float g = *(t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ if (g) g = 1.f / g;
+ for (; n; n -= 8, in += 8, out += 8)
+ {
+ float f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];
+ float f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];
+
+ out[0] = f0 * g; out[1] = f1 * g; out[2] = f2 * g; out[3] = f3 * g;
+ out[4] = f4 * g; out[5] = f5 * g; out[6] = f6 * g; out[7] = f7 * g;
+ }
+ return (w+5);
+}
+
+static void over_dsp(t_over *x, t_signal **sp)
+{
+ if (sp[0]->s_n&7)
+ dsp_add(over_perform, 4,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
+ else
+ dsp_add(over_perf8, 4,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
+}
+
+static void scalarover_dsp(t_scalarover *x, t_signal **sp)
+{
+ if (sp[0]->s_n&7)
+ dsp_add(scalarover_perform, 4, sp[0]->s_vec, &x->x_g,
+ sp[1]->s_vec, sp[0]->s_n);
+ else
+ dsp_add(scalarover_perf8, 4, sp[0]->s_vec, &x->x_g,
+ sp[1]->s_vec, sp[0]->s_n);
+}
+
+static void over_setup(void)
+{
+ over_class = class_new(gensym("/~"), (t_newmethod)over_new, 0,
+ sizeof(t_over), 0, A_GIMME, 0);
+ CLASS_MAINSIGNALIN(over_class, t_over, x_f);
+ class_addmethod(over_class, (t_method)over_dsp, gensym("dsp"), 0);
+ class_sethelpsymbol(over_class, gensym("sigbinops"));
+ scalarover_class = class_new(gensym("/~"), 0, 0,
+ sizeof(t_scalarover), 0, 0);
+ CLASS_MAINSIGNALIN(scalarover_class, t_scalarover, x_f);
+ class_addmethod(scalarover_class, (t_method)scalarover_dsp, gensym("dsp"),
+ 0);
+ class_sethelpsymbol(scalarover_class, gensym("sigbinops"));
+}
+
+/* ----------------------------- max ----------------------------- */
+static t_class *max_class, *scalarmax_class;
+
+typedef struct _max
+{
+ t_object x_obj;
+ float x_f;
+} t_max;
+
+typedef struct _scalarmax
+{
+ t_object x_obj;
+ float x_f;
+ t_float x_g;
+} t_scalarmax;
+
+static void *max_new(t_symbol *s, int argc, t_atom *argv)
+{
+ if (argc > 1) post("max~: extra arguments ignored");
+ if (argc)
+ {
+ t_scalarmax *x = (t_scalarmax *)pd_new(scalarmax_class);
+ floatinlet_new(&x->x_obj, &x->x_g);
+ x->x_g = atom_getfloatarg(0, argc, argv);
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_f = 0;
+ return (x);
+ }
+ else
+ {
+ t_max *x = (t_max *)pd_new(max_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_f = 0;
+ return (x);
+ }
+}
+
+t_int *max_perform(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *in2 = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ while (n--)
+ {
+ float f = *in1++, g = *in2++;
+ *out++ = (f > g ? f : g);
+ }
+ return (w+5);
+}
+
+t_int *max_perf8(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *in2 = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ for (; n; n -= 8, in1 += 8, in2 += 8, out += 8)
+ {
+ float f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3];
+ float f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7];
+
+ float g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3];
+ float g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7];
+
+ out[0] = (f0 > g0 ? f0 : g0); out[1] = (f1 > g1 ? f1 : g1);
+ out[2] = (f2 > g2 ? f2 : g2); out[3] = (f3 > g3 ? f3 : g3);
+ out[4] = (f4 > g4 ? f4 : g4); out[5] = (f5 > g5 ? f5 : g5);
+ out[6] = (f6 > g6 ? f6 : g6); out[7] = (f7 > g7 ? f7 : g7);
+ }
+ return (w+5);
+}
+
+t_int *scalarmax_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float f = *(t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ while (n--)
+ {
+ t_float g = *in++;
+ *out++ = (f > g ? f : g);
+ }
+ return (w+5);
+}
+
+t_int *scalarmax_perf8(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float g = *(t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ for (; n; n -= 8, in += 8, out += 8)
+ {
+ float f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];
+ float f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];
+
+ out[0] = (f0 > g ? f0 : g); out[1] = (f1 > g ? f1 : g);
+ out[2] = (f2 > g ? f2 : g); out[3] = (f3 > g ? f3 : g);
+ out[4] = (f4 > g ? f4 : g); out[5] = (f5 > g ? f5 : g);
+ out[6] = (f6 > g ? f6 : g); out[7] = (f7 > g ? f7 : g);
+ }
+ return (w+5);
+}
+
+static void max_dsp(t_max *x, t_signal **sp)
+{
+ if (sp[0]->s_n&7)
+ dsp_add(max_perform, 4,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
+ else
+ dsp_add(max_perf8, 4,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
+}
+
+static void scalarmax_dsp(t_scalarmax *x, t_signal **sp)
+{
+ if (sp[0]->s_n&7)
+ dsp_add(scalarmax_perform, 4, sp[0]->s_vec, &x->x_g,
+ sp[1]->s_vec, sp[0]->s_n);
+ else
+ dsp_add(scalarmax_perf8, 4, sp[0]->s_vec, &x->x_g,
+ sp[1]->s_vec, sp[0]->s_n);
+}
+
+static void max_setup(void)
+{
+ max_class = class_new(gensym("max~"), (t_newmethod)max_new, 0,
+ sizeof(t_max), 0, A_GIMME, 0);
+ CLASS_MAINSIGNALIN(max_class, t_max, x_f);
+ class_addmethod(max_class, (t_method)max_dsp, gensym("dsp"), 0);
+ class_sethelpsymbol(max_class, gensym("sigbinops"));
+ scalarmax_class = class_new(gensym("max~"), 0, 0,
+ sizeof(t_scalarmax), 0, 0);
+ CLASS_MAINSIGNALIN(scalarmax_class, t_scalarmax, x_f);
+ class_addmethod(scalarmax_class, (t_method)scalarmax_dsp, gensym("dsp"),
+ 0);
+ class_sethelpsymbol(scalarmax_class, gensym("sigbinops"));
+}
+
+/* ----------------------------- min ----------------------------- */
+static t_class *min_class, *scalarmin_class;
+
+typedef struct _min
+{
+ t_object x_obj;
+ float x_f;
+} t_min;
+
+typedef struct _scalarmin
+{
+ t_object x_obj;
+ t_float x_g;
+ float x_f;
+} t_scalarmin;
+
+static void *min_new(t_symbol *s, int argc, t_atom *argv)
+{
+ if (argc > 1) post("min~: extra arguments ignored");
+ if (argc)
+ {
+ t_scalarmin *x = (t_scalarmin *)pd_new(scalarmin_class);
+ floatinlet_new(&x->x_obj, &x->x_g);
+ x->x_g = atom_getfloatarg(0, argc, argv);
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_f = 0;
+ return (x);
+ }
+ else
+ {
+ t_min *x = (t_min *)pd_new(min_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_f = 0;
+ return (x);
+ }
+}
+
+t_int *min_perform(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *in2 = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ while (n--)
+ {
+ float f = *in1++, g = *in2++;
+ *out++ = (f < g ? f : g);
+ }
+ return (w+5);
+}
+
+t_int *min_perf8(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *in2 = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ for (; n; n -= 8, in1 += 8, in2 += 8, out += 8)
+ {
+ float f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3];
+ float f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7];
+
+ float g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3];
+ float g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7];
+
+ out[0] = (f0 < g0 ? f0 : g0); out[1] = (f1 < g1 ? f1 : g1);
+ out[2] = (f2 < g2 ? f2 : g2); out[3] = (f3 < g3 ? f3 : g3);
+ out[4] = (f4 < g4 ? f4 : g4); out[5] = (f5 < g5 ? f5 : g5);
+ out[6] = (f6 < g6 ? f6 : g6); out[7] = (f7 < g7 ? f7 : g7);
+ }
+ return (w+5);
+}
+
+t_int *scalarmin_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float f = *(t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ while (n--)
+ {
+ t_float g = *in++;
+ *out++ = (f < g ? f : g);
+ }
+ return (w+5);
+}
+
+t_int *scalarmin_perf8(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float g = *(t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ for (; n; n -= 8, in += 8, out += 8)
+ {
+ float f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];
+ float f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];
+
+ out[0] = (f0 < g ? f0 : g); out[1] = (f1 < g ? f1 : g);
+ out[2] = (f2 < g ? f2 : g); out[3] = (f3 < g ? f3 : g);
+ out[4] = (f4 < g ? f4 : g); out[5] = (f5 < g ? f5 : g);
+ out[6] = (f6 < g ? f6 : g); out[7] = (f7 < g ? f7 : g);
+ }
+ return (w+5);
+}
+
+static void min_dsp(t_min *x, t_signal **sp)
+{
+ if (sp[0]->s_n&7)
+ dsp_add(min_perform, 4,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
+ else
+ dsp_add(min_perf8, 4,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
+}
+
+static void scalarmin_dsp(t_scalarmin *x, t_signal **sp)
+{
+ if (sp[0]->s_n&7)
+ dsp_add(scalarmin_perform, 4, sp[0]->s_vec, &x->x_g,
+ sp[1]->s_vec, sp[0]->s_n);
+ else
+ dsp_add(scalarmin_perf8, 4, sp[0]->s_vec, &x->x_g,
+ sp[1]->s_vec, sp[0]->s_n);
+}
+
+static void min_setup(void)
+{
+ min_class = class_new(gensym("min~"), (t_newmethod)min_new, 0,
+ sizeof(t_min), 0, A_GIMME, 0);
+ CLASS_MAINSIGNALIN(min_class, t_min, x_f);
+ class_addmethod(min_class, (t_method)min_dsp, gensym("dsp"), 0);
+ class_sethelpsymbol(min_class, gensym("sigbinops"));
+ scalarmin_class = class_new(gensym("min~"), 0, 0,
+ sizeof(t_scalarmin), 0, 0);
+ CLASS_MAINSIGNALIN(scalarmin_class, t_scalarmin, x_f);
+ class_addmethod(scalarmin_class, (t_method)scalarmin_dsp, gensym("dsp"),
+ 0);
+ class_sethelpsymbol(scalarmin_class, gensym("sigbinops"));
+}
+
+/* ----------------------- global setup routine ---------------- */
+void d_arithmetic_setup(void)
+{
+ plus_setup();
+ minus_setup();
+ times_setup();
+ over_setup();
+ max_setup();
+ min_setup();
+}
+
diff --git a/pd/src/d_array.c b/pd/src/d_array.c
new file mode 100644
index 00000000..3491ad35
--- /dev/null
+++ b/pd/src/d_array.c
@@ -0,0 +1,1075 @@
+/* Copyright (c) 1997-1999 Miller Puckette and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* sampling */
+
+/* LATER make tabread4 and tabread~ */
+
+#include "m_imp.h"
+
+
+/* ------------------------- tabwrite~ -------------------------- */
+
+static t_class *tabwrite_tilde_class;
+
+typedef struct _tabwrite_tilde
+{
+ t_object x_obj;
+ int x_phase;
+ int x_nsampsintab;
+ float *x_vec;
+ t_symbol *x_arrayname;
+ t_clock *x_clock;
+ float x_f;
+} t_tabwrite_tilde;
+
+static void tabwrite_tilde_tick(t_tabwrite_tilde *x);
+
+static void *tabwrite_tilde_new(t_symbol *s)
+{
+ t_tabwrite_tilde *x = (t_tabwrite_tilde *)pd_new(tabwrite_tilde_class);
+ x->x_clock = clock_new(x, (t_method)tabwrite_tilde_tick);
+ x->x_phase = 0x7fffffff;
+ x->x_arrayname = s;
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *tabwrite_tilde_perform(t_int *w)
+{
+ t_tabwrite_tilde *x = (t_tabwrite_tilde *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ int n = (int)(w[3]), phase = x->x_phase, endphase = x->x_nsampsintab;
+ if (!x->x_vec) goto bad;
+
+ if (endphase > phase)
+ {
+ int nxfer = endphase - phase;
+ float *fp = x->x_vec + phase;
+ if (nxfer > n) nxfer = n;
+ phase += nxfer;
+ while (nxfer--)
+ {
+ float f = *in++;
+ /* bash NANs and underflow/overflow hazards to zero */
+ if (!((f > 1.0e-20f && f < 1.0e20f) || (f < -1e-20f && f > -1e20)))
+ f = 0;
+ *fp++ = f;
+ }
+ if (phase >= endphase)
+ {
+ clock_delay(x->x_clock, 0);
+ phase = 0x7fffffff;
+ }
+ x->x_phase = phase;
+ }
+bad:
+ return (w+4);
+}
+
+void tabwrite_tilde_set(t_tabwrite_tilde *x, t_symbol *s)
+{
+ t_garray *a;
+
+ x->x_arrayname = s;
+ if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))
+ {
+ if (*s->s_name) pd_error(x, "tabwrite~: %s: no such array",
+ x->x_arrayname->s_name);
+ x->x_vec = 0;
+ }
+ else if (!garray_getfloatarray(a, &x->x_nsampsintab, &x->x_vec))
+ {
+ error("%s: bad template for tabwrite~", x->x_arrayname->s_name);
+ x->x_vec = 0;
+ }
+ else garray_usedindsp(a);
+}
+
+static void tabwrite_tilde_dsp(t_tabwrite_tilde *x, t_signal **sp)
+{
+ tabwrite_tilde_set(x, x->x_arrayname);
+ dsp_add(tabwrite_tilde_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
+}
+
+static void tabwrite_tilde_bang(t_tabwrite_tilde *x)
+{
+ x->x_phase = 0;
+}
+
+static void tabwrite_tilde_stop(t_tabwrite_tilde *x)
+{
+ if (x->x_phase != 0x7fffffff)
+ {
+ tabwrite_tilde_tick(x);
+ x->x_phase = 0x7fffffff;
+ }
+}
+
+static void tabwrite_tilde_tick(t_tabwrite_tilde *x)
+{
+ t_garray *a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class);
+ if (!a) bug("tabwrite_tilde_tick");
+ else garray_redraw(a);
+}
+
+static void tabwrite_tilde_free(t_tabwrite_tilde *x)
+{
+ clock_free(x->x_clock);
+}
+
+static void tabwrite_tilde_setup(void)
+{
+ tabwrite_tilde_class = class_new(gensym("tabwrite~"),
+ (t_newmethod)tabwrite_tilde_new, (t_method)tabwrite_tilde_free,
+ sizeof(t_tabwrite_tilde), 0, A_DEFSYM, 0);
+ CLASS_MAINSIGNALIN(tabwrite_tilde_class, t_tabwrite_tilde, x_f);
+ class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_dsp,
+ gensym("dsp"), 0);
+ class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_set,
+ gensym("set"), A_SYMBOL, 0);
+ class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_stop,
+ gensym("stop"), 0);
+ class_addbang(tabwrite_tilde_class, tabwrite_tilde_bang);
+}
+
+/* ------------ tabplay~ - non-transposing sample playback --------------- */
+
+static t_class *tabplay_tilde_class;
+
+typedef struct _tabplay_tilde
+{
+ t_object x_obj;
+ t_outlet *x_bangout;
+ int x_phase;
+ int x_nsampsintab;
+ int x_limit;
+ float *x_vec;
+ t_symbol *x_arrayname;
+ t_clock *x_clock;
+} t_tabplay_tilde;
+
+static void tabplay_tilde_tick(t_tabplay_tilde *x);
+
+static void *tabplay_tilde_new(t_symbol *s)
+{
+ t_tabplay_tilde *x = (t_tabplay_tilde *)pd_new(tabplay_tilde_class);
+ x->x_clock = clock_new(x, (t_method)tabplay_tilde_tick);
+ x->x_phase = 0x7fffffff;
+ x->x_limit = 0;
+ x->x_arrayname = s;
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_bangout = outlet_new(&x->x_obj, &s_bang);
+ return (x);
+}
+
+static t_int *tabplay_tilde_perform(t_int *w)
+{
+ t_tabplay_tilde *x = (t_tabplay_tilde *)(w[1]);
+ t_float *out = (t_float *)(w[2]), *fp;
+ int n = (int)(w[3]), phase = x->x_phase,
+ endphase = (x->x_nsampsintab < x->x_limit ?
+ x->x_nsampsintab : x->x_limit), nxfer, n3;
+ if (!x->x_vec || phase >= endphase)
+ goto zero;
+
+ nxfer = endphase - phase;
+ fp = x->x_vec + phase;
+ if (nxfer > n)
+ nxfer = n;
+ n3 = n - nxfer;
+ phase += nxfer;
+ while (nxfer--)
+ *out++ = *fp++;
+ if (phase >= endphase)
+ {
+ clock_delay(x->x_clock, 0);
+ x->x_phase = 0x7fffffff;
+ while (n3--)
+ *out++ = 0;
+ }
+ else x->x_phase = phase;
+
+ return (w+4);
+zero:
+ while (n--) *out++ = 0;
+ return (w+4);
+}
+
+void tabplay_tilde_set(t_tabplay_tilde *x, t_symbol *s)
+{
+ t_garray *a;
+
+ x->x_arrayname = s;
+ if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))
+ {
+ if (*s->s_name) pd_error(x, "tabplay~: %s: no such array",
+ x->x_arrayname->s_name);
+ x->x_vec = 0;
+ }
+ else if (!garray_getfloatarray(a, &x->x_nsampsintab, &x->x_vec))
+ {
+ error("%s: bad template for tabplay~", x->x_arrayname->s_name);
+ x->x_vec = 0;
+ }
+ else garray_usedindsp(a);
+}
+
+static void tabplay_tilde_dsp(t_tabplay_tilde *x, t_signal **sp)
+{
+ tabplay_tilde_set(x, x->x_arrayname);
+ dsp_add(tabplay_tilde_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
+}
+
+static void tabplay_tilde_list(t_tabplay_tilde *x, t_symbol *s,
+ int argc, t_atom *argv)
+{
+ long start = atom_getfloatarg(0, argc, argv);
+ long length = atom_getfloatarg(1, argc, argv);
+ if (start < 0) start = 0;
+ if (length <= 0)
+ x->x_limit = 0x7fffffff;
+ else
+ x->x_limit = start + length;
+ x->x_phase = start;
+}
+
+static void tabplay_tilde_stop(t_tabplay_tilde *x)
+{
+ x->x_phase = 0x7fffffff;
+}
+
+static void tabplay_tilde_tick(t_tabplay_tilde *x)
+{
+ outlet_bang(x->x_bangout);
+}
+
+static void tabplay_tilde_free(t_tabplay_tilde *x)
+{
+ clock_free(x->x_clock);
+}
+
+static void tabplay_tilde_setup(void)
+{
+ tabplay_tilde_class = class_new(gensym("tabplay~"),
+ (t_newmethod)tabplay_tilde_new, (t_method)tabplay_tilde_free,
+ sizeof(t_tabplay_tilde), 0, A_DEFSYM, 0);
+ class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_dsp,
+ gensym("dsp"), 0);
+ class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_stop,
+ gensym("stop"), 0);
+ class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_set,
+ gensym("set"), A_DEFSYM, 0);
+ class_addlist(tabplay_tilde_class, tabplay_tilde_list);
+}
+
+/******************** tabread~ ***********************/
+
+static t_class *tabread_tilde_class;
+
+typedef struct _tabread_tilde
+{
+ t_object x_obj;
+ int x_npoints;
+ float *x_vec;
+ t_symbol *x_arrayname;
+ float x_f;
+} t_tabread_tilde;
+
+static void *tabread_tilde_new(t_symbol *s)
+{
+ t_tabread_tilde *x = (t_tabread_tilde *)pd_new(tabread_tilde_class);
+ x->x_arrayname = s;
+ x->x_vec = 0;
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *tabread_tilde_perform(t_int *w)
+{
+ t_tabread_tilde *x = (t_tabread_tilde *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ int maxindex;
+ float *buf = x->x_vec, *fp;
+ int i;
+
+ maxindex = x->x_npoints - 1;
+ if (!buf) goto zero;
+
+ for (i = 0; i < n; i++)
+ {
+ int index = *in++;
+ if (index < 0)
+ index = 0;
+ else if (index > maxindex)
+ index = maxindex;
+ *out++ = buf[index];
+ }
+ return (w+5);
+ zero:
+ while (n--) *out++ = 0;
+
+ return (w+5);
+}
+
+void tabread_tilde_set(t_tabread_tilde *x, t_symbol *s)
+{
+ t_garray *a;
+
+ x->x_arrayname = s;
+ if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))
+ {
+ if (*s->s_name)
+ error("tabread~: %s: no such array", x->x_arrayname->s_name);
+ x->x_vec = 0;
+ }
+ else if (!garray_getfloatarray(a, &x->x_npoints, &x->x_vec))
+ {
+ error("%s: bad template for tabread~", x->x_arrayname->s_name);
+ x->x_vec = 0;
+ }
+ else garray_usedindsp(a);
+}
+
+static void tabread_tilde_dsp(t_tabread_tilde *x, t_signal **sp)
+{
+ tabread_tilde_set(x, x->x_arrayname);
+
+ dsp_add(tabread_tilde_perform, 4, x,
+ sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+
+}
+
+static void tabread_tilde_free(t_tabread_tilde *x)
+{
+}
+
+static void tabread_tilde_setup(void)
+{
+ tabread_tilde_class = class_new(gensym("tabread~"),
+ (t_newmethod)tabread_tilde_new, (t_method)tabread_tilde_free,
+ sizeof(t_tabread_tilde), 0, A_DEFSYM, 0);
+ CLASS_MAINSIGNALIN(tabread_tilde_class, t_tabread_tilde, x_f);
+ class_addmethod(tabread_tilde_class, (t_method)tabread_tilde_dsp,
+ gensym("dsp"), 0);
+ class_addmethod(tabread_tilde_class, (t_method)tabread_tilde_set,
+ gensym("set"), A_SYMBOL, 0);
+}
+
+/******************** tabread4~ ***********************/
+
+static t_class *tabread4_tilde_class;
+
+typedef struct _tabread4_tilde
+{
+ t_object x_obj;
+ int x_npoints;
+ float *x_vec;
+ t_symbol *x_arrayname;
+ float x_f;
+} t_tabread4_tilde;
+
+static void *tabread4_tilde_new(t_symbol *s)
+{
+ t_tabread4_tilde *x = (t_tabread4_tilde *)pd_new(tabread4_tilde_class);
+ x->x_arrayname = s;
+ x->x_vec = 0;
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *tabread4_tilde_perform(t_int *w)
+{
+ t_tabread4_tilde *x = (t_tabread4_tilde *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ int maxindex;
+ float *buf = x->x_vec, *fp;
+ int i;
+
+ maxindex = x->x_npoints - 3;
+
+ if (!buf) goto zero;
+
+#if 0 /* test for spam -- I'm not ready to deal with this */
+ for (i = 0, xmax = 0, xmin = maxindex, fp = in1; i < n; i++, fp++)
+ {
+ float f = *in1;
+ if (f < xmin) xmin = f;
+ else if (f > xmax) xmax = f;
+ }
+ if (xmax < xmin + x->c_maxextent) xmax = xmin + x->c_maxextent;
+ for (i = 0, splitlo = xmin+ x->c_maxextent, splithi = xmax - x->c_maxextent,
+ fp = in1; i < n; i++, fp++)
+ {
+ float f = *in1;
+ if (f > splitlo && f < splithi) goto zero;
+ }
+#endif
+
+ for (i = 0; i < n; i++)
+ {
+ float findex = *in++;
+ int index = findex;
+ float frac, a, b, c, d, cminusb;
+ static int count;
+ if (index < 1)
+ index = 1, frac = 0;
+ else if (index > maxindex)
+ index = maxindex, frac = 1;
+ else frac = findex - index;
+ fp = buf + index;
+ a = fp[-1];
+ b = fp[0];
+ c = fp[1];
+ d = fp[2];
+ /* if (!i && !(count++ & 1023))
+ post("fp = %lx, shit = %lx, b = %f", fp, buf->b_shit, b); */
+ cminusb = c-b;
+ *out++ = b + frac * (
+ cminusb - 0.5f * (frac-1.) * (
+ (a - d + 3.0f * cminusb) * frac + (b - a - cminusb)
+ )
+ );
+ }
+ return (w+5);
+ zero:
+ while (n--) *out++ = 0;
+
+ return (w+5);
+}
+
+void tabread4_tilde_set(t_tabread4_tilde *x, t_symbol *s)
+{
+ t_garray *a;
+
+ x->x_arrayname = s;
+ if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))
+ {
+ if (*s->s_name)
+ error("tabread4~: %s: no such array", x->x_arrayname->s_name);
+ x->x_vec = 0;
+ }
+ else if (!garray_getfloatarray(a, &x->x_npoints, &x->x_vec))
+ {
+ error("%s: bad template for tabread4~", x->x_arrayname->s_name);
+ x->x_vec = 0;
+ }
+ else garray_usedindsp(a);
+}
+
+static void tabread4_tilde_dsp(t_tabread4_tilde *x, t_signal **sp)
+{
+ tabread4_tilde_set(x, x->x_arrayname);
+
+ dsp_add(tabread4_tilde_perform, 4, x,
+ sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+
+}
+
+static void tabread4_tilde_free(t_tabread4_tilde *x)
+{
+}
+
+static void tabread4_tilde_setup(void)
+{
+ tabread4_tilde_class = class_new(gensym("tabread4~"),
+ (t_newmethod)tabread4_tilde_new, (t_method)tabread4_tilde_free,
+ sizeof(t_tabread4_tilde), 0, A_DEFSYM, 0);
+ CLASS_MAINSIGNALIN(tabread4_tilde_class, t_tabread4_tilde, x_f);
+ class_addmethod(tabread4_tilde_class, (t_method)tabread4_tilde_dsp,
+ gensym("dsp"), 0);
+ class_addmethod(tabread4_tilde_class, (t_method)tabread4_tilde_set,
+ gensym("set"), A_SYMBOL, 0);
+}
+
+/******************** tabosc4~ ***********************/
+
+/* this is all copied from d_osc.c... what include file could this go in? */
+#define UNITBIT32 1572864. /* 3*2^19; bit 32 has place value 1 */
+
+ /* machine-dependent definitions. These ifdefs really
+ should have been by CPU type and not by operating system! */
+#ifdef IRIX
+ /* big-endian. Most significant byte is at low address in memory */
+#define HIOFFSET 0 /* word offset to find MSB */
+#define LOWOFFSET 1 /* word offset to find LSB */
+#define int32 long /* a data type that has 32 bits */
+#else
+#ifdef NT
+ /* little-endian; most significant byte is at highest address */
+#define HIOFFSET 1
+#define LOWOFFSET 0
+#define int32 long
+#else
+#ifdef __FreeBSD__
+#include <machine/endian.h>
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define HIOFFSET 1
+#define LOWOFFSET 0
+#else
+#define HIOFFSET 0 /* word offset to find MSB */
+#define LOWOFFSET 1 /* word offset to find LSB */
+#endif /* BYTE_ORDER */
+#include <sys/types.h>
+#define int32 int32_t
+#endif
+
+#ifdef __linux__
+#include <endian.h>
+#if !defined(__BYTE_ORDER) || !defined(__LITTLE_ENDIAN)
+#error No byte order defined
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define HIOFFSET 1
+#define LOWOFFSET 0
+#else
+#define HIOFFSET 0 /* word offset to find MSB */
+#define LOWOFFSET 1 /* word offset to find LSB */
+#endif /* __BYTE_ORDER */
+
+#include <sys/types.h>
+#define int32 int32_t
+
+#else
+#ifdef MACOSX
+#define HIOFFSET 0 /* word offset to find MSB */
+#define LOWOFFSET 1 /* word offset to find LSB */
+#define int32 int /* a data type that has 32 bits */
+
+#endif /* MACOSX */
+#endif /* __linux__ */
+#endif /* NT */
+#endif /* SGI */
+
+union tabfudge
+{
+ double tf_d;
+ int32 tf_i[2];
+};
+
+static t_class *tabosc4_tilde_class;
+
+typedef struct _tabosc4_tilde
+{
+ t_object x_obj;
+ float x_fnpoints;
+ float x_finvnpoints;
+ float *x_vec;
+ t_symbol *x_arrayname;
+ float x_f;
+ double x_phase;
+ float x_conv;
+} t_tabosc4_tilde;
+
+static void *tabosc4_tilde_new(t_symbol *s)
+{
+ t_tabosc4_tilde *x = (t_tabosc4_tilde *)pd_new(tabosc4_tilde_class);
+ x->x_arrayname = s;
+ x->x_vec = 0;
+ x->x_fnpoints = 512.;
+ x->x_finvnpoints = (1./512.);
+ outlet_new(&x->x_obj, gensym("signal"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *tabosc4_tilde_perform(t_int *w)
+{
+ t_tabosc4_tilde *x = (t_tabosc4_tilde *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ int normhipart;
+ union tabfudge tf;
+ float fnpoints = x->x_fnpoints;
+ int mask = fnpoints - 1;
+ float conv = fnpoints * x->x_conv;
+ int maxindex;
+ float *tab = x->x_vec, *addr;
+ int i;
+ double dphase = fnpoints * x->x_phase + UNITBIT32;
+
+ if (!tab) goto zero;
+ tf.tf_d = UNITBIT32;
+ normhipart = tf.tf_i[HIOFFSET];
+
+#if 1
+ while (n--)
+ {
+ float frac, a, b, c, d, cminusb;
+ tf.tf_d = dphase;
+ dphase += *in++ * conv;
+ addr = tab + (tf.tf_i[HIOFFSET] & mask);
+ tf.tf_i[HIOFFSET] = normhipart;
+ frac = tf.tf_d - UNITBIT32;
+ a = addr[0];
+ b = addr[1];
+ c = addr[2];
+ d = addr[3];
+ cminusb = c-b;
+ *out++ = b + frac * (
+ cminusb - 0.5f * (frac-1.) * (
+ (a - d + 3.0f * cminusb) * frac + (b - a - cminusb)
+ )
+ );
+ }
+#endif
+
+ tf.tf_d = UNITBIT32 * fnpoints;
+ normhipart = tf.tf_i[HIOFFSET];
+ tf.tf_d = dphase + (UNITBIT32 * fnpoints - UNITBIT32);
+ tf.tf_i[HIOFFSET] = normhipart;
+ x->x_phase = (tf.tf_d - UNITBIT32 * fnpoints) * x->x_finvnpoints;
+ return (w+5);
+ zero:
+ while (n--) *out++ = 0;
+
+ return (w+5);
+}
+
+void tabosc4_tilde_set(t_tabosc4_tilde *x, t_symbol *s)
+{
+ t_garray *a;
+ int npoints, pointsinarray;
+
+ x->x_arrayname = s;
+ if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))
+ {
+ if (*s->s_name)
+ pd_error(x, "tabosc4~: %s: no such array", x->x_arrayname->s_name);
+ x->x_vec = 0;
+ }
+ else if (!garray_getfloatarray(a, &pointsinarray, &x->x_vec))
+ {
+ pd_error(x, "%s: bad template for tabosc4~", x->x_arrayname->s_name);
+ x->x_vec = 0;
+ }
+ else if ((npoints = pointsinarray - 3) != (1 << ilog2(pointsinarray - 3)))
+ {
+ pd_error(x, "%s: number of points (%d) not a power of 2 plus three",
+ x->x_arrayname->s_name, pointsinarray);
+ x->x_vec = 0;
+ garray_usedindsp(a);
+ }
+ else
+ {
+ x->x_fnpoints = npoints;
+ x->x_finvnpoints = 1./npoints;
+ garray_usedindsp(a);
+ }
+}
+
+static void tabosc4_tilde_ft1(t_tabosc4_tilde *x, t_float f)
+{
+ x->x_phase = f;
+}
+
+static void tabosc4_tilde_dsp(t_tabosc4_tilde *x, t_signal **sp)
+{
+ x->x_conv = 1. / sp[0]->s_sr;
+ tabosc4_tilde_set(x, x->x_arrayname);
+
+ dsp_add(tabosc4_tilde_perform, 4, x,
+ sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+static void tabosc4_tilde_setup(void)
+{
+ tabosc4_tilde_class = class_new(gensym("tabosc4~"),
+ (t_newmethod)tabosc4_tilde_new, 0,
+ sizeof(t_tabosc4_tilde), 0, A_DEFSYM, 0);
+ CLASS_MAINSIGNALIN(tabosc4_tilde_class, t_tabosc4_tilde, x_f);
+ class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_dsp,
+ gensym("dsp"), 0);
+ class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_set,
+ gensym("set"), A_SYMBOL, 0);
+ class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_ft1,
+ gensym("ft1"), A_FLOAT, 0);
+}
+
+/* ------------------------ tabsend~ ------------------------- */
+
+static t_class *tabsend_class;
+
+typedef struct _tabsend
+{
+ t_object x_obj;
+ float *x_vec;
+ int x_graphperiod;
+ int x_graphcount;
+ t_symbol *x_arrayname;
+ t_clock *x_clock;
+ float x_f;
+} t_tabsend;
+
+static void tabsend_tick(t_tabsend *x);
+
+static void *tabsend_new(t_symbol *s)
+{
+ t_tabsend *x = (t_tabsend *)pd_new(tabsend_class);
+ x->x_graphcount = 0;
+ x->x_arrayname = s;
+ x->x_clock = clock_new(x, (t_method)tabsend_tick);
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *tabsend_perform(t_int *w)
+{
+ t_tabsend *x = (t_tabsend *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ int n = w[3];
+ t_float *dest = x->x_vec;
+ int i = x->x_graphcount;
+ if (!x->x_vec) goto bad;
+
+ while (n--)
+ {
+ float f = *in++;
+ /* bash NANs and underflow/overflow hazards to zero */
+ if (!((f > 1.0e-20f && f < 1.0e20f) || (f < -1e-20f && f > -1e20)))
+ f = 0;
+ *dest++ = f;
+ }
+ if (!i--)
+ {
+ clock_delay(x->x_clock, 0);
+ i = x->x_graphperiod;
+ }
+ x->x_graphcount = i;
+bad:
+ return (w+4);
+}
+
+static void tabsend_dsp(t_tabsend *x, t_signal **sp)
+{
+ int i, vecsize;
+ t_garray *a;
+
+ if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))
+ {
+ if (*x->x_arrayname->s_name)
+ error("tabsend~: %s: no such array", x->x_arrayname->s_name);
+ }
+ else if (!garray_getfloatarray(a, &vecsize, &x->x_vec))
+ error("%s: bad template for tabsend~", x->x_arrayname->s_name);
+ else
+ {
+ int n = sp[0]->s_n;
+ int ticksper = sp[0]->s_sr/n;
+ if (ticksper < 1) ticksper = 1;
+ x->x_graphperiod = ticksper;
+ if (x->x_graphcount > ticksper) x->x_graphcount = ticksper;
+ if (n < vecsize) vecsize = n;
+ garray_usedindsp(a);
+ dsp_add(tabsend_perform, 3, x, sp[0]->s_vec, vecsize);
+ }
+}
+
+static void tabsend_tick(t_tabsend *x)
+{
+ t_garray *a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class);
+ if (!a) bug("tabsend_tick");
+ else garray_redraw(a);
+}
+
+static void tabsend_free(t_tabsend *x)
+{
+ clock_free(x->x_clock);
+}
+
+static void tabsend_setup(void)
+{
+ tabsend_class = class_new(gensym("tabsend~"), (t_newmethod)tabsend_new,
+ (t_method)tabsend_free, sizeof(t_tabsend), 0, A_DEFSYM, 0);
+ CLASS_MAINSIGNALIN(tabsend_class, t_tabsend, x_f);
+ class_addmethod(tabsend_class, (t_method)tabsend_dsp, gensym("dsp"), 0);
+}
+
+/* ------------------------ tabreceive~ ------------------------- */
+
+static t_class *tabreceive_class;
+
+typedef struct _tabreceive
+{
+ t_object x_obj;
+ float *x_vec;
+ t_symbol *x_arrayname;
+} t_tabreceive;
+
+static t_int *tabreceive_perform(t_int *w)
+{
+ t_tabreceive *x = (t_tabreceive *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = w[3];
+ t_float *from = x->x_vec;
+ if (from) while (n--) *out++ = *from++;
+ else while (n--) *out++ = 0;
+ return (w+4);
+}
+
+static void tabreceive_dsp(t_tabreceive *x, t_signal **sp)
+{
+ t_garray *a;
+ int vecsize;
+
+ if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))
+ {
+ if (*x->x_arrayname->s_name)
+ error("tabsend~: %s: no such array", x->x_arrayname->s_name);
+ }
+ else if (!garray_getfloatarray(a, &vecsize, &x->x_vec))
+ error("%s: bad template for tabreceive~", x->x_arrayname->s_name);
+ else
+ {
+ int n = sp[0]->s_n;
+ if (n < vecsize) vecsize = n;
+ garray_usedindsp(a);
+ dsp_add(tabreceive_perform, 3, x, sp[0]->s_vec, vecsize);
+ }
+}
+
+static void *tabreceive_new(t_symbol *s)
+{
+ t_tabreceive *x = (t_tabreceive *)pd_new(tabreceive_class);
+ x->x_arrayname = s;
+ outlet_new(&x->x_obj, &s_signal);
+ return (x);
+}
+
+static void tabreceive_setup(void)
+{
+ tabreceive_class = class_new(gensym("tabreceive~"),
+ (t_newmethod)tabreceive_new, 0,
+ sizeof(t_tabreceive), 0, A_DEFSYM, 0);
+ class_addmethod(tabreceive_class, (t_method)tabreceive_dsp,
+ gensym("dsp"), 0);
+}
+
+/* ---------- tabread: control, non-interpolating ------------------------ */
+
+static t_class *tabread_class;
+
+typedef struct _tabread
+{
+ t_object x_obj;
+ t_symbol *x_arrayname;
+} t_tabread;
+
+static void tabread_float(t_tabread *x, t_float f)
+{
+ t_garray *a;
+ int npoints;
+ t_float *vec;
+
+ if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))
+ error("%s: no such array", x->x_arrayname->s_name);
+ else if (!garray_getfloatarray(a, &npoints, &vec))
+ error("%s: bad template for tabread", x->x_arrayname->s_name);
+ else
+ {
+ int n = f;
+ if (n < 0) n = 0;
+ else if (n >= npoints) n = npoints - 1;
+ outlet_float(x->x_obj.ob_outlet, (npoints ? vec[n] : 0));
+ }
+}
+
+static void tabread_set(t_tabread *x, t_symbol *s)
+{
+ x->x_arrayname = s;
+}
+
+static void *tabread_new(t_symbol *s)
+{
+ t_tabread *x = (t_tabread *)pd_new(tabread_class);
+ x->x_arrayname = s;
+ outlet_new(&x->x_obj, &s_float);
+ return (x);
+}
+
+static void tabread_setup(void)
+{
+ tabread_class = class_new(gensym("tabread"), (t_newmethod)tabread_new,
+ 0, sizeof(t_tabread), 0, A_DEFSYM, 0);
+ class_addfloat(tabread_class, (t_method)tabread_float);
+ class_addmethod(tabread_class, (t_method)tabread_set, gensym("set"),
+ A_SYMBOL, 0);
+}
+
+/* ---------- tabread4: control, non-interpolating ------------------------ */
+
+static t_class *tabread4_class;
+
+typedef struct _tabread4
+{
+ t_object x_obj;
+ t_symbol *x_arrayname;
+} t_tabread4;
+
+static void tabread4_float(t_tabread4 *x, t_float f)
+{
+ t_garray *a;
+ int npoints;
+ t_float *vec;
+
+ if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))
+ error("%s: no such array", x->x_arrayname->s_name);
+ else if (!garray_getfloatarray(a, &npoints, &vec))
+ error("%s: bad template for tabread4", x->x_arrayname->s_name);
+ else if (npoints < 4)
+ outlet_float(x->x_obj.ob_outlet, 0);
+ else if (f <= 1)
+ outlet_float(x->x_obj.ob_outlet, vec[1]);
+ else if (f >= npoints - 2)
+ outlet_float(x->x_obj.ob_outlet, vec[npoints - 2]);
+ else
+ {
+ int n = f;
+ float a, b, c, d, cminusb, frac, *fp;
+ if (n >= npoints - 2)
+ n = npoints - 3;
+ fp = vec + n;
+ frac = f - n;
+ a = fp[-1];
+ b = fp[0];
+ c = fp[1];
+ d = fp[2];
+ cminusb = c-b;
+ outlet_float(x->x_obj.ob_outlet, b + frac * (
+ cminusb - 0.5f * (frac-1.) * (
+ (a - d + 3.0f * cminusb) * frac + (b - a - cminusb))));
+ }
+}
+
+static void tabread4_set(t_tabread4 *x, t_symbol *s)
+{
+ x->x_arrayname = s;
+}
+
+static void *tabread4_new(t_symbol *s)
+{
+ t_tabread4 *x = (t_tabread4 *)pd_new(tabread4_class);
+ x->x_arrayname = s;
+ outlet_new(&x->x_obj, &s_float);
+ return (x);
+}
+
+static void tabread4_setup(void)
+{
+ tabread4_class = class_new(gensym("tabread4"), (t_newmethod)tabread4_new,
+ 0, sizeof(t_tabread4), 0, A_DEFSYM, 0);
+ class_addfloat(tabread4_class, (t_method)tabread4_float);
+ class_addmethod(tabread4_class, (t_method)tabread4_set, gensym("set"),
+ A_SYMBOL, 0);
+}
+
+/* ------------------ tabwrite: control ------------------------ */
+
+static t_class *tabwrite_class;
+
+typedef struct _tabwrite
+{
+ t_object x_obj;
+ t_symbol *x_arrayname;
+ t_clock *x_clock;
+ float x_ft1;
+ double x_updtime;
+ int x_set;
+} t_tabwrite;
+
+static void tabwrite_tick(t_tabwrite *x)
+{
+ t_garray *a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class);
+ if (!a) bug("tabwrite_tick");
+ else garray_redraw(a);
+ x->x_set = 0;
+ x->x_updtime = clock_getsystime();
+}
+
+static void tabwrite_float(t_tabwrite *x, t_float f)
+{
+ int i, vecsize;
+ t_garray *a;
+ t_float *vec;
+
+ if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))
+ error("%s: no such array", x->x_arrayname->s_name);
+ else if (!garray_getfloatarray(a, &vecsize, &vec))
+ error("%s: bad template for tabwrite", x->x_arrayname->s_name);
+ else
+ {
+ int n = x->x_ft1;
+ double timesince = clock_gettimesince(x->x_updtime);
+ if (n < 0) n = 0;
+ else if (n >= vecsize) n = vecsize-1;
+ vec[n] = f;
+ if (timesince > 1000)
+ {
+ tabwrite_tick(x);
+ }
+ else
+ {
+ if (x->x_set == 0)
+ {
+ clock_delay(x->x_clock, 1000 - timesince);
+ x->x_set = 1;
+ }
+ }
+ }
+}
+
+static void tabwrite_set(t_tabwrite *x, t_symbol *s)
+{
+ x->x_arrayname = s;
+}
+
+static void tabwrite_free(t_tabwrite *x)
+{
+ clock_free(x->x_clock);
+}
+
+static void *tabwrite_new(t_symbol *s)
+{
+ t_tabwrite *x = (t_tabwrite *)pd_new(tabwrite_class);
+ x->x_ft1 = 0;
+ x->x_arrayname = s;
+ x->x_updtime = clock_getsystime();
+ x->x_clock = clock_new(x, (t_method)tabwrite_tick);
+ floatinlet_new(&x->x_obj, &x->x_ft1);
+ return (x);
+}
+
+void tabwrite_setup(void)
+{
+ tabwrite_class = class_new(gensym("tabwrite"), (t_newmethod)tabwrite_new,
+ (t_method)tabwrite_free, sizeof(t_tabwrite), 0, A_DEFSYM, 0);
+ class_addfloat(tabwrite_class, (t_method)tabwrite_float);
+ class_addmethod(tabwrite_class, (t_method)tabwrite_set, gensym("set"), A_SYMBOL, 0);
+}
+
+/* ------------------------ global setup routine ------------------------- */
+
+void d_array_setup(void)
+{
+ tabwrite_tilde_setup();
+ tabplay_tilde_setup();
+ tabread_tilde_setup();
+ tabread4_tilde_setup();
+ tabosc4_tilde_setup();
+ tabsend_setup();
+ tabreceive_setup();
+ tabread_setup();
+ tabread4_setup();
+ tabwrite_setup();
+}
+
diff --git a/pd/src/d_ctl.c b/pd/src/d_ctl.c
new file mode 100644
index 00000000..e143a067
--- /dev/null
+++ b/pd/src/d_ctl.c
@@ -0,0 +1,496 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* The sig~ and line~ routines; possibly fancier envelope generators to
+ come later.
+*/
+
+#include "m_pd.h"
+#include "math.h"
+
+/* -------------------------- sig~ ------------------------------ */
+static t_class *sig_class;
+
+typedef struct _sig
+{
+ t_object x_obj;
+ float x_f;
+} t_sig;
+
+static t_int *sig_perform(t_int *w)
+{
+ t_float f = *(t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ while (n--) *out++ = f;
+ return (w+4);
+}
+
+static t_int *sig_perf8(t_int *w)
+{
+ t_float f = *(t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+
+ for (; n; n -= 8, out += 8)
+ {
+ out[0] = f;
+ out[1] = f;
+ out[2] = f;
+ out[3] = f;
+ out[4] = f;
+ out[5] = f;
+ out[6] = f;
+ out[7] = f;
+ }
+ return (w+4);
+}
+
+void dsp_add_scalarcopy(t_sample *in, t_sample *out, int n)
+{
+ if (n&7)
+ dsp_add(sig_perform, 3, in, out, n);
+ else
+ dsp_add(sig_perf8, 3, in, out, n);
+}
+
+static void sig_float(t_sig *x, t_float f)
+{
+ x->x_f = f;
+}
+
+static void sig_dsp(t_sig *x, t_signal **sp)
+{
+ dsp_add(sig_perform, 3, &x->x_f, sp[0]->s_vec, sp[0]->s_n);
+}
+
+static void *sig_new(t_floatarg f)
+{
+ t_sig *x = (t_sig *)pd_new(sig_class);
+ x->x_f = f;
+ outlet_new(&x->x_obj, gensym("signal"));
+ return (x);
+}
+
+static void sig_setup(void)
+{
+ sig_class = class_new(gensym("sig~"), (t_newmethod)sig_new, 0,
+ sizeof(t_sig), 0, A_DEFFLOAT, 0);
+ class_addfloat(sig_class, (t_method)sig_float);
+ class_addmethod(sig_class, (t_method)sig_dsp, gensym("dsp"), 0);
+}
+
+/* -------------------------- line~ ------------------------------ */
+static t_class *line_class;
+
+typedef struct _line
+{
+ t_object x_obj;
+ float x_target;
+ float x_value;
+ float x_biginc;
+ float x_inc;
+ float x_1overn;
+ float x_msectodsptick;
+ float x_inletvalue;
+ float x_inletwas;
+ int x_ticksleft;
+ int x_retarget;
+} t_line;
+
+static t_int *line_perform(t_int *w)
+{
+ t_line *x = (t_line *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ float f = x->x_value;
+ /* bash NANs and underflow/overflow hazards to zero */
+ if (!((f > 1.0e-20f && f < 1.0e20f) || (f < -1e-20f && f > -1e20)))
+ x->x_value = f = 0;
+ if (x->x_retarget)
+ {
+ int nticks = x->x_inletwas * x->x_msectodsptick;
+ if (!nticks) nticks = 1;
+ x->x_ticksleft = nticks;
+ x->x_biginc = (x->x_target - x->x_value)/(float)nticks;
+ x->x_inc = x->x_1overn * x->x_biginc;
+ x->x_retarget = 0;
+ }
+ if (x->x_ticksleft)
+ {
+ float f = x->x_value;
+ while (n--) *out++ = f, f += x->x_inc;
+ x->x_value += x->x_biginc;
+ x->x_ticksleft--;
+ }
+ else
+ {
+ x->x_value = x->x_target;
+ while (n--) *out++ = x->x_value;
+ }
+ return (w+4);
+}
+
+static void line_float(t_line *x, t_float f)
+{
+ if (x->x_inletvalue <= 0)
+ {
+ x->x_target = x->x_value = f;
+ x->x_ticksleft = x->x_retarget = 0;
+ }
+ else
+ {
+ x->x_target = f;
+ x->x_retarget = 1;
+ x->x_inletwas = x->x_inletvalue;
+ x->x_inletvalue = 0;
+ }
+}
+
+static void line_stop(t_line *x)
+{
+ x->x_target = x->x_value;
+ x->x_ticksleft = x->x_retarget = 0;
+}
+
+static void line_dsp(t_line *x, t_signal **sp)
+{
+ dsp_add(line_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
+ x->x_1overn = 1./sp[0]->s_n;
+ x->x_msectodsptick = sp[0]->s_sr / (1000 * sp[0]->s_n);
+}
+
+static void *line_new(void)
+{
+ t_line *x = (t_line *)pd_new(line_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ floatinlet_new(&x->x_obj, &x->x_inletvalue);
+ x->x_ticksleft = x->x_retarget = 0;
+ x->x_value = x->x_target = x->x_inletvalue = x->x_inletwas = 0;
+ return (x);
+}
+
+static void line_setup(void)
+{
+ line_class = class_new(gensym("line~"), line_new, 0,
+ sizeof(t_line), 0, 0);
+ class_addfloat(line_class, (t_method)line_float);
+ class_addmethod(line_class, (t_method)line_dsp, gensym("dsp"), 0);
+ class_addmethod(line_class, (t_method)line_stop, gensym("stop"), 0);
+}
+
+/* -------------------------- snapshot~ ------------------------------ */
+static t_class *snapshot_class;
+
+typedef struct _snapshot
+{
+ t_object x_obj;
+ t_sample x_value;
+ float x_f;
+} t_snapshot;
+
+static void *snapshot_new(void)
+{
+ t_snapshot *x = (t_snapshot *)pd_new(snapshot_class);
+ x->x_value = 0;
+ outlet_new(&x->x_obj, &s_float);
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *snapshot_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ *out = *in;
+ return (w+3);
+}
+
+static void snapshot_dsp(t_snapshot *x, t_signal **sp)
+{
+ dsp_add(snapshot_perform, 2, sp[0]->s_vec + (sp[0]->s_n-1), &x->x_value);
+}
+
+static void snapshot_bang(t_snapshot *x)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_value);
+}
+
+static void snapshot_setup(void)
+{
+ snapshot_class = class_new(gensym("snapshot~"), snapshot_new, 0,
+ sizeof(t_snapshot), 0, 0);
+ CLASS_MAINSIGNALIN(snapshot_class, t_snapshot, x_f);
+ class_addmethod(snapshot_class, (t_method)snapshot_dsp, gensym("dsp"), 0);
+ class_addbang(snapshot_class, snapshot_bang);
+}
+
+/* ---------------- env~ - simple envelope follower. ----------------- */
+
+#define MAXOVERLAP 10
+#define MAXVSTAKEN 64
+
+typedef struct sigenv
+{
+ t_object x_obj; /* header */
+ void *x_outlet; /* a "float" outlet */
+ void *x_clock; /* a "clock" object */
+ float *x_buf; /* a Hanning window */
+ int x_phase; /* number of points since last output */
+ int x_period; /* requested period of output */
+ int x_realperiod; /* period rounded up to vecsize multiple */
+ int x_npoints; /* analysis window size in samples */
+ float x_result; /* result to output */
+ float x_sumbuf[MAXOVERLAP]; /* summing buffer */
+ float x_f;
+} t_sigenv;
+
+t_class *sigenv_class;
+static void sigenv_tick(t_sigenv *x);
+
+static void *sigenv_new(t_floatarg fnpoints, t_floatarg fperiod)
+{
+ int npoints = fnpoints;
+ int period = fperiod;
+ t_sigenv *x;
+ float *buf;
+ int i;
+
+ if (npoints < 1) npoints = 1024;
+ if (period < 1) period = npoints/2;
+ if (period < npoints / MAXOVERLAP + 1)
+ period = npoints / MAXOVERLAP + 1;
+ if (!(buf = getbytes(sizeof(float) * (npoints + MAXVSTAKEN))))
+ {
+ error("env: couldn't allocate buffer");
+ return (0);
+ }
+ x = (t_sigenv *)pd_new(sigenv_class);
+ x->x_buf = buf;
+ x->x_npoints = npoints;
+ x->x_phase = 0;
+ x->x_period = period;
+ for (i = 0; i < MAXOVERLAP; i++) x->x_sumbuf[i] = 0;
+ for (i = 0; i < npoints; i++)
+ buf[i] = (1. - cos((2 * 3.14159 * i) / npoints))/npoints;
+ for (; i < npoints+MAXVSTAKEN; i++) buf[i] = 0;
+ x->x_clock = clock_new(x, (t_method)sigenv_tick);
+ x->x_outlet = outlet_new(&x->x_obj, gensym("float"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigenv_perform(t_int *w)
+{
+ t_sigenv *x = (t_sigenv *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ int count;
+ float *sump;
+ in += n;
+ for (count = x->x_phase, sump = x->x_sumbuf;
+ count < x->x_npoints; count += x->x_realperiod, sump++)
+ {
+ float *hp = x->x_buf + count;
+ float *fp = in;
+ float sum = *sump;
+ int i;
+
+ for (i = 0; i < n; i++)
+ {
+ fp--;
+ sum += *hp++ * (*fp * *fp);
+ }
+ *sump = sum;
+ }
+ sump[0] = 0;
+ x->x_phase -= n;
+ if (x->x_phase < 0)
+ {
+ x->x_result = x->x_sumbuf[0];
+ for (count = x->x_realperiod, sump = x->x_sumbuf;
+ count < x->x_npoints; count += x->x_realperiod, sump++)
+ sump[0] = sump[1];
+ sump[0] = 0;
+ x->x_phase = x->x_realperiod - n;
+ clock_delay(x->x_clock, 0L);
+ }
+ return (w+4);
+}
+
+static void sigenv_dsp(t_sigenv *x, t_signal **sp)
+{
+ if (x->x_period % sp[0]->s_n) x->x_realperiod =
+ x->x_period + sp[0]->s_n - (x->x_period % sp[0]->s_n);
+ else x->x_realperiod = x->x_period;
+ dsp_add(sigenv_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
+ if (sp[0]->s_n > MAXVSTAKEN) bug("sigenv_dsp");
+}
+
+static void sigenv_tick(t_sigenv *x) /* callback function for the clock */
+{
+ outlet_float(x->x_outlet, powtodb(x->x_result));
+}
+
+static void sigenv_ff(t_sigenv *x) /* cleanup on free */
+{
+ clock_free(x->x_clock);
+ freebytes(x->x_buf, (x->x_npoints + MAXVSTAKEN) * sizeof(float));
+}
+
+
+void sigenv_setup(void )
+{
+ sigenv_class = class_new(gensym("env~"), (t_newmethod)sigenv_new,
+ (t_method)sigenv_ff, sizeof(t_sigenv), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(sigenv_class, t_sigenv, x_f);
+ class_addmethod(sigenv_class, (t_method)sigenv_dsp, gensym("dsp"), 0);
+}
+
+/* --------------------- threshold~ ----------------------------- */
+
+static t_class *threshold_tilde_class;
+
+typedef struct _threshold_tilde
+{
+ t_object x_obj;
+ t_outlet *x_outlet1; /* bang out for high thresh */
+ t_outlet *x_outlet2; /* bang out for low thresh */
+ t_clock *x_clock; /* wakeup for message output */
+ float x_f; /* scalar inlet */
+ int x_state; /* 1 = high, 0 = low */
+ float x_hithresh; /* value of high threshold */
+ float x_lothresh; /* value of low threshold */
+ float x_deadwait; /* msec remaining in dead period */
+ float x_msecpertick; /* msec per DSP tick */
+ float x_hideadtime; /* hi dead time in msec */
+ float x_lodeadtime; /* lo dead time in msec */
+} t_threshold_tilde;
+
+static void threshold_tilde_tick(t_threshold_tilde *x);
+static void threshold_tilde_set(t_threshold_tilde *x,
+ t_floatarg hithresh, t_floatarg hideadtime,
+ t_floatarg lothresh, t_floatarg lodeadtime);
+
+static t_threshold_tilde *threshold_tilde_new(t_floatarg hithresh,
+ t_floatarg hideadtime, t_floatarg lothresh, t_floatarg lodeadtime)
+{
+ t_threshold_tilde *x = (t_threshold_tilde *)
+ pd_new(threshold_tilde_class);
+ x->x_state = 0; /* low state */
+ x->x_deadwait = 0; /* no dead time */
+ x->x_clock = clock_new(x, (t_method)threshold_tilde_tick);
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_bang);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_bang);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1"));
+ x->x_msecpertick = 0.;
+ x->x_f = 0;
+ threshold_tilde_set(x, hithresh, hideadtime, lothresh, lodeadtime);
+ return (x);
+}
+
+ /* "set" message to specify thresholds and dead times */
+static void threshold_tilde_set(t_threshold_tilde *x,
+ t_floatarg hithresh, t_floatarg hideadtime,
+ t_floatarg lothresh, t_floatarg lodeadtime)
+{
+ if (lothresh > hithresh)
+ lothresh = hithresh;
+ x->x_hithresh = hithresh;
+ x->x_hideadtime = hideadtime;
+ x->x_lothresh = lothresh;
+ x->x_lodeadtime = lodeadtime;
+}
+
+ /* number in inlet sets state -- note incompatible with JMAX which used
+ "int" message for this, impossible here because of auto signal conversion */
+static void threshold_tilde_ft1(t_threshold_tilde *x, t_floatarg f)
+{
+ x->x_state = (f != 0);
+ x->x_deadwait = 0;
+}
+
+static void threshold_tilde_tick(t_threshold_tilde *x)
+{
+ if (x->x_state)
+ outlet_bang(x->x_outlet1);
+ else outlet_bang(x->x_outlet2);
+}
+
+static t_int *threshold_tilde_perform(t_int *w)
+{
+ float *in1 = (float *)(w[1]);
+ t_threshold_tilde *x = (t_threshold_tilde *)(w[2]);
+ int n = (t_int)(w[3]);
+ if (x->x_deadwait > 0)
+ x->x_deadwait -= x->x_msecpertick;
+ else if (x->x_state)
+ {
+ /* we're high; look for low sample */
+ for (; n--; in1++)
+ {
+ if (*in1 < x->x_lothresh)
+ {
+ clock_delay(x->x_clock, 0L);
+ x->x_state = 0;
+ x->x_deadwait = x->x_lodeadtime;
+ goto done;
+ }
+ }
+ }
+ else
+ {
+ /* we're low; look for high sample */
+ for (; n--; in1++)
+ {
+ if (*in1 >= x->x_hithresh)
+ {
+ clock_delay(x->x_clock, 0L);
+ x->x_state = 1;
+ x->x_deadwait = x->x_hideadtime;
+ goto done;
+ }
+ }
+ }
+done:
+ return (w+4);
+}
+
+void threshold_tilde_dsp(t_threshold_tilde *x, t_signal **sp)
+{
+ x->x_msecpertick = 1000. * sp[0]->s_n / sp[0]->s_sr;
+ dsp_add(threshold_tilde_perform, 3, sp[0]->s_vec, x, sp[0]->s_n);
+}
+
+static void threshold_tilde_ff(t_threshold_tilde *x)
+{
+ clock_free(x->x_clock);
+}
+
+static void threshold_tilde_setup( void)
+{
+ threshold_tilde_class = class_new(gensym("threshold~"),
+ (t_newmethod)threshold_tilde_new, (t_method)threshold_tilde_ff,
+ sizeof(t_threshold_tilde), 0,
+ A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(threshold_tilde_class, t_threshold_tilde, x_f);
+ class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_set,
+ gensym("set"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_ft1,
+ gensym("ft1"), A_FLOAT, 0);
+ class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_dsp,
+ gensym("dsp"), 0);
+}
+
+/* ------------------------ global setup routine ------------------------- */
+
+void d_ctl_setup(void)
+{
+ sig_setup();
+ line_setup();
+ snapshot_setup();
+ sigenv_setup();
+ threshold_tilde_setup();
+}
+
diff --git a/pd/src/d_dac.c b/pd/src/d_dac.c
new file mode 100644
index 00000000..620adba5
--- /dev/null
+++ b/pd/src/d_dac.c
@@ -0,0 +1,183 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* The dac~ and adc~ routines.
+*/
+
+#include "m_imp.h"
+
+/* ----------------------------- dac~ --------------------------- */
+static t_class *dac_class;
+
+typedef struct _dac
+{
+ t_object x_obj;
+ t_int x_n;
+ t_int *x_vec;
+ float x_f;
+} t_dac;
+
+static void *dac_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_dac *x = (t_dac *)pd_new(dac_class);
+ t_atom defarg[2], *ap;
+ int i;
+ if (!argc)
+ {
+ argv = defarg;
+ argc = 2;
+ SETFLOAT(&defarg[0], 1);
+ SETFLOAT(&defarg[1], 2);
+ }
+ x->x_n = argc;
+ x->x_vec = (t_int *)getbytes(argc * sizeof(*x->x_vec));
+ for (i = 0; i < argc; i++)
+ x->x_vec[i] = atom_getintarg(i, argc, argv);
+ for (i = 1; i < argc; i++)
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ x->x_f = 0;
+ return (x);
+}
+
+static void dac_dsp(t_dac *x, t_signal **sp)
+{
+ t_int i, *ip;
+ t_signal **sp2;
+ for (i = x->x_n, ip = x->x_vec, sp2 = sp; i--; ip++, sp2++)
+ {
+ int ch = *ip - 1;
+ if ((*sp2)->s_n != DACBLKSIZE)
+ error("dac~: bad vector size");
+ else if (ch >= 0 && ch < sys_get_outchannels())
+ dsp_add(plus_perform, 4, sys_soundout + DACBLKSIZE*ch,
+ (*sp2)->s_vec, sys_soundout + DACBLKSIZE*ch, DACBLKSIZE);
+ }
+}
+
+static void dac_free(t_dac *x)
+{
+ freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec));
+}
+
+static void dac_setup(void)
+{
+ dac_class = class_new(gensym("dac~"), (t_newmethod)dac_new,
+ (t_method)dac_free, sizeof(t_dac), 0, A_GIMME, 0);
+ CLASS_MAINSIGNALIN(dac_class, t_dac, x_f);
+ class_addmethod(dac_class, (t_method)dac_dsp, gensym("dsp"), A_CANT, 0);
+ class_sethelpsymbol(dac_class, gensym("adc~_dac~"));
+}
+
+/* ----------------------------- adc~ --------------------------- */
+static t_class *adc_class;
+
+typedef struct _adc
+{
+ t_object x_obj;
+ t_int x_n;
+ t_int *x_vec;
+} t_adc;
+
+static void *adc_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_adc *x = (t_adc *)pd_new(adc_class);
+ t_atom defarg[2], *ap;
+ int i;
+ if (!argc)
+ {
+ argv = defarg;
+ argc = 2;
+ SETFLOAT(&defarg[0], 1);
+ SETFLOAT(&defarg[1], 2);
+ }
+ x->x_n = argc;
+ x->x_vec = (t_int *)getbytes(argc * sizeof(*x->x_vec));
+ for (i = 0; i < argc; i++)
+ x->x_vec[i] = atom_getintarg(i, argc, argv);
+ for (i = 0; i < argc; i++)
+ outlet_new(&x->x_obj, &s_signal);
+ return (x);
+}
+
+t_int *copy_perform(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ while (n--) *out++ = *in1++;
+ return (w+4);
+}
+
+t_int *copy_perf8(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+
+ for (; n; n -= 8, in1 += 8, out += 8)
+ {
+ float f0 = in1[0];
+ float f1 = in1[1];
+ float f2 = in1[2];
+ float f3 = in1[3];
+ float f4 = in1[4];
+ float f5 = in1[5];
+ float f6 = in1[6];
+ float f7 = in1[7];
+
+ out[0] = f0;
+ out[1] = f1;
+ out[2] = f2;
+ out[3] = f3;
+ out[4] = f4;
+ out[5] = f5;
+ out[6] = f6;
+ out[7] = f7;
+ }
+ return (w+4);
+}
+
+void dsp_add_copy(t_sample *in, t_sample *out, int n)
+{
+ if (n&7)
+ dsp_add(copy_perform, 3, in, out, n);
+ else
+ dsp_add(copy_perf8, 3, in, out, n);
+}
+
+static void adc_dsp(t_adc *x, t_signal **sp)
+{
+ t_int i, *ip;
+ t_signal **sp2;
+ for (i = x->x_n, ip = x->x_vec, sp2 = sp; i--; ip++, sp2++)
+ {
+ int ch = *ip - 1;
+ if ((*sp2)->s_n != DACBLKSIZE)
+ error("adc~: bad vector size");
+ else if (ch >= 0 && ch < sys_get_inchannels())
+ dsp_add_copy(sys_soundin + DACBLKSIZE*ch,
+ (*sp2)->s_vec, DACBLKSIZE);
+ else dsp_add_zero((*sp2)->s_vec, DACBLKSIZE);
+ }
+}
+
+static void adc_free(t_adc *x)
+{
+ freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec));
+}
+
+static void adc_setup(void)
+{
+ adc_class = class_new(gensym("adc~"), (t_newmethod)adc_new,
+ (t_method)adc_free, sizeof(t_adc), 0, A_GIMME, 0);
+ class_addmethod(adc_class, (t_method)adc_dsp, gensym("dsp"), A_CANT, 0);
+ class_sethelpsymbol(adc_class, gensym("adc~_dac~"));
+}
+
+void d_dac_setup(void)
+{
+ dac_setup();
+ adc_setup();
+}
+
diff --git a/pd/src/d_delay.c b/pd/src/d_delay.c
new file mode 100644
index 00000000..f14bb112
--- /dev/null
+++ b/pd/src/d_delay.c
@@ -0,0 +1,314 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* send~, delread~, throw~, catch~ */
+
+#include "m_pd.h"
+extern int ugen_getsortno(void);
+
+#define DEFDELVS 64 /* LATER get this from canvas at DSP time */
+static int delread_zero = 0; /* four bytes of zero for delread~, vd~ */
+
+/* ----------------------------- delwrite~ ----------------------------- */
+static t_class *sigdelwrite_class;
+
+typedef struct delwritectl
+{
+ int c_n;
+ float *c_vec;
+ int c_phase;
+} t_delwritectl;
+
+typedef struct _sigdelwrite
+{
+ t_object x_obj;
+ t_symbol *x_sym;
+ t_delwritectl x_cspace;
+ int x_sortno; /* DSP sort number at which this was last put on chain */
+ int x_rsortno; /* DSP sort # for first delread or write in chain */
+ int x_vecsize; /* vector size for delread~ to use */
+ float x_f;
+} t_sigdelwrite;
+
+#define XTRASAMPS 4
+#define SAMPBLK 4
+
+ /* routine to check that all delwrites/delreads/vds have same vecsize */
+static void sigdelwrite_checkvecsize(t_sigdelwrite *x, int vecsize)
+{
+ if (x->x_rsortno != ugen_getsortno())
+ {
+ x->x_vecsize = vecsize;
+ x->x_rsortno = ugen_getsortno();
+ }
+ else if (vecsize != x->x_vecsize)
+ pd_error(x, "delread/delwrite/vd vector size mismatch");
+}
+
+static void *sigdelwrite_new(t_symbol *s, t_floatarg msec)
+{
+ int nsamps;
+ t_sigdelwrite *x = (t_sigdelwrite *)pd_new(sigdelwrite_class);
+ if (!*s->s_name) s = gensym("delwrite~");
+ pd_bind(&x->x_obj.ob_pd, s);
+ x->x_sym = s;
+ nsamps = msec * sys_getsr() * (float)(0.001f);
+ if (nsamps < 1) nsamps = 1;
+ nsamps += ((- nsamps) & (SAMPBLK - 1));
+ nsamps += DEFDELVS;
+ x->x_cspace.c_n = nsamps;
+ x->x_cspace.c_vec =
+ (float *)getbytes((nsamps + XTRASAMPS) * sizeof(float));
+ x->x_cspace.c_phase = XTRASAMPS;
+ x->x_sortno = 0;
+ x->x_vecsize = 0;
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigdelwrite_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_delwritectl *c = (t_delwritectl *)(w[2]);
+ int n = (int)(w[3]);
+ int phase = c->c_phase, nsamps = c->c_n;
+ float *vp = c->c_vec, *bp = vp + phase, *ep = vp + (c->c_n + XTRASAMPS);
+ phase += n;
+ while (n--)
+ {
+ float f = *in++;
+ /* bash NANs and underflow/overflow hazards to zero */
+ if (!((f > 1.0e-20f && f < 1.0e20f) || (f < -1e-20f && f > -1e20)))
+ f = 0;
+ *bp++ = f;
+ if (bp == ep)
+ {
+ vp[0] = ep[-4];
+ vp[1] = ep[-3];
+ vp[2] = ep[-2];
+ vp[3] = ep[-1];
+ bp = vp + XTRASAMPS;
+ phase -= nsamps;
+ }
+ }
+ c->c_phase = phase;
+ return (w+4);
+}
+
+static void sigdelwrite_dsp(t_sigdelwrite *x, t_signal **sp)
+{
+ dsp_add(sigdelwrite_perform, 3, sp[0]->s_vec, &x->x_cspace, sp[0]->s_n);
+ x->x_sortno = ugen_getsortno();
+ sigdelwrite_checkvecsize(x, sp[0]->s_n);
+}
+
+static void sigdelwrite_free(t_sigdelwrite *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, x->x_sym);
+ freebytes(x->x_cspace.c_vec,
+ (x->x_cspace.c_n + XTRASAMPS) * sizeof(float));
+}
+
+static void sigdelwrite_setup(void)
+{
+ sigdelwrite_class = class_new(gensym("delwrite~"),
+ (t_newmethod)sigdelwrite_new, (t_method)sigdelwrite_free,
+ sizeof(t_sigdelwrite), 0, A_DEFSYM, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(sigdelwrite_class, t_sigdelwrite, x_f);
+ class_addmethod(sigdelwrite_class, (t_method)sigdelwrite_dsp,
+ gensym("dsp"), 0);
+}
+
+/* ----------------------------- delread~ ----------------------------- */
+static t_class *sigdelread_class;
+
+typedef struct _sigdelread
+{
+ t_object x_obj;
+ t_symbol *x_sym;
+ t_float x_deltime; /* delay in msec */
+ int x_delsamps; /* delay in samples */
+ t_float x_sr; /* samples per msec */
+ t_float x_n; /* vector size */
+ int x_zerodel; /* 0 or vecsize depending on read/write order */
+} t_sigdelread;
+
+static void sigdelread_float(t_sigdelread *x, t_float f);
+
+static void *sigdelread_new(t_symbol *s, t_floatarg f)
+{
+ t_sigdelread *x = (t_sigdelread *)pd_new(sigdelread_class);
+ x->x_sym = s;
+ x->x_sr = 1;
+ x->x_n = 1;
+ x->x_zerodel = 0;
+ sigdelread_float(x, f);
+ outlet_new(&x->x_obj, &s_signal);
+ return (x);
+}
+
+static void sigdelread_float(t_sigdelread *x, t_float f)
+{
+ int samps;
+ t_sigdelwrite *delwriter =
+ (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class);
+ x->x_deltime = f;
+ if (delwriter)
+ {
+ int delsize = delwriter->x_cspace.c_n;
+ x->x_delsamps = (int)(0.5 + x->x_sr * x->x_deltime)
+ + x->x_n - x->x_zerodel;
+ if (x->x_delsamps < x->x_n) x->x_delsamps = x->x_n;
+ else if (x->x_delsamps > delwriter->x_cspace.c_n - DEFDELVS)
+ x->x_delsamps = delwriter->x_cspace.c_n - DEFDELVS;
+ }
+}
+
+static t_int *sigdelread_perform(t_int *w)
+{
+ t_float *out = (t_float *)(w[1]);
+ t_delwritectl *c = (t_delwritectl *)(w[2]);
+ int delsamps = *(int *)(w[3]);
+ int n = (int)(w[4]);
+ int phase = c->c_phase - delsamps, nsamps = c->c_n;
+ float *vp = c->c_vec, *bp, *ep = vp + (c->c_n + XTRASAMPS);
+
+ if (phase < 0) phase += nsamps;
+ bp = vp + phase;
+ while (n--)
+ {
+ *out++ = *bp++;
+ if (bp == ep) bp -= nsamps;
+ }
+ return (w+5);
+}
+
+static void sigdelread_dsp(t_sigdelread *x, t_signal **sp)
+{
+ t_sigdelwrite *delwriter =
+ (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class);
+ x->x_sr = sp[0]->s_sr * 0.001;
+ x->x_n = sp[0]->s_n;
+ if (delwriter)
+ {
+ sigdelwrite_checkvecsize(delwriter, sp[0]->s_n);
+ x->x_zerodel = (delwriter->x_sortno == ugen_getsortno() ?
+ 0 : delwriter->x_vecsize);
+ sigdelread_float(x, x->x_deltime);
+ dsp_add(sigdelread_perform, 4,
+ sp[0]->s_vec, &delwriter->x_cspace, &x->x_delsamps, sp[0]->s_n);
+ }
+ else if (*x->x_sym->s_name)
+ error("delread~: %s: no such delwrite~",x->x_sym->s_name);
+}
+
+static void sigdelread_setup(void)
+{
+ sigdelread_class = class_new(gensym("delread~"),
+ (t_newmethod)sigdelread_new, 0,
+ sizeof(t_sigdelread), 0, A_DEFSYM, A_DEFFLOAT, 0);
+ class_addmethod(sigdelread_class, (t_method)sigdelread_dsp,
+ gensym("dsp"), 0);
+ class_addfloat(sigdelread_class, (t_method)sigdelread_float);
+}
+
+
+/* ----------------------------- vd~ ----------------------------- */
+static t_class *sigvd_class;
+
+typedef struct _sigvd
+{
+ t_object x_obj;
+ t_symbol *x_sym;
+ t_float x_sr; /* samples per msec */
+ int x_zerodel; /* 0 or vecsize depending on read/write order */
+ float x_f;
+} t_sigvd;
+
+static void *sigvd_new(t_symbol *s)
+{
+ t_sigvd *x = (t_sigvd *)pd_new(sigvd_class);
+ if (!*s->s_name) s = gensym("vd~");
+ x->x_sym = s;
+ x->x_sr = 1;
+ x->x_zerodel = 0;
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigvd_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ t_delwritectl *ctl = (t_delwritectl *)(w[3]);
+ t_sigvd *x = (t_sigvd *)(w[4]);
+ int n = (int)(w[5]);
+
+ int nsamps = ctl->c_n;
+ float limit = nsamps - n - 1;
+ float fn = n-4;
+ float *vp = ctl->c_vec, *bp, *wp = vp + ctl->c_phase;
+ float zerodel = x->x_zerodel;
+ while (n--)
+ {
+ float delsamps = x->x_sr * *in++ - zerodel, frac;
+ int idelsamps;
+ float a, b, c, d, cminusb;
+ if (delsamps < 1.00001f) delsamps = 1.00001f;
+ if (delsamps > limit) delsamps = limit;
+ delsamps += fn;
+ fn = fn - 1.0f;
+ idelsamps = delsamps;
+ frac = delsamps - (float)idelsamps;
+ bp = wp - (idelsamps + 3);
+ if (bp < vp + 4) bp += nsamps;
+ d = bp[-3];
+ c = bp[-2];
+ b = bp[-1];
+ a = bp[0];
+ cminusb = c-b;
+ *out++ = b + frac * (
+ cminusb - 0.5f * (frac-1.) * (
+ (a - d + 3.0f * cminusb) * frac + (b - a - cminusb)
+ )
+ );
+ }
+ return (w+6);
+}
+
+static void sigvd_dsp(t_sigvd *x, t_signal **sp)
+{
+ t_sigdelwrite *delwriter =
+ (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class);
+ x->x_sr = sp[0]->s_sr * 0.001;
+ if (delwriter)
+ {
+ sigdelwrite_checkvecsize(delwriter, sp[0]->s_n);
+ x->x_zerodel = (delwriter->x_sortno == ugen_getsortno() ?
+ 0 : delwriter->x_vecsize);
+ dsp_add(sigvd_perform, 5,
+ sp[0]->s_vec, sp[1]->s_vec,
+ &delwriter->x_cspace, x, sp[0]->s_n);
+ }
+ else error("vd~: %s: no such delwrite~",x->x_sym->s_name);
+}
+
+static void sigvd_setup(void)
+{
+ sigvd_class = class_new(gensym("vd~"), (t_newmethod)sigvd_new, 0,
+ sizeof(t_sigvd), 0, A_DEFSYM, 0);
+ class_addmethod(sigvd_class, (t_method)sigvd_dsp, gensym("dsp"), 0);
+ CLASS_MAINSIGNALIN(sigvd_class, t_sigvd, x_f);
+}
+
+/* ----------------------- global setup routine ---------------- */
+
+void d_delay_setup(void)
+{
+ sigdelwrite_setup();
+ sigdelread_setup();
+ sigvd_setup();
+}
+
diff --git a/pd/src/d_fft.c b/pd/src/d_fft.c
new file mode 100644
index 00000000..7e5a95a3
--- /dev/null
+++ b/pd/src/d_fft.c
@@ -0,0 +1,342 @@
+/* Copyright (c) 1997-1999 Miller Puckette and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include "m_pd.h"
+
+/* ------------------------ fft~ and ifft~ -------------------------------- */
+static t_class *sigfft_class, *sigifft_class;
+
+typedef struct fft
+{
+ t_object x_obj;
+ float x_f;
+} t_sigfft;
+
+static void *sigfft_new(void)
+{
+ t_sigfft *x = (t_sigfft *)pd_new(sigfft_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ outlet_new(&x->x_obj, gensym("signal"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ x->x_f = 0;
+ return (x);
+}
+
+static void *sigifft_new(void)
+{
+ t_sigfft *x = (t_sigfft *)pd_new(sigifft_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ outlet_new(&x->x_obj, gensym("signal"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigfft_swap(t_int *w)
+{
+ float *in1 = (t_float *)(w[1]);
+ float *in2 = (t_float *)(w[2]);
+ int n = w[3];
+ for (;n--; in1++, in2++)
+ {
+ float f = *in1;
+ *in1 = *in2;
+ *in2 = f;
+ }
+ return (w+4);
+}
+
+static t_int *sigfft_perform(t_int *w)
+{
+ float *in1 = (t_float *)(w[1]);
+ float *in2 = (t_float *)(w[2]);
+ int n = w[3];
+ mayer_fft(n, in1, in2);
+ return (w+4);
+}
+
+static t_int *sigifft_perform(t_int *w)
+{
+ float *in1 = (t_float *)(w[1]);
+ float *in2 = (t_float *)(w[2]);
+ int n = w[3];
+ mayer_ifft(n, in1, in2);
+ return (w+4);
+}
+
+static void sigfft_dspx(t_sigfft *x, t_signal **sp, t_int *(*f)(t_int *w))
+{
+ int n = sp[0]->s_n;
+ float *in1 = sp[0]->s_vec;
+ float *in2 = sp[1]->s_vec;
+ float *out1 = sp[2]->s_vec;
+ float *out2 = sp[3]->s_vec;
+ if (out1 == in2 && out2 == in1)
+ dsp_add(sigfft_swap, 3, out1, out2, n);
+ else if (out1 == in2)
+ {
+ dsp_add(copy_perform, 3, in2, out2, n);
+ dsp_add(copy_perform, 3, in1, out1, n);
+ }
+ else
+ {
+ if (out1 != in1) dsp_add(copy_perform, 3, in1, out1, n);
+ if (out2 != in2) dsp_add(copy_perform, 3, in2, out2, n);
+ }
+ dsp_add(f, 3, sp[2]->s_vec, sp[3]->s_vec, n);
+}
+
+static void sigfft_dsp(t_sigfft *x, t_signal **sp)
+{
+ sigfft_dspx(x, sp, sigfft_perform);
+}
+
+static void sigifft_dsp(t_sigfft *x, t_signal **sp)
+{
+ sigfft_dspx(x, sp, sigifft_perform);
+}
+
+static void sigfft_setup(void)
+{
+ sigfft_class = class_new(gensym("fft~"), sigfft_new, 0,
+ sizeof(t_sigfft), 0, 0);
+ CLASS_MAINSIGNALIN(sigfft_class, t_sigfft, x_f);
+ class_addmethod(sigfft_class, (t_method)sigfft_dsp, gensym("dsp"), 0);
+
+ sigifft_class = class_new(gensym("ifft~"), sigifft_new, 0,
+ sizeof(t_sigfft), 0, 0);
+ CLASS_MAINSIGNALIN(sigifft_class, t_sigfft, x_f);
+ class_addmethod(sigifft_class, (t_method)sigifft_dsp, gensym("dsp"), 0);
+ class_sethelpsymbol(sigifft_class, gensym("fft~"));
+}
+
+/* ----------------------- rfft~ -------------------------------- */
+
+static t_class *sigrfft_class;
+
+typedef struct rfft
+{
+ t_object x_obj;
+ float x_f;
+} t_sigrfft;
+
+static void *sigrfft_new(void)
+{
+ t_sigrfft *x = (t_sigrfft *)pd_new(sigrfft_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigrfft_flip(t_int *w)
+{
+ float *in = (t_float *)(w[1]);
+ float *out = (t_float *)(w[2]);
+ int n = w[3];
+ while (n--) *(--out) = *in++;
+ *(--out) = 0; /* to hell with it */
+ return (w+4);
+}
+
+static t_int *sigrfft_perform(t_int *w)
+{
+ float *in = (t_float *)(w[1]);
+ int n = w[2];
+ mayer_realfft(n, in);
+ return (w+3);
+}
+
+static void sigrfft_dsp(t_sigrfft *x, t_signal **sp)
+{
+ int n = sp[0]->s_n, n2 = (n>>1);
+ float *in1 = sp[0]->s_vec;
+ float *out1 = sp[1]->s_vec;
+ float *out2 = sp[2]->s_vec;
+ if (n < 4)
+ {
+ error("fft: minimum 4 points");
+ return;
+ }
+ if (in1 == out2) /* this probably never happens */
+ {
+ dsp_add(sigrfft_perform, 2, out2, n);
+ dsp_add(copy_perform, 3, out2, out1, n2);
+ dsp_add(sigrfft_flip, 3, out2 + (n2+1), out2 + n2, n2-1);
+ }
+ else
+ {
+ if (in1 != out1) dsp_add(copy_perform, 3, in1, out1, n);
+ dsp_add(sigrfft_perform, 2, out1, n);
+ dsp_add(sigrfft_flip, 3, out1 + (n2+1), out2 + n2, n2-1);
+ }
+ dsp_add_zero(out1 + n2, n2);
+ dsp_add_zero(out2 + n2, n2);
+}
+
+static void sigrfft_setup(void)
+{
+ sigrfft_class = class_new(gensym("rfft~"), sigrfft_new, 0,
+ sizeof(t_sigrfft), 0, 0);
+ CLASS_MAINSIGNALIN(sigrfft_class, t_sigrfft, x_f);
+ class_addmethod(sigrfft_class, (t_method)sigrfft_dsp, gensym("dsp"), 0);
+ class_sethelpsymbol(sigrfft_class, gensym("fft~"));
+}
+
+/* ----------------------- rifft~ -------------------------------- */
+
+static t_class *sigrifft_class;
+
+typedef struct rifft
+{
+ t_object x_obj;
+ float x_f;
+} t_sigrifft;
+
+static void *sigrifft_new(void)
+{
+ t_sigrifft *x = (t_sigrifft *)pd_new(sigrifft_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigrifft_perform(t_int *w)
+{
+ float *in = (t_float *)(w[1]);
+ int n = w[2];
+ mayer_realifft(n, in);
+ return (w+3);
+}
+
+static void sigrifft_dsp(t_sigrifft *x, t_signal **sp)
+{
+ int n = sp[0]->s_n, n2 = (n>>1);
+ float *in1 = sp[0]->s_vec;
+ float *in2 = sp[1]->s_vec;
+ float *out1 = sp[2]->s_vec;
+ if (n < 4)
+ {
+ error("fft: minimum 4 points");
+ return;
+ }
+ if (in2 == out1)
+ {
+ dsp_add(sigrfft_flip, 3, out1+1, out1 + n, (n2-1));
+ dsp_add(copy_perform, 3, in1, out1, n2);
+ }
+ else
+ {
+ if (in1 != out1) dsp_add(copy_perform, 3, in1, out1, n2);
+ dsp_add(sigrfft_flip, 3, in2+1, out1 + n, n2-1);
+ }
+ dsp_add(sigrifft_perform, 2, out1, n);
+}
+
+static void sigrifft_setup(void)
+{
+ sigrifft_class = class_new(gensym("rifft~"), sigrifft_new, 0,
+ sizeof(t_sigrifft), 0, 0);
+ CLASS_MAINSIGNALIN(sigrifft_class, t_sigrifft, x_f);
+ class_addmethod(sigrifft_class, (t_method)sigrifft_dsp, gensym("dsp"), 0);
+ class_sethelpsymbol(sigrifft_class, gensym("fft~"));
+}
+
+/* ----------------------- framp~ -------------------------------- */
+
+static t_class *sigframp_class;
+
+typedef struct framp
+{
+ t_object x_obj;
+ float x_f;
+} t_sigframp;
+
+static void *sigframp_new(void)
+{
+ t_sigframp *x = (t_sigframp *)pd_new(sigframp_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ outlet_new(&x->x_obj, gensym("signal"));
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigframp_perform(t_int *w)
+{
+ float *inreal = (t_float *)(w[1]);
+ float *inimag = (t_float *)(w[2]);
+ float *outfreq = (t_float *)(w[3]);
+ float *outamp = (t_float *)(w[4]);
+ float lastreal = 0, currentreal = inreal[0], nextreal = inreal[1];
+ float lastimag = 0, currentimag = inimag[0], nextimag = inimag[1];
+ int n = w[5];
+ int m = n + 1;
+ float fbin = 1, oneovern2 = 1.f/((float)n * (float)n);
+
+ inreal += 2;
+ inimag += 2;
+ *outamp++ = *outfreq++ = 0;
+ n -= 2;
+ while (n--)
+ {
+ float re, im, pow, freq;
+ lastreal = currentreal;
+ currentreal = nextreal;
+ nextreal = *inreal++;
+ lastimag = currentimag;
+ currentimag = nextimag;
+ nextimag = *inimag++;
+ re = currentreal - 0.5f * (lastreal + nextreal);
+ im = currentimag - 0.5f * (lastimag + nextimag);
+ pow = re * re + im * im;
+ if (pow > 1e-19)
+ {
+ float detune = ((lastreal - nextreal) * re +
+ (lastimag - nextimag) * im) / (2.0f * pow);
+ if (detune > 2 || detune < -2) freq = pow = 0;
+ else freq = fbin + detune;
+ }
+ else freq = pow = 0;
+ *outfreq++ = freq;
+ *outamp++ = oneovern2 * pow;
+ fbin += 1.0f;
+ }
+ while (m--) *outamp++ = *outfreq++ = 0;
+ return (w+6);
+}
+
+t_int *sigsqrt_perform(t_int *w);
+
+static void sigframp_dsp(t_sigframp *x, t_signal **sp)
+{
+ int n = sp[0]->s_n, n2 = (n>>1);
+ if (n < 4)
+ {
+ error("framp: minimum 4 points");
+ return;
+ }
+ dsp_add(sigframp_perform, 5, sp[0]->s_vec, sp[1]->s_vec,
+ sp[2]->s_vec, sp[3]->s_vec, n2);
+ dsp_add(sigsqrt_perform, 3, sp[3]->s_vec, sp[3]->s_vec, n2);
+}
+
+static void sigframp_setup(void)
+{
+ sigframp_class = class_new(gensym("framp~"), sigframp_new, 0,
+ sizeof(t_sigframp), 0, 0);
+ CLASS_MAINSIGNALIN(sigframp_class, t_sigframp, x_f);
+ class_addmethod(sigframp_class, (t_method)sigframp_dsp, gensym("dsp"), 0);
+}
+
+/* ------------------------ global setup routine ------------------------- */
+
+void d_fft_setup(void)
+{
+ sigfft_setup();
+ sigrfft_setup();
+ sigrifft_setup();
+ sigframp_setup();
+}
diff --git a/pd/src/d_fftroutine.c b/pd/src/d_fftroutine.c
new file mode 100644
index 00000000..7f0d2523
--- /dev/null
+++ b/pd/src/d_fftroutine.c
@@ -0,0 +1,1001 @@
+/*****************************************************************************/
+/* */
+/* Fast Fourier Transform */
+/* Network Abstraction, Definitions */
+/* Kevin Peterson, MIT Media Lab, EMS */
+/* UROP - Fall '86 */
+/* REV: 6/12/87(KHP) - To incorporate link list of different sized networks */
+/* */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* added debug option 5/91 brown@nadia */
+/* change sign at AAA */
+/* */
+/* Fast Fourier Transform */
+/* FFT Network Interaction and Support Modules */
+/* Kevin Peterson, MIT Media Lab, EMS */
+/* UROP - Fall '86 */
+/* REV: 6/12/87(KHP) - Generalized to one procedure call with typed I/O */
+/* */
+/*****************************************************************************/
+
+/* Overview:
+
+ My realization of the FFT involves a representation of a network of
+ "butterfly" elements that takes a set of 'N' sound samples as input and
+ computes the discrete Fourier transform. This network consists of a
+ series of stages (log2 N), each stage consisting of N/2 parallel butterfly
+ elements. Consecutive stages are connected by specific, predetermined flow
+ paths, (see Oppenheim, Schafer for details) and each butterfly element has
+ an associated multiplicative coefficient.
+
+ FFT NETWORK:
+ -----------
+ ____ _ ____ _ ____ _ ____ _ ____
+ o--| |o-| |-o| |o-| |-o| |o-| |-o| |o-| |-o| |--o
+ |reg1| | | |W^r1| | | |reg1| | | |W^r1| | | |reg1|
+ | | | | | | | | | | | | | | | | | | .....
+ | | | | | | | | | | | | | | | | | |
+ o--|____|o-| |-o|____|o-| |-o|____|o-| |-o|____|o-| |-o|____|--o
+ | | | | | | | |
+ | | | | | | | |
+ ____ | | ____ | | ____ | | ____ | | ____
+ o--| |o-| |-o| |o-| |-o| |o-| |-o| |o-| |-o| |--o
+ |reg2| | | |W^r2| | | |reg2| | | |W^r2| | | |reg2|
+ | | | | | | | | | | | | | | | | | | .....
+ | | | | | | | | | | | | | | | | | |
+ o--|____|o-| |-o|____|o-| |-o|____|o-| |-o|____|o-| |-o|____|--o
+ | | | | | | | |
+ | | | | | | | |
+ : : : : : : : : :
+ : : : : : : : : :
+ : : : : : : : : :
+ : : : : : : : : :
+ : : : : : : : : :
+
+ ____ | | ____ | | ____ | | ____ | | ____
+ o--| |o-| |-o| |o-| |-o| |o-| |-o| |o-| |-o| |--o
+ |reg | | | |W^r | | | |reg | | | |W^r | | | |reg |
+ | N/2| | | | N/2| | | | N/2| | | | N/2| | | | N/2| .....
+ | | | | | | | | | | | | | | | | | |
+ o--|____|o-|_|-o|____|o-|_|-o|____|o-|_|-o|____|o-|_|-o|____|--o
+
+ ^ ^ ^ ^
+ Initial | Bttrfly | Rd/Wrt | Bttrfly | Rd/Wrt
+ Buffer | | Register | | Register
+ |____________|____________|____________|
+ |
+ |
+ Interconnect
+ Paths
+
+ The use of "in-place" computation permits one to use only one set of
+ registers realized by an array of complex number structures. To describe
+ the coefficients for each butterfly I am using a two dimensional array
+ (stage, butterfly) of complex numbers. The predetermined stage connections
+ will be described in a two dimensional array of indicies. These indicies
+ will be used to determine the order of reading at each stage of the
+ computation.
+*/
+
+
+/*****************************************************************************/
+/* INCLUDE FILES */
+/*****************************************************************************/
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+
+ /* the following is needed only to declare pd_fft() as exportable in NT */
+#include "m_pd.h"
+
+/* some basic definitions */
+#ifndef BOOL
+#define BOOL int
+#define TRUE 1
+#define FALSE 0
+#endif
+
+#define SAMPLE float /* data type used in calculation */
+
+#define SHORT_SIZE sizeof(short)
+#define INT_SIZE sizeof(int)
+#define FLOAT_SIZE sizeof(float)
+#define SAMPLE_SIZE sizeof(SAMPLE)
+#define PNTR_SIZE sizeof(char *)
+
+#define PI 3.1415927
+#define TWO_PI 6.2831854
+
+/* type definitions for I/O buffers */
+#define REAL 0 /* real only */
+#define IMAG 2 /* imaginary only */
+#define RECT 8 /* real and imaginary */
+#define MAG 16 /* magnitude only */
+#define PHASE 32 /* phase only */
+#define POLAR 64 /* magnitude and phase*/
+
+/* scale definitions for I/O buffers */
+#define LINEAR 0
+#define DB 1 /* 20log10 */
+
+/* transform direction definition */
+#define FORWARD 1 /* Forward FFT */
+#define INVERSE 2 /* Inverse FFT */
+
+/* window type definitions */
+#define HANNING 1
+#define RECTANGULAR 0
+
+
+
+/* network structure definition */
+
+typedef struct Tfft_net {
+ int n;
+ int stages;
+ int bps;
+ int direction;
+ int window_type;
+ int *load_index;
+ SAMPLE *window, *inv_window;
+ SAMPLE *regr;
+ SAMPLE *regi;
+ SAMPLE **indexpr;
+ SAMPLE **indexpi;
+ SAMPLE **indexqr;
+ SAMPLE **indexqi;
+ SAMPLE *coeffr, *inv_coeffr;
+ SAMPLE *coeffi, *inv_coeffi;
+ struct Tfft_net *next;
+} FFT_NET;
+
+
+void cfft(int trnsfrm_dir, int npnt, int window,
+ float *source_buf, int source_form, int source_scale,
+ float *result_buf, int result_form, int result_scale, int debug);
+
+
+/*****************************************************************************/
+/* GLOBAL DECLARATIONS */
+/*****************************************************************************/
+
+static FFT_NET *firstnet;
+
+/* prototypes */
+
+void net_alloc(FFT_NET *fft_net);
+void net_dealloc(FFT_NET *fft_net);
+int power_of_two(int n);
+void create_hanning(SAMPLE *window, int n, SAMPLE scale);
+void create_rectangular(SAMPLE *window, int n, SAMPLE scale);
+void short_to_float(short *short_buf, float *float_buf, int n);
+void load_registers(FFT_NET *fft_net, float *buf, int buf_form,
+ int buf_scale, int trnsfrm_dir);
+void compute_fft(FFT_NET *fft_net);
+void store_registers(FFT_NET *fft_net, float *buf, int buf_form,
+ int buf_scale, int debug);
+void build_fft_network(FFT_NET *fft_net, int n, int window_type);
+
+/*****************************************************************************/
+/* GENERALIZED FAST FOURIER TRANSFORM MODULE */
+/*****************************************************************************/
+
+void cfft(int trnsfrm_dir, int npnt, int window,
+ float *source_buf, int source_form, int source_scale,
+ float *result_buf, int result_form, int result_scale, int debug)
+
+/* modifies: result_buf
+ effects: Computes npnt FFT specified by form, scale, and dir parameters.
+ Source samples (single precision float) are taken from soure_buf and
+ the transfrmd representation is stored in result_buf (single precision
+ float). The parameters are defined as follows:
+
+ trnsfrm_dir = FORWARD | INVERSE
+ npnt = 2^k for some any positive integer k
+ window = HANNING | RECTANGULAR
+ (RECT = real and imag parts, POLAR = magnitude and phase)
+ source_form = REAL | IMAG | RECT | POLAR
+ result_form = REAL | IMAG | RECT | MAG | PHASE | POLAR
+ xxxxxx_scale= LINEAR | DB ( 20log10 |mag| )
+
+ The input/output buffers are stored in a form appropriate to the type.
+ For example: REAL => {real, real, real ...},
+ MAG => {mag, mag, mag, ... },
+ RECT => {real, imag, real, imag, ... },
+ POLAR => {mag, phase, mag, phase, ... }.
+
+ To look at the magnitude (in db) of a 1024 point FFT of a real time
+ signal we have:
+
+ fft(FORWARD, 1024, RECTANGULAR, input, REAL, LINEAR, output, MAG, DB)
+
+ All possible input and output combinations are possible given the
+ choice of type and scale parameters.
+*/
+
+{
+ FFT_NET *thisnet = (FFT_NET *)0;
+ FFT_NET *lastnet = (FFT_NET *)0;
+
+ /* A linked list of fft networks of different sizes is maintained to
+ avoid building with every call. The network is built on the first
+ call but reused for subsequent calls requesting the same size
+ transformation.
+ */
+
+ thisnet=firstnet;
+ while (thisnet) {
+ if (!(thisnet->n == npnt) || !(thisnet->window_type == window)) {
+ /* current net doesn't match size or window type */
+ lastnet=thisnet;
+ thisnet=thisnet->next;
+ continue; /* keep looking */
+ }
+
+ else { /* network matches desired size */
+ load_registers(thisnet, source_buf, source_form, source_scale,
+ trnsfrm_dir);
+ compute_fft(thisnet); /* do transformation */
+ store_registers(thisnet, result_buf, result_form, result_scale,debug);
+ return;
+ }
+ }
+
+ /* none of existing networks match required size*/
+
+ if (lastnet) { /* add new network to end of list */
+ thisnet = (FFT_NET *)malloc(sizeof(FFT_NET)); /* allocate */
+ thisnet->next = 0;
+ lastnet->next = thisnet; /* add to end of list */
+ }
+ else { /* first network to be created */
+ thisnet=firstnet=(FFT_NET *)malloc(sizeof(FFT_NET)); /* alloc. */
+ thisnet->next = 0;
+ }
+
+ /* build new network and compute transformation */
+ build_fft_network(thisnet, npnt, window);
+ load_registers(thisnet, source_buf, source_form, source_scale,
+ trnsfrm_dir);
+ compute_fft(thisnet);
+ store_registers(thisnet, result_buf, result_form, result_scale,debug);
+ return;
+}
+
+void fft_clear(void)
+
+/* effects: Deallocates all preserved FFT networks. Should be used when
+ finished with all computations.
+*/
+
+{
+ FFT_NET *thisnet, *nextnet;
+
+ if (firstnet) {
+ thisnet=firstnet;
+ do {
+ nextnet = thisnet->next;
+ net_dealloc(thisnet);
+ free((char *)thisnet);
+ } while (thisnet = nextnet);
+ }
+}
+
+
+/*****************************************************************************/
+/* NETWORK CONSTRUCTION */
+/*****************************************************************************/
+
+void build_fft_network(FFT_NET *fft_net, int n, int window_type)
+
+
+/* modifies:fft_net
+ effects: Constructs the fft network as described in fft.h. Butterfly
+ coefficients, read/write indicies, bit reversed load indicies,
+ and array allocations are computed.
+*/
+
+{
+ int cntr, i, j, s;
+ int stages, bps;
+ int **p, **q, *pp, *qp;
+ SAMPLE two_pi_div_n = TWO_PI / n;
+
+
+ /* network definition */
+ fft_net->n = n;
+ fft_net->bps = bps = n/2;
+ for (i = 0, j = n; j > 1; j >>= 1, i++);
+ fft_net->stages = stages = i;
+ fft_net->direction = FORWARD;
+ fft_net->window_type = window_type;
+ fft_net->next = (FFT_NET *)0;
+
+ /* allocate registers, index, coefficient arrays */
+ net_alloc(fft_net);
+
+
+ /* create appropriate windows */
+ if (window_type==HANNING) {
+ create_hanning(fft_net->window, n, 1.);
+ create_hanning(fft_net->inv_window, n, 1./n);
+ }
+ else {
+ create_rectangular(fft_net->window, n, 1.);
+ create_rectangular(fft_net->inv_window, n, 1./n);
+ }
+
+
+ /* calculate butterfly coefficients */ {
+
+ int num_diff_coeffs, power_inc, power;
+ SAMPLE *coeffpr = fft_net->coeffr;
+ SAMPLE *coeffpi = fft_net->coeffi;
+ SAMPLE *inv_coeffpr = fft_net->inv_coeffr;
+ SAMPLE *inv_coeffpi = fft_net->inv_coeffi;
+
+ /* stage one coeffs are 1 + 0j */
+ for (i = 0; i < bps; i++) {
+ *coeffpr = *inv_coeffpr = 1.;
+ *coeffpi = *inv_coeffpi = 0.;
+ coeffpr++; inv_coeffpr++;
+ coeffpi++; inv_coeffpi++;
+ }
+
+ /* stage 2 to last stage coeffs need calculation */
+ /* (1<<r <=> 2^r */
+ for (s = 2; s <= stages; s++) {
+
+ num_diff_coeffs = n / (1 << (stages - s + 1));
+ power_inc = 1 << (stages -s);
+ cntr = 0;
+
+ for (i = bps/num_diff_coeffs; i > 0; i--) {
+
+ power = 0;
+
+ for (j = num_diff_coeffs; j > 0; j--) {
+ *coeffpr = cos(two_pi_div_n*power);
+ *inv_coeffpr = cos(two_pi_div_n*power);
+/* AAA change these signs */ *coeffpi = -sin(two_pi_div_n*power);
+/* change back */ *inv_coeffpi = sin(two_pi_div_n*power);
+ power += power_inc;
+ coeffpr++; inv_coeffpr++;
+ coeffpi++; inv_coeffpi++;
+ }
+ }
+ }
+ }
+
+ /* calculate network indicies: stage exchange indicies are
+ calculated and then used as offset values from the base
+ register locations. The final addresses are then stored in
+ fft_net.
+ */ {
+
+ int index, inc;
+ SAMPLE **indexpr = fft_net->indexpr;
+ SAMPLE **indexpi = fft_net->indexpi;
+ SAMPLE **indexqr = fft_net->indexqr;
+ SAMPLE **indexqi = fft_net->indexqi;
+ SAMPLE *regr = fft_net->regr;
+ SAMPLE *regi = fft_net->regi;
+
+
+ /* allocate temporary 2d stage exchange index, 1d temp
+ load index */
+ p = (int **)malloc(stages * PNTR_SIZE);
+ q = (int **)malloc(stages * PNTR_SIZE);
+
+ for (s = 0; s < stages; s++) {
+ p[s] = (int *)malloc(bps * INT_SIZE);
+ q[s] = (int *)malloc(bps * INT_SIZE);
+ }
+
+ /* calculate stage exchange indicies: */
+ for (s = 0; s < stages; s++) {
+ pp = p[s];
+ qp = q[s];
+ inc = 1 << s;
+ cntr = 1 << (stages-s-1);
+ i = j = index = 0;
+
+ do {
+ do {
+ qp[i] = index + inc;
+ pp[i++] = index++;
+ } while (++j < inc);
+ index = qp[i-1] + 1;
+ j = 0;
+ } while (--cntr);
+ }
+
+ /* compute actual address values using indicies as offsets */
+ for (s = 0; s < stages; s++) {
+ for (i = 0; i < bps; i++) {
+ *indexpr++ = regr + p[s][i];
+ *indexpi++ = regi + p[s][i];
+ *indexqr++ = regr + q[s][i];
+ *indexqi++ = regi + q[s][i];
+ }
+ }
+ }
+
+
+ /* calculate load indicies (bit reverse ordering) */
+ /* bit reverse ordering achieved by passing normal
+ order indicies backwards through the network */
+
+ /* init to normal order indicies */ {
+ int *load_index,*load_indexp;
+ int *temp_indexp, *temp_index;
+ temp_index=temp_indexp=(int *)malloc(n * INT_SIZE);
+
+ i = 0; j = n;
+ load_index = load_indexp = fft_net->load_index;
+
+ while (j--)
+ *load_indexp++ = i++;
+
+ /* pass indicies backwards through net */
+ for (s = stages - 1; s > 0; s--) {
+ pp = p[s];
+ qp = q[s];
+
+ for (i = 0; i < bps; i++) {
+ temp_index[pp[i]]=load_index[2*i];
+ temp_index[qp[i]]=load_index[2*i+1];
+ }
+ j = n;
+ load_indexp = load_index;
+ temp_indexp = temp_index;
+ while (j--)
+ *load_indexp++ = *temp_indexp++;
+ }
+
+ /* free all temporary arrays */
+ free((char *)temp_index);
+ for (s = 0; s < stages; s++) {
+ free((char *)p[s]);free((char *)q[s]);
+ }
+ free((char *)p);free((char *)q);
+ }
+}
+
+
+
+/*****************************************************************************/
+/* REGISTER LOAD AND STORE */
+/*****************************************************************************/
+
+void load_registers(FFT_NET *fft_net, float *buf, int buf_form,
+ int buf_scale, int trnsfrm_dir)
+
+/* effects: Multiplies the input buffer with the appropriate window and
+ stores the resulting values in the initial registers of the
+ network. Input buffer must contain values appropriate to form.
+ For RECT, the buffer contains real num. followed by imag num,
+ and for POLAR, it contains magnitude followed by phase. Pure
+ inputs are listed normally. Both LINEAR and DB scales are
+ interpreted.
+*/
+
+{
+ int *load_index = fft_net->load_index;
+ SAMPLE *window;
+ int index, i = 0, n = fft_net->n;
+
+ if (trnsfrm_dir==FORWARD) window = fft_net->window;
+ else if (trnsfrm_dir==INVERSE) window = fft_net->inv_window;
+ else {
+ fprintf(stderr, "load_registers:illegal transform direction\n");
+ exit(0);
+ }
+ fft_net->direction = trnsfrm_dir;
+
+ switch(buf_scale) {
+ case LINEAR: {
+
+ switch (buf_form) {
+ case REAL: { /* pure REAL */
+ while (i < fft_net->n) {
+ index = load_index[i];
+ fft_net->regr[i]=(SAMPLE)buf[index] * window[index];
+ fft_net->regi[i]=0.;
+ i++;
+ }
+ } break;
+
+ case IMAG: { /* pure IMAGinary */
+ while (i < fft_net->n) {
+ index = load_index[i];
+ fft_net->regr[i]=0;
+ fft_net->regi[i]=(SAMPLE)buf[index] * window[index];
+ i++;
+ }
+ } break;
+
+ case RECT: { /* both REAL and IMAGinary */
+ while (i < fft_net->n) {
+ index = load_index[i];
+ fft_net->regr[i]=(SAMPLE)buf[index*2] * window[index];
+ fft_net->regi[i]=(SAMPLE)buf[index*2+1] * window[index];
+ i++;
+ }
+ } break;
+
+ case POLAR: { /* magnitude followed by phase */
+ while (i < fft_net->n) {
+ index = load_index[i];
+ fft_net->regr[i]=(SAMPLE)(buf[index*2] * cos(buf[index*2+1]))
+ * window[index];
+ fft_net->regi[i]=(SAMPLE)(buf[index*2] * sin(buf[index*2+1]))
+ * window[index];
+ i++;
+ }
+ } break;
+
+ default: {
+ fprintf(stderr, "load_registers:illegal input form\n");
+ exit(0);
+ } break;
+ }
+ } break;
+
+ case DB: {
+
+ switch (buf_form) {
+ case REAL: { /* log pure REAL */
+ while (i < fft_net->n) {
+ index = load_index[i];
+ fft_net->regr[i]=(SAMPLE)pow(10., (1./20.)*buf[index])
+ * window[index]; /* window scaling after linearization */
+ fft_net->regi[i]=0.;
+ i++;
+ }
+ } break;
+
+ case IMAG: { /* log pure IMAGinary */
+ while (i < fft_net->n) {
+ index = load_index[i];
+ fft_net->regr[i]=0.;
+ fft_net->regi[i]=(SAMPLE)pow(10., (1./20.)*buf[index])
+ * window[index];
+ i++;
+ }
+ } break;
+
+ case RECT: { /* log REAL and log IMAGinary */
+ while (i < fft_net->n) {
+ index = load_index[i];
+ fft_net->regr[i]=(SAMPLE)pow(10., (1./20.)*buf[index*2])
+ * window[index];
+ fft_net->regi[i]=(SAMPLE)pow(10., (1./20.)*buf[index*2+1])
+ * window[index];
+ i++;
+ }
+ } break;
+
+ case POLAR: { /* log mag followed by phase */
+ while (i < fft_net->n) {
+ index = load_index[i];
+ fft_net->regr[i]=(SAMPLE)(pow(10., (1./20.)*buf[index*2])
+ * cos(buf[index*2+1])) * window[index];
+ fft_net->regi[i]=(SAMPLE)(pow(10., (1./20.)*buf[index*2])
+ * sin(buf[index*2+1])) * window[index];
+ i++;
+ }
+ } break;
+
+ default: {
+ fprintf(stderr, "load_registers:illegal input form\n");
+ exit(0);
+ } break;
+ }
+ } break;
+
+ default: {
+ fprintf(stderr, "load_registers:illegal input scale\n");
+ exit(0);
+ } break;
+ }
+}
+
+
+void store_registers(FFT_NET *fft_net, float *buf, int buf_form,
+ int buf_scale, int debug)
+
+/* modifies: buf
+ effects: Writes the final contents of the network registers into buf in
+ either linear or db scale, polar or rectangular form. If any of
+ the pure forms(REAL, IMAG, MAG, or PHASE) are used then only the
+ corresponding part of the registers is stored in buf.
+*/
+
+{
+ int i;
+ SAMPLE real, imag, mag, phase;
+ int n;
+
+ i = 0;
+ n = fft_net->n;
+
+ switch (buf_scale) {
+ case LINEAR: {
+
+ switch (buf_form) {
+ case REAL: { /* pure REAL */
+ do {
+ *buf++ = (float)fft_net->regr[i];
+ } while (++i < n);
+ } break;
+
+ case IMAG: { /* pure IMAGinary */
+ do {
+ *buf++ = (float)fft_net->regi[i];
+ } while (++i < n);
+ } break;
+
+ case RECT: { /* both REAL and IMAGinary */
+ do {
+ *buf++ = (float)fft_net->regr[i];
+ *buf++ = (float)fft_net->regi[i];
+ } while (++i < n);
+ } break;
+
+ case MAG: { /* magnitude only */
+ do {
+ real = fft_net->regr[i];
+ imag = fft_net->regi[i];
+ *buf++ = (float)sqrt(real*real+imag*imag);
+ } while (++i < n);
+ } break;
+
+ case PHASE: { /* phase only */
+ do {
+ real = fft_net->regr[i];
+ imag = fft_net->regi[i];
+ if (real > .00001)
+ *buf++ = (float)atan2(imag, real);
+ else { /* deal with bad case */
+ if (imag > 0){ *buf++ = PI / 2.;
+ if(debug) fprintf(stderr,"real=0 and imag > 0\n");}
+ else if (imag < 0){ *buf++ = -PI / 2.;
+ if(debug) fprintf(stderr,"real=0 and imag < 0\n");}
+ else { *buf++ = 0;
+ if(debug) fprintf(stderr,"real=0 and imag=0\n");}
+ }
+ } while (++i < n);
+ } break;
+
+ case POLAR: { /* magnitude and phase */
+ do {
+ real = fft_net->regr[i];
+ imag = fft_net->regi[i];
+ *buf++ = (float)sqrt(real*real+imag*imag);
+ if (real) /* a hack to avoid div by zero */
+ *buf++ = (float)atan2(imag, real);
+ else { /* deal with bad case */
+ if (imag > 0) *buf++ = PI / 2.;
+ else if (imag < 0) *buf++ = -PI / 2.;
+ else *buf++ = 0;
+ }
+ } while (++i < n);
+ } break;
+
+ default: {
+ fprintf(stderr, "store_registers:illegal output form\n");
+ exit(0);
+ } break;
+ }
+ } break;
+
+ case DB: {
+
+ switch (buf_form) {
+ case REAL: { /* real only */
+ do {
+ *buf++ = (float)20.*log10(fft_net->regr[i]);
+ } while (++i < n);
+ } break;
+
+ case IMAG: { /* imag only */
+ do {
+ *buf++ = (float)20.*log10(fft_net->regi[i]);
+ } while (++i < n);
+ } break;
+
+ case RECT: { /* real and imag */
+ do {
+ *buf++ = (float)20.*log10(fft_net->regr[i]);
+ *buf++ = (float)20.*log10(fft_net->regi[i]);
+ } while (++i < n);
+ } break;
+
+ case MAG: { /* magnitude only */
+ do {
+ real = fft_net->regr[i];
+ imag = fft_net->regi[i];
+ *buf++ = (float)20.*log10(sqrt(real*real+imag*imag));
+ } while (++i < n);
+ } break;
+
+ case PHASE: { /* phase only */
+ do {
+ real = fft_net->regr[i];
+ imag = fft_net->regi[i];
+ if (real)
+ *buf++ = (float)atan2(imag, real);
+ else { /* deal with bad case */
+ if (imag > 0) *buf++ = PI / 2.;
+ else if (imag < 0) *buf++ = -PI / 2.;
+ else *buf++ = 0;
+ }
+ } while (++i < n);
+ } break;
+
+ case POLAR: { /* magnitude and phase */
+ do {
+ real = fft_net->regr[i];
+ imag = fft_net->regi[i];
+ *buf++ = (float)20.*log10(sqrt(real*real+imag*imag));
+ if (real)
+ *buf++ = (float)atan2(imag, real);
+ else { /* deal with bad case */
+ if (imag > 0) *buf++ = PI / 2.;
+ else if (imag < 0) *buf++ = -PI / 2.;
+ else *buf++ = 0;
+ }
+ } while (++i < n);
+ } break;
+
+ default: {
+ fprintf(stderr, "store_registers:illegal output form\n");
+ exit(0);
+ } break;
+ }
+ } break;
+
+ default: {
+ fprintf(stderr, "store_registers:illegal output scale\n");
+ exit(0);
+ } break;
+ }
+}
+
+
+
+/*****************************************************************************/
+/* COMPUTE TRANSFORMATION */
+/*****************************************************************************/
+
+void compute_fft(FFT_NET *fft_net)
+
+
+/* modifies: fft_net
+ effects: Passes the values (already loaded) in the registers through
+ the network, multiplying with appropriate coefficients at each
+ stage. The fft result will be in the registers at the end of
+ the computation. The direction of the transformation is indicated
+ by the network flag 'direction'. The form of the computation is:
+
+ X(pn) = X(p) + C*X(q)
+ X(qn) = X(p) - C*X(q)
+
+ where X(pn,qn) represents the output of the registers at each stage.
+ The calculations are actually done in place. Register pointers are
+ used to speed up the calculations.
+
+ Register and coefficient addresses involved in the calculations
+ are stored sequentially and are accessed as such. fft_net->indexp,
+ indexq contain pointers to the relevant addresses, and fft_net->coeffs,
+ inv_coeffs points to the appropriate coefficients at each stage of the
+ computation.
+*/
+
+{
+ SAMPLE **xpr, **xpi, **xqr, **xqi, *cr, *ci;
+ int i;
+ SAMPLE tpr, tpi, tqr, tqi;
+ int bps = fft_net->bps;
+ int cnt = bps * (fft_net->stages - 1);
+
+ /* predetermined register addresses and coefficients */
+ xpr = fft_net->indexpr;
+ xpi = fft_net->indexpi;
+ xqr = fft_net->indexqr;
+ xqi = fft_net->indexqi;
+
+ if (fft_net->direction==FORWARD) { /* FORWARD FFT coefficients */
+ cr = fft_net->coeffr;
+ ci = fft_net->coeffi;
+ }
+ else { /* INVERSE FFT coefficients */
+ cr = fft_net->inv_coeffr;
+ ci = fft_net->inv_coeffi;
+ }
+
+ /* stage one coefficients are 1 + 0j so C*X(q)=X(q) */
+ /* bps mults can be avoided */
+
+ for (i = 0; i < bps; i++) {
+
+ /* add X(p) and X(q) */
+ tpr = **xpr + **xqr;
+ tpi = **xpi + **xqi;
+ tqr = **xpr - **xqr;
+ tqi = **xpi - **xqi;
+
+ /* exchange register with temp */
+ **xpr = tpr;
+ **xpi = tpi;
+ **xqr = tqr;
+ **xqi = tqi;
+
+ /* next set of register for calculations: */
+ xpr++; xpi++; xqr++; xqi++; cr++; ci++;
+
+ }
+
+ for (i = 0; i < cnt; i++) {
+
+ /* mult X(q) by coeff C */
+ tqr = **xqr * *cr - **xqi * *ci;
+ tqi = **xqr * *ci + **xqi * *cr;
+
+ /* exchange register with temp */
+ **xqr = tqr;
+ **xqi = tqi;
+
+ /* add X(p) and X(q) */
+ tpr = **xpr + **xqr;
+ tpi = **xpi + **xqi;
+ tqr = **xpr - **xqr;
+ tqi = **xpi - **xqi;
+
+ /* exchange register with temp */
+ **xpr = tpr;
+ **xpi = tpi;
+ **xqr = tqr;
+ **xqi = tqi;
+ /* next set of register for calculations: */
+ xpr++; xpi++; xqr++; xqi++; cr++; ci++;
+ }
+}
+
+
+/****************************************************************************/
+/* SUPPORT MODULES */
+/****************************************************************************/
+
+void net_alloc(FFT_NET *fft_net)
+
+
+/* effects: Allocates appropriate two dimensional arrays and assigns
+ correct internal pointers.
+*/
+
+{
+
+ int stages, bps, n;
+
+ n = fft_net->n;
+ stages = fft_net->stages;
+ bps = fft_net->bps;
+
+
+ /* two dimensional arrays with elements stored sequentially */
+
+ fft_net->load_index = (int *)malloc(n * INT_SIZE);
+ fft_net->regr = (SAMPLE *)malloc(n * SAMPLE_SIZE);
+ fft_net->regi = (SAMPLE *)malloc(n * SAMPLE_SIZE);
+ fft_net->coeffr = (SAMPLE *)malloc(stages*bps*SAMPLE_SIZE);
+ fft_net->coeffi = (SAMPLE *)malloc(stages*bps*SAMPLE_SIZE);
+ fft_net->inv_coeffr = (SAMPLE *)malloc(stages*bps*SAMPLE_SIZE);
+ fft_net->inv_coeffi = (SAMPLE *)malloc(stages*bps*SAMPLE_SIZE);
+ fft_net->indexpr = (SAMPLE **)malloc(stages * bps * PNTR_SIZE);
+ fft_net->indexpi = (SAMPLE **)malloc(stages * bps * PNTR_SIZE);
+ fft_net->indexqr = (SAMPLE **)malloc(stages * bps * PNTR_SIZE);
+ fft_net->indexqi = (SAMPLE **)malloc(stages * bps * PNTR_SIZE);
+
+ /* one dimensional load window */
+ fft_net->window = (SAMPLE *)malloc(n * SAMPLE_SIZE);
+ fft_net->inv_window = (SAMPLE *)malloc(n * SAMPLE_SIZE);
+}
+
+void net_dealloc(FFT_NET *fft_net)
+
+
+/* effects: Deallocates given FFT network.
+*/
+
+{
+
+ free((char *)fft_net->load_index);
+ free((char *)fft_net->regr);
+ free((char *)fft_net->regi);
+ free((char *)fft_net->coeffr);
+ free((char *)fft_net->coeffi);
+ free((char *)fft_net->inv_coeffr);
+ free((char *)fft_net->inv_coeffi);
+ free((char *)fft_net->indexpr);
+ free((char *)fft_net->indexpi);
+ free((char *)fft_net->indexqr);
+ free((char *)fft_net->indexqi);
+ free((char *)fft_net->window);
+ free((char *)fft_net->inv_window);
+}
+
+
+BOOL power_of_two(n)
+
+int n;
+
+/* effects: Returns TRUE if n is a power of two, otherwise FALSE.
+*/
+
+{
+ int i;
+
+ for (i = n; i > 1; i >>= 1)
+ if (i & 1) return FALSE; /* more than one bit high */
+ return TRUE;
+}
+
+
+void create_hanning(SAMPLE *window, int n, SAMPLE scale)
+
+/* effects: Fills the buffer window with a hanning window of the appropriate
+ size scaled by scale.
+*/
+
+{
+ SAMPLE a, pi_div_n = PI/n;
+ int k;
+
+ for (k=1; k <= n; k++) {
+ a = sin(k * pi_div_n);
+ *window++ = scale * a * a;
+ }
+}
+
+
+void create_rectangular(SAMPLE *window, int n, SAMPLE scale)
+
+/* effects: Fills the buffer window with a rectangular window of the
+ appropriate size of height scale.
+*/
+
+{
+ while (n--)
+ *window++ = scale;
+}
+
+
+void short_to_float(short *short_buf, float *float_buf, int n)
+
+/* effects; Converts short_buf to floats and stores them in float_buf.
+*/
+
+{
+ while (n--) {
+ *float_buf++ = (float)*short_buf++;
+ }
+}
+
+
+/* here's the meat: */
+
+void pd_fft(float *buf, int npoints, int inverse)
+{
+ double renorm;
+ float *fp, *fp2;
+ int i;
+ renorm = (inverse ? npoints : 1.);
+ cfft((inverse ? INVERSE : FORWARD), npoints, RECTANGULAR,
+ buf, RECT, LINEAR, buf, RECT, LINEAR, 0);
+ for (i = npoints << 1, fp = buf; i--; fp++) *fp *= renorm;
+}
diff --git a/pd/src/d_filter.c b/pd/src/d_filter.c
new file mode 100644
index 00000000..1693cd85
--- /dev/null
+++ b/pd/src/d_filter.c
@@ -0,0 +1,534 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* "filters", both linear and nonlinear.
+*/
+
+#include "m_pd.h"
+#include <math.h>
+
+/* ---------------- hip~ - 1-pole 1-zero hipass filter. ----------------- */
+
+typedef struct hipctl
+{
+ float c_x;
+ float c_coef;
+} t_hipctl;
+
+typedef struct sighip
+{
+ t_object x_obj;
+ float x_sr;
+ float x_hz;
+ t_hipctl x_cspace;
+ t_hipctl *x_ctl;
+ float x_f;
+} t_sighip;
+
+t_class *sighip_class;
+static void sighip_ft1(t_sighip *x, t_floatarg f);
+
+static void *sighip_new(t_floatarg f)
+{
+ t_sighip *x = (t_sighip *)pd_new(sighip_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1"));
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_sr = 44100;
+ x->x_ctl = &x->x_cspace;
+ x->x_cspace.c_x = 0;
+ sighip_ft1(x, f);
+ x->x_f = 0;
+ return (x);
+}
+
+static void sighip_ft1(t_sighip *x, t_floatarg f)
+{
+ if (f < 0.001) f = 10;
+ x->x_hz = f;
+ x->x_ctl->c_coef = 1 - f * (2 * 3.14159) / x->x_sr;
+ if (x->x_ctl->c_coef < 0) x->x_ctl->c_coef = 0;
+}
+
+static t_int *sighip_perform(t_int *w)
+{
+ float *in = (float *)(w[1]);
+ float *out = (float *)(w[2]);
+ t_hipctl *c = (t_hipctl *)(w[3]);
+ int n = (t_int)(w[4]);
+ int i;
+ float last = c->c_x;
+ float coef = c->c_coef;
+ for (i = 0; i < n; i++)
+ {
+ float new = *in++ + coef * last;
+ *out++ = new - last;
+ last = new;
+ }
+ /* NAN protect */
+ if (!((last <= 0) || (last >= 0)))
+ last = 0;
+ c->c_x = last;
+ return (w+5);
+}
+
+static void sighip_dsp(t_sighip *x, t_signal **sp)
+{
+ x->x_sr = sp[0]->s_sr;
+ sighip_ft1(x, x->x_hz);
+ dsp_add(sighip_perform, 4,
+ sp[0]->s_vec, sp[1]->s_vec,
+ x->x_ctl, sp[0]->s_n);
+
+}
+
+static void sighip_clear(t_sighip *x, t_floatarg q)
+{
+ x->x_cspace.c_x = 0;
+}
+
+void sighip_setup(void)
+{
+ sighip_class = class_new(gensym("hip~"), (t_newmethod)sighip_new, 0,
+ sizeof(t_sighip), 0, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(sighip_class, t_sighip, x_f);
+ class_addmethod(sighip_class, (t_method)sighip_dsp, gensym("dsp"), 0);
+ class_addmethod(sighip_class, (t_method)sighip_ft1,
+ gensym("ft1"), A_FLOAT, 0);
+ class_addmethod(sighip_class, (t_method)sighip_clear, gensym("clear"), 0);
+}
+
+/* ---------------- lop~ - 1-pole lopass filter. ----------------- */
+
+typedef struct lopctl
+{
+ float c_x;
+ float c_coef;
+} t_lopctl;
+
+typedef struct siglop
+{
+ t_object x_obj;
+ float x_sr;
+ float x_hz;
+ t_lopctl x_cspace;
+ t_lopctl *x_ctl;
+ float x_f;
+} t_siglop;
+
+t_class *siglop_class;
+
+static void siglop_ft1(t_siglop *x, t_floatarg f);
+
+static void *siglop_new(t_floatarg f)
+{
+ t_siglop *x = (t_siglop *)pd_new(siglop_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1"));
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_sr = 44100;
+ x->x_ctl = &x->x_cspace;
+ x->x_cspace.c_x = 0;
+ siglop_ft1(x, f);
+ x->x_f = 0;
+ return (x);
+}
+
+static void siglop_ft1(t_siglop *x, t_floatarg f)
+{
+ if (f < 0.001) f = 10;
+ x->x_hz = f;
+ x->x_ctl->c_coef = f * (2 * 3.14159) / x->x_sr;
+ if (x->x_ctl->c_coef > 1) x->x_ctl->c_coef = 1;
+}
+
+static void siglop_clear(t_siglop *x, t_floatarg q)
+{
+ x->x_cspace.c_x = 0;
+}
+
+static t_int *siglop_perform(t_int *w)
+{
+ float *in = (float *)(w[1]);
+ float *out = (float *)(w[2]);
+ t_lopctl *c = (t_lopctl *)(w[3]);
+ int n = (t_int)(w[4]);
+ int i;
+ float last = c->c_x;
+ float coef = c->c_coef;
+ float feedback = 1 - coef;
+ for (i = 0; i < n; i++)
+ last = *out++ = coef * *in++ + feedback * last;
+ /* NAN protect */
+ if (!((last <= 0) || (last >= 0)))
+ last = 0;
+ c->c_x = last;
+ return (w+5);
+}
+
+static void siglop_dsp(t_siglop *x, t_signal **sp)
+{
+ x->x_sr = sp[0]->s_sr;
+ siglop_ft1(x, x->x_hz);
+ dsp_add(siglop_perform, 4,
+ sp[0]->s_vec, sp[1]->s_vec,
+ x->x_ctl, sp[0]->s_n);
+
+}
+
+void siglop_setup(void)
+{
+ siglop_class = class_new(gensym("lop~"), (t_newmethod)siglop_new, 0,
+ sizeof(t_siglop), 0, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(siglop_class, t_siglop, x_f);
+ class_addmethod(siglop_class, (t_method)siglop_dsp, gensym("dsp"), 0);
+ class_addmethod(siglop_class, (t_method)siglop_ft1,
+ gensym("ft1"), A_FLOAT, 0);
+ class_addmethod(siglop_class, (t_method)siglop_clear, gensym("clear"), 0);
+}
+
+/* ---------------- bp~ - 2-pole bandpass filter. ----------------- */
+
+typedef struct bpctl
+{
+ float c_x1;
+ float c_x2;
+ float c_coef1;
+ float c_coef2;
+ float c_gain;
+} t_bpctl;
+
+typedef struct sigbp
+{
+ t_object x_obj;
+ float x_sr;
+ float x_freq;
+ float x_q;
+ t_bpctl x_cspace;
+ t_bpctl *x_ctl;
+ float x_f;
+} t_sigbp;
+
+t_class *sigbp_class;
+
+static void sigbp_docoef(t_sigbp *x, t_floatarg f, t_floatarg q);
+
+static void *sigbp_new(t_floatarg f, t_floatarg q)
+{
+ t_sigbp *x = (t_sigbp *)pd_new(sigbp_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft2"));
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_sr = 44100;
+ x->x_ctl = &x->x_cspace;
+ x->x_cspace.c_x1 = 0;
+ x->x_cspace.c_x2 = 0;
+ sigbp_docoef(x, f, q);
+ x->x_f = 0;
+ return (x);
+}
+
+static float sigbp_qcos(float f)
+{
+ if (f >= -(0.5f*3.14159f) && f <= 0.5f*3.14159f)
+ {
+ float g = f*f;
+ return (((g*g*g * (-1.0f/720.0f) + g*g*(1.0f/24.0f)) - g*0.5) + 1);
+ }
+ else return (0);
+}
+
+static void sigbp_docoef(t_sigbp *x, t_floatarg f, t_floatarg q)
+{
+ float r, oneminusr, omega;
+ if (f < 0.001) f = 10;
+ if (q < 0) q = 0;
+ x->x_freq = f;
+ x->x_q = q;
+ omega = f * (2.0f * 3.14159f) / x->x_sr;
+ if (q < 0.001) oneminusr = 1.0f;
+ else oneminusr = omega/q;
+ if (oneminusr > 1.0f) oneminusr = 1.0f;
+ r = 1.0f - oneminusr;
+ x->x_ctl->c_coef1 = 2.0f * sigbp_qcos(omega) * r;
+ x->x_ctl->c_coef2 = - r * r;
+ x->x_ctl->c_gain = 2 * oneminusr * (oneminusr + r * omega);
+ /* post("r %f, omega %f, coef1 %f, coef2 %f",
+ r, omega, x->x_ctl->c_coef1, x->x_ctl->c_coef2); */
+}
+
+static void sigbp_ft1(t_sigbp *x, t_floatarg f)
+{
+ sigbp_docoef(x, f, x->x_q);
+}
+
+static void sigbp_ft2(t_sigbp *x, t_floatarg q)
+{
+ sigbp_docoef(x, x->x_freq, q);
+}
+
+static void sigbp_clear(t_sigbp *x, t_floatarg q)
+{
+ x->x_ctl->c_x1 = x->x_ctl->c_x2 = 0;
+}
+
+static t_int *sigbp_perform(t_int *w)
+{
+ float *in = (float *)(w[1]);
+ float *out = (float *)(w[2]);
+ t_bpctl *c = (t_bpctl *)(w[3]);
+ int n = (t_int)(w[4]);
+ int i;
+ float last = c->c_x1;
+ float prev = c->c_x2;
+ float coef1 = c->c_coef1;
+ float coef2 = c->c_coef2;
+ float gain = c->c_gain;
+ for (i = 0; i < n; i++)
+ {
+ float output = *in++ + coef1 * last + coef2 * prev;
+ *out++ = gain * output;
+ prev = last;
+ last = output;
+ }
+ /* NAN protect */
+ if (!((last <= 0) || (last >= 0)))
+ last = 0;
+ if (!((prev <= 0) || (prev >= 0)))
+ prev = 0;
+ c->c_x1 = last;
+ c->c_x2 = prev;
+ return (w+5);
+}
+
+static void sigbp_dsp(t_sigbp *x, t_signal **sp)
+{
+ x->x_sr = sp[0]->s_sr;
+ sigbp_docoef(x, x->x_freq, x->x_q);
+ dsp_add(sigbp_perform, 4,
+ sp[0]->s_vec, sp[1]->s_vec,
+ x->x_ctl, sp[0]->s_n);
+
+}
+
+void sigbp_setup(void)
+{
+ sigbp_class = class_new(gensym("bp~"), (t_newmethod)sigbp_new, 0,
+ sizeof(t_sigbp), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(sigbp_class, t_sigbp, x_f);
+ class_addmethod(sigbp_class, (t_method)sigbp_dsp, gensym("dsp"), 0);
+ class_addmethod(sigbp_class, (t_method)sigbp_ft1,
+ gensym("ft1"), A_FLOAT, 0);
+ class_addmethod(sigbp_class, (t_method)sigbp_ft2,
+ gensym("ft2"), A_FLOAT, 0);
+ class_addmethod(sigbp_class, (t_method)sigbp_clear, gensym("clear"), 0);
+}
+
+/* ---------------- biquad~ - raw biquad filter ----------------- */
+
+typedef struct biquadctl
+{
+ float c_x1;
+ float c_x2;
+ float c_fb1;
+ float c_fb2;
+ float c_ff1;
+ float c_ff2;
+ float c_ff3;
+} t_biquadctl;
+
+typedef struct sigbiquad
+{
+ t_object x_obj;
+ float x_f;
+ t_biquadctl x_cspace;
+ t_biquadctl *x_ctl;
+} t_sigbiquad;
+
+t_class *sigbiquad_class;
+
+static void sigbiquad_list(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv);
+
+static void *sigbiquad_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_sigbiquad *x = (t_sigbiquad *)pd_new(sigbiquad_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_ctl = &x->x_cspace;
+ x->x_cspace.c_x1 = x->x_cspace.c_x2 = 0;
+ sigbiquad_list(x, s, argc, argv);
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigbiquad_perform(t_int *w)
+{
+ float *in = (float *)(w[1]);
+ float *out = (float *)(w[2]);
+ t_biquadctl *c = (t_biquadctl *)(w[3]);
+ int n = (t_int)(w[4]);
+ int i;
+ float last = c->c_x1;
+ float prev = c->c_x2;
+ float fb1 = c->c_fb1;
+ float fb2 = c->c_fb2;
+ float ff1 = c->c_ff1;
+ float ff2 = c->c_ff2;
+ float ff3 = c->c_ff3;
+ for (i = 0; i < n; i++)
+ {
+ float output = *in++ + fb1 * last + fb2 * prev;
+ *out++ = ff1 * output + ff2 * last + ff3 * prev;
+ prev = last;
+ last = output;
+ }
+ c->c_x1 = last;
+ c->c_x2 = prev;
+ return (w+5);
+}
+
+static void sigbiquad_list(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv)
+{
+ float fb1 = atom_getfloatarg(0, argc, argv);
+ float fb2 = atom_getfloatarg(1, argc, argv);
+ float ff1 = atom_getfloatarg(2, argc, argv);
+ float ff2 = atom_getfloatarg(3, argc, argv);
+ float ff3 = atom_getfloatarg(4, argc, argv);
+ float discriminant = fb1 * fb1 + 4 * fb2;
+ t_biquadctl *c = x->x_ctl;
+ if (discriminant < 0) /* imaginary roots -- resonant filter */
+ {
+ /* they're conjugates so we just check that the product
+ is less than one */
+ if (fb2 >= -1.0f) goto stable;
+ }
+ else /* real roots */
+ {
+ /* check that the parabola 1 - fb1 x - fb2 x^2 has a
+ vertex between -1 and 1, and that it's nonnegative
+ at both ends, which implies both roots are in [1-,1]. */
+ if (fb1 <= 2.0f && fb1 >= -2.0f &&
+ 1.0f - fb1 -fb2 >= 0 && 1.0f + fb1 - fb2 >= 0)
+ goto stable;
+ }
+ /* if unstable, just bash to zero */
+ fb1 = fb2 = ff1 = ff2 = ff3 = 0;
+stable:
+ c->c_fb1 = fb1;
+ c->c_fb2 = fb2;
+ c->c_ff1 = ff1;
+ c->c_ff2 = ff2;
+ c->c_ff3 = ff3;
+}
+
+static void sigbiquad_set(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_biquadctl *c = x->x_ctl;
+ c->c_x1 = atom_getfloatarg(0, argc, argv);
+ c->c_x2 = atom_getfloatarg(1, argc, argv);
+}
+
+static void sigbiquad_dsp(t_sigbiquad *x, t_signal **sp)
+{
+ dsp_add(sigbiquad_perform, 4,
+ sp[0]->s_vec, sp[1]->s_vec,
+ x->x_ctl, sp[0]->s_n);
+
+}
+
+void sigbiquad_setup(void)
+{
+ sigbiquad_class = class_new(gensym("biquad~"), (t_newmethod)sigbiquad_new,
+ 0, sizeof(t_sigbiquad), 0, A_GIMME, 0);
+ CLASS_MAINSIGNALIN(sigbiquad_class, t_sigbiquad, x_f);
+ class_addmethod(sigbiquad_class, (t_method)sigbiquad_dsp, gensym("dsp"), 0);
+ class_addlist(sigbiquad_class, sigbiquad_list);
+ class_addmethod(sigbiquad_class, (t_method)sigbiquad_set, gensym("set"),
+ A_GIMME, 0);
+ class_addmethod(sigbiquad_class, (t_method)sigbiquad_set, gensym("clear"),
+ A_GIMME, 0);
+}
+
+/* ---------------- samphold~ - sample and hold ----------------- */
+
+typedef struct sigsamphold
+{
+ t_object x_obj;
+ float x_f;
+ float x_lastin;
+ float x_lastout;
+} t_sigsamphold;
+
+t_class *sigsamphold_class;
+
+static void *sigsamphold_new(void)
+{
+ t_sigsamphold *x = (t_sigsamphold *)pd_new(sigsamphold_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_lastin = 0;
+ x->x_lastout = 0;
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigsamphold_perform(t_int *w)
+{
+ float *in1 = (float *)(w[1]);
+ float *in2 = (float *)(w[2]);
+ float *out = (float *)(w[3]);
+ t_sigsamphold *x = (t_sigsamphold *)(w[4]);
+ int n = (t_int)(w[5]);
+ int i;
+ float lastin = x->x_lastin;
+ float lastout = x->x_lastout;
+ for (i = 0; i < n; i++, *in1++)
+ {
+ float next = *in2++;
+ if (next < lastin) lastout = *in1;
+ *out++ = lastout;
+ lastin = next;
+ }
+ x->x_lastin = lastin;
+ x->x_lastout = lastout;
+ return (w+6);
+}
+
+static void sigsamphold_dsp(t_sigsamphold *x, t_signal **sp)
+{
+ dsp_add(sigsamphold_perform, 5,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec,
+ x, sp[0]->s_n);
+}
+
+static void sigsamphold_reset(t_sigsamphold *x)
+{
+ x->x_lastin = 1e20;
+}
+
+static void sigsamphold_set(t_sigsamphold *x, t_float f)
+{
+ x->x_lastout = f;
+}
+
+void sigsamphold_setup(void)
+{
+ sigsamphold_class = class_new(gensym("samphold~"),
+ (t_newmethod)sigsamphold_new, 0, sizeof(t_sigsamphold), 0, 0);
+ CLASS_MAINSIGNALIN(sigsamphold_class, t_sigsamphold, x_f);
+ class_addmethod(sigsamphold_class, (t_method)sigsamphold_set,
+ gensym("set"), A_FLOAT, 0);
+ class_addmethod(sigsamphold_class, (t_method)sigsamphold_reset,
+ gensym("reset"), 0);
+ class_addmethod(sigsamphold_class, (t_method)sigsamphold_dsp,
+ gensym("dsp"), 0);
+}
+
+/* ------------------------ setup routine ------------------------- */
+
+void d_filter_setup(void)
+{
+ sighip_setup();
+ siglop_setup();
+ sigbp_setup();
+ sigbiquad_setup();
+ sigsamphold_setup();
+}
diff --git a/pd/src/d_global.c b/pd/src/d_global.c
new file mode 100644
index 00000000..633eba1c
--- /dev/null
+++ b/pd/src/d_global.c
@@ -0,0 +1,312 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* send~, receive~, throw~, catch~ */
+
+#include "m_pd.h"
+#include <string.h>
+
+#define DEFSENDVS 64 /* LATER get send to get this from canvas */
+
+/* ----------------------------- send~ ----------------------------- */
+static t_class *sigsend_class;
+
+typedef struct _sigsend
+{
+ t_object x_obj;
+ t_symbol *x_sym;
+ int x_n;
+ float *x_vec;
+ float x_f;
+} t_sigsend;
+
+static void *sigsend_new(t_symbol *s)
+{
+ t_sigsend *x = (t_sigsend *)pd_new(sigsend_class);
+ pd_bind(&x->x_obj.ob_pd, s);
+ x->x_sym = s;
+ x->x_n = DEFSENDVS;
+ x->x_vec = (float *)getbytes(DEFSENDVS * sizeof(float));
+ memset((char *)(x->x_vec), 0, DEFSENDVS * sizeof(float));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigsend_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ while (n--)
+ {
+ float f = *in++;
+ /* bash NANs and underflow/overflow hazards to zero */
+ if (!((f > 1.0e-20f && f < 1.0e20f) || (f < -1e-20f && f > -1e20)))
+ f = 0;
+ *out++ = f;
+ }
+ return (w+4);
+}
+
+static void sigsend_dsp(t_sigsend *x, t_signal **sp)
+{
+ if (x->x_n == sp[0]->s_n)
+ dsp_add(sigsend_perform, 3, sp[0]->s_vec, x->x_vec, sp[0]->s_n);
+ else error("sigsend %s: unexpected vector size", x->x_sym->s_name);
+}
+
+static void sigsend_free(t_sigsend *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, x->x_sym);
+ freebytes(x->x_vec, x->x_n * sizeof(float));
+}
+
+static void sigsend_setup(void)
+{
+ sigsend_class = class_new(gensym("send~"), (t_newmethod)sigsend_new,
+ (t_method)sigsend_free, sizeof(t_sigsend), 0, A_DEFSYM, 0);
+ class_addcreator((t_newmethod)sigsend_new, gensym("s~"), A_DEFSYM, 0);
+ CLASS_MAINSIGNALIN(sigsend_class, t_sigsend, x_f);
+ class_addmethod(sigsend_class, (t_method)sigsend_dsp, gensym("dsp"), 0);
+}
+
+/* ----------------------------- receive~ ----------------------------- */
+static t_class *sigreceive_class;
+
+typedef struct _sigreceive
+{
+ t_object x_obj;
+ t_symbol *x_sym;
+ t_float *x_wherefrom;
+ int x_n;
+} t_sigreceive;
+
+static void *sigreceive_new(t_symbol *s)
+{
+ t_sigreceive *x = (t_sigreceive *)pd_new(sigreceive_class);
+ x->x_n = DEFSENDVS; /* LATER find our vector size correctly */
+ x->x_sym = s;
+ x->x_wherefrom = 0;
+ outlet_new(&x->x_obj, &s_signal);
+ return (x);
+}
+
+static t_int *sigreceive_perform(t_int *w)
+{
+ t_sigreceive *x = (t_sigreceive *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ t_float *in = x->x_wherefrom;
+ if (in)
+ {
+ while (n--)
+ *out++ = *in++;
+ }
+ else
+ {
+ while (n--)
+ *out++ = 0;
+ }
+ return (w+4);
+}
+
+static void sigreceive_set(t_sigreceive *x, t_symbol *s)
+{
+ t_sigsend *sender = (t_sigsend *)pd_findbyclass((x->x_sym = s),
+ sigsend_class);
+ if (sender)
+ {
+ if (sender->x_n == x->x_n)
+ x->x_wherefrom = sender->x_vec;
+ else
+ {
+ pd_error(x, "receive~ %s: vector size mismatch", x->x_sym->s_name);
+ x->x_wherefrom = 0;
+ }
+ }
+ else
+ {
+ pd_error(x, "receive~ %s: no matching send", x->x_sym->s_name);
+ x->x_wherefrom = 0;
+ }
+}
+
+static void sigreceive_dsp(t_sigreceive *x, t_signal **sp)
+{
+ if (sp[0]->s_n != x->x_n)
+ {
+ pd_error(x, "receive~ %s: vector size mismatch", x->x_sym->s_name);
+ }
+ else
+ {
+ sigreceive_set(x, x->x_sym);
+ dsp_add(sigreceive_perform, 3,
+ x, sp[0]->s_vec, sp[0]->s_n);
+ }
+}
+
+static void sigreceive_setup(void)
+{
+ sigreceive_class = class_new(gensym("receive~"),
+ (t_newmethod)sigreceive_new, 0,
+ sizeof(t_sigreceive), 0, A_DEFSYM, 0);
+ class_addcreator((t_newmethod)sigreceive_new, gensym("r~"), A_DEFSYM, 0);
+ class_addmethod(sigreceive_class, (t_method)sigreceive_set, gensym("set"),
+ A_SYMBOL, 0);
+ class_addmethod(sigreceive_class, (t_method)sigreceive_dsp, gensym("dsp"),
+ 0);
+ class_sethelpsymbol(sigreceive_class, gensym("send~"));
+}
+
+/* ----------------------------- catch~ ----------------------------- */
+static t_class *sigcatch_class;
+
+typedef struct _sigcatch
+{
+ t_object x_obj;
+ t_symbol *x_sym;
+ int x_n;
+ float *x_vec;
+} t_sigcatch;
+
+static void *sigcatch_new(t_symbol *s)
+{
+ t_sigcatch *x = (t_sigcatch *)pd_new(sigcatch_class);
+ pd_bind(&x->x_obj.ob_pd, s);
+ x->x_sym = s;
+ x->x_n = DEFSENDVS;
+ x->x_vec = (float *)getbytes(DEFSENDVS * sizeof(float));
+ memset((char *)(x->x_vec), 0, DEFSENDVS * sizeof(float));
+ outlet_new(&x->x_obj, &s_signal);
+ return (x);
+}
+
+static t_int *sigcatch_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ while (n--) *out++ = *in, *in++ = 0;
+ return (w+4);
+}
+
+static void sigcatch_dsp(t_sigcatch *x, t_signal **sp)
+{
+ if (x->x_n == sp[0]->s_n)
+ dsp_add(sigcatch_perform, 3, x->x_vec, sp[0]->s_vec, sp[0]->s_n);
+ else error("sigcatch %s: unexpected vector size", x->x_sym->s_name);
+}
+
+static void sigcatch_free(t_sigcatch *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, x->x_sym);
+ freebytes(x->x_vec, x->x_n * sizeof(float));
+}
+
+static void sigcatch_setup(void)
+{
+ sigcatch_class = class_new(gensym("catch~"), (t_newmethod)sigcatch_new,
+ (t_method)sigcatch_free, sizeof(t_sigcatch), CLASS_NOINLET, A_DEFSYM, 0);
+ class_addmethod(sigcatch_class, (t_method)sigcatch_dsp, gensym("dsp"), 0);
+ class_sethelpsymbol(sigcatch_class, gensym("throw~"));
+}
+
+/* ----------------------------- throw~ ----------------------------- */
+static t_class *sigthrow_class;
+
+typedef struct _sigthrow
+{
+ t_object x_obj;
+ t_symbol *x_sym;
+ t_float *x_whereto;
+ int x_n;
+ t_float x_f;
+} t_sigthrow;
+
+static void *sigthrow_new(t_symbol *s)
+{
+ t_sigthrow *x = (t_sigthrow *)pd_new(sigthrow_class);
+ x->x_sym = s;
+ x->x_whereto = 0;
+ x->x_n = DEFSENDVS;
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigthrow_perform(t_int *w)
+{
+ t_sigthrow *x = (t_sigthrow *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ t_float *out = x->x_whereto;
+ if (out)
+ {
+ while (n--)
+ {
+ float f = *in++;
+ /* bash NANs and underflow/overflow hazards to zero */
+ if (!((f > 1.0e-20f && f < 1.0e20f) || (f < -1e-20f && f > -1e20)))
+ f = 0;
+ *out++ += f;
+ }
+ }
+ return (w+4);
+}
+
+static void sigthrow_set(t_sigthrow *x, t_symbol *s)
+{
+ t_sigcatch *catcher = (t_sigcatch *)pd_findbyclass((x->x_sym = s),
+ sigcatch_class);
+ if (catcher)
+ {
+ if (catcher->x_n == x->x_n)
+ x->x_whereto = catcher->x_vec;
+ else
+ {
+ pd_error(x, "throw~ %s: vector size mismatch", x->x_sym->s_name);
+ x->x_whereto = 0;
+ }
+ }
+ else
+ {
+ pd_error(x, "throw~ %s: no matching catch", x->x_sym->s_name);
+ x->x_whereto = 0;
+ }
+}
+
+static void sigthrow_dsp(t_sigthrow *x, t_signal **sp)
+{
+ if (sp[0]->s_n != x->x_n)
+ {
+ pd_error(x, "throw~ %s: vector size mismatch", x->x_sym->s_name);
+ }
+ else
+ {
+ sigthrow_set(x, x->x_sym);
+ dsp_add(sigthrow_perform, 3,
+ x, sp[0]->s_vec, sp[0]->s_n);
+ }
+}
+
+static void sigthrow_setup(void)
+{
+ sigthrow_class = class_new(gensym("throw~"), (t_newmethod)sigthrow_new, 0,
+ sizeof(t_sigthrow), 0, A_DEFSYM, 0);
+ class_addcreator((t_newmethod)sigthrow_new, gensym("r~"), A_DEFSYM, 0);
+ class_addmethod(sigthrow_class, (t_method)sigthrow_set, gensym("set"),
+ A_SYMBOL, 0);
+ CLASS_MAINSIGNALIN(sigthrow_class, t_sigthrow, x_f);
+ class_addmethod(sigthrow_class, (t_method)sigthrow_dsp, gensym("dsp"), 0);
+}
+
+/* ----------------------- global setup routine ---------------- */
+
+void d_global_setup(void)
+{
+ sigsend_setup();
+ sigreceive_setup();
+ sigcatch_setup();
+ sigthrow_setup();
+}
+
diff --git a/pd/src/d_math.c b/pd/src/d_math.c
new file mode 100644
index 00000000..d64e2e34
--- /dev/null
+++ b/pd/src/d_math.c
@@ -0,0 +1,573 @@
+/* Copyright (c) 1997-2001 Miller Puckette and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* mathematical functions and other transfer functions, including tilde
+ versions of stuff from x_acoustics.c.
+*/
+
+#include "m_pd.h"
+#include <math.h>
+#define LOGTEN 2.302585092994
+
+/* ------------------------- clip~ -------------------------- */
+static t_class *clip_class;
+
+typedef struct _clip
+{
+ t_object x_obj;
+ float x_f;
+ t_sample x_lo;
+ t_sample x_hi;
+} t_clip;
+
+static void *clip_new(t_floatarg lo, t_floatarg hi)
+{
+ t_clip *x = (t_clip *)pd_new(clip_class);
+ x->x_lo = lo;
+ x->x_hi = hi;
+ outlet_new(&x->x_obj, gensym("signal"));
+ floatinlet_new(&x->x_obj, &x->x_lo);
+ floatinlet_new(&x->x_obj, &x->x_hi);
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *clip_perform(t_int *w)
+{
+ t_clip *x = (t_clip *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ while (n--)
+ {
+ float f = *in++;
+ if (f < x->x_lo) f = x->x_lo;
+ if (f > x->x_hi) f = x->x_hi;
+ *out++ = f;
+ }
+ return (w+5);
+}
+
+static void clip_dsp(t_clip *x, t_signal **sp)
+{
+ dsp_add(clip_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+static void clip_setup(void)
+{
+ clip_class = class_new(gensym("clip~"), (t_newmethod)clip_new, 0,
+ sizeof(t_clip), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(clip_class, t_clip, x_f);
+ class_addmethod(clip_class, (t_method)clip_dsp, gensym("dsp"), 0);
+}
+
+/* sigrsqrt - reciprocal square root good to 8 mantissa bits */
+
+#define DUMTAB1SIZE 256
+#define DUMTAB2SIZE 1024
+
+static float rsqrt_exptab[DUMTAB1SIZE], rsqrt_mantissatab[DUMTAB2SIZE];
+
+static void init_rsqrt(void)
+{
+ int i;
+ for (i = 0; i < DUMTAB1SIZE; i++)
+ {
+ float f;
+ long l = (i ? (i == DUMTAB1SIZE-1 ? DUMTAB1SIZE-2 : i) : 1)<< 23;
+ *(long *)(&f) = l;
+ rsqrt_exptab[i] = 1./sqrt(f);
+ }
+ for (i = 0; i < DUMTAB2SIZE; i++)
+ {
+ float f = 1 + (1./DUMTAB2SIZE) * i;
+ rsqrt_mantissatab[i] = 1./sqrt(f);
+ }
+}
+
+ /* these are used in externs like "bonk" */
+
+float q8_rsqrt(float f)
+{
+ long l = *(long *)(&f);
+ if (f < 0) return (0);
+ else return (rsqrt_exptab[(l >> 23) & 0xff] *
+ rsqrt_mantissatab[(l >> 13) & 0x3ff]);
+}
+
+float q8_sqrt(float f)
+{
+ long l = *(long *)(&f);
+ if (f < 0) return (0);
+ else return (f * rsqrt_exptab[(l >> 23) & 0xff] *
+ rsqrt_mantissatab[(l >> 13) & 0x3ff]);
+}
+
+ /* the old names are OK unless we're in IRIX N32 */
+
+#ifndef N32
+float qsqrt(float f) {return (q8_sqrt(f)); }
+float qrsqrt(float f) {return (q8_rsqrt(f)); }
+#endif
+
+
+
+typedef struct sigrsqrt
+{
+ t_object x_obj;
+ float x_f;
+} t_sigrsqrt;
+
+static t_class *sigrsqrt_class;
+
+static void *sigrsqrt_new(void)
+{
+ t_sigrsqrt *x = (t_sigrsqrt *)pd_new(sigrsqrt_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigrsqrt_perform(t_int *w)
+{
+ float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2);
+ t_int n = *(t_int *)(w+3);
+ while (n--)
+ {
+ float f = *in;
+ long l = *(long *)(in++);
+ if (f < 0) *out++ = 0;
+ else
+ {
+ float g = rsqrt_exptab[(l >> 23) & 0xff] *
+ rsqrt_mantissatab[(l >> 13) & 0x3ff];
+ *out++ = 1.5 * g - 0.5 * g * g * g * f;
+ }
+ }
+ return (w + 4);
+}
+
+static void sigrsqrt_dsp(t_sigrsqrt *x, t_signal **sp)
+{
+ dsp_add(sigrsqrt_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+void sigrsqrt_setup(void)
+{
+ init_rsqrt();
+ sigrsqrt_class = class_new(gensym("rsqrt~"), (t_newmethod)sigrsqrt_new, 0,
+ sizeof(t_sigrsqrt), 0, 0);
+ /* an old name for it: */
+ class_addcreator(sigrsqrt_new, gensym("q8_rsqrt~"), 0);
+ CLASS_MAINSIGNALIN(sigrsqrt_class, t_sigrsqrt, x_f);
+ class_addmethod(sigrsqrt_class, (t_method)sigrsqrt_dsp, gensym("dsp"), 0);
+}
+
+
+/* sigsqrt - square root good to 8 mantissa bits */
+
+typedef struct sigsqrt
+{
+ t_object x_obj;
+ float x_f;
+} t_sigsqrt;
+
+static t_class *sigsqrt_class;
+
+static void *sigsqrt_new(void)
+{
+ t_sigsqrt *x = (t_sigsqrt *)pd_new(sigsqrt_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+t_int *sigsqrt_perform(t_int *w) /* not static; also used in d_fft.c */
+{
+ float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2);
+ t_int n = *(t_int *)(w+3);
+ while (n--)
+ {
+ float f = *in;
+ long l = *(long *)(in++);
+ if (f < 0) *out++ = 0;
+ else
+ {
+ float g = rsqrt_exptab[(l >> 23) & 0xff] *
+ rsqrt_mantissatab[(l >> 13) & 0x3ff];
+ *out++ = f * (1.5 * g - 0.5 * g * g * g * f);
+ }
+ }
+ return (w + 4);
+}
+
+static void sigsqrt_dsp(t_sigsqrt *x, t_signal **sp)
+{
+ dsp_add(sigsqrt_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+void sigsqrt_setup(void)
+{
+ sigsqrt_class = class_new(gensym("sqrt~"), (t_newmethod)sigsqrt_new, 0,
+ sizeof(t_sigsqrt), 0, 0);
+ class_addcreator(sigsqrt_new, gensym("q8_sqrt~"), 0); /* old name */
+ CLASS_MAINSIGNALIN(sigsqrt_class, t_sigsqrt, x_f);
+ class_addmethod(sigsqrt_class, (t_method)sigsqrt_dsp, gensym("dsp"), 0);
+}
+
+/* ------------------------------ wrap~ -------------------------- */
+
+typedef struct wrap
+{
+ t_object x_obj;
+ float x_f;
+} t_sigwrap;
+
+t_class *sigwrap_class;
+
+static void *sigwrap_new(void)
+{
+ t_sigwrap *x = (t_sigwrap *)pd_new(sigwrap_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *sigwrap_perform(t_int *w)
+{
+ float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2);
+ t_int n = *(t_int *)(w+3);
+ while (n--)
+ {
+ float f = *in++;
+ int k = f;
+ if (f > 0) *out++ = f-k;
+ else *out++ = f - (k-1);
+ }
+ return (w + 4);
+}
+
+static void sigwrap_dsp(t_sigwrap *x, t_signal **sp)
+{
+ dsp_add(sigwrap_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+void sigwrap_setup(void)
+{
+ sigwrap_class = class_new(gensym("wrap~"), (t_newmethod)sigwrap_new, 0,
+ sizeof(t_sigwrap), 0, 0);
+ CLASS_MAINSIGNALIN(sigwrap_class, t_sigwrap, x_f);
+ class_addmethod(sigwrap_class, (t_method)sigwrap_dsp, gensym("dsp"), 0);
+}
+
+/* ------------------------------ mtof_tilde~ -------------------------- */
+
+typedef struct mtof_tilde
+{
+ t_object x_obj;
+ float x_f;
+} t_mtof_tilde;
+
+t_class *mtof_tilde_class;
+
+static void *mtof_tilde_new(void)
+{
+ t_mtof_tilde *x = (t_mtof_tilde *)pd_new(mtof_tilde_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *mtof_tilde_perform(t_int *w)
+{
+ float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2);
+ t_int n = *(t_int *)(w+3);
+ for (; n--; in++, out++)
+ {
+ float f = *in;
+ if (f <= -1500) *out = 0;
+ else
+ {
+ if (f > 1499) f = 1499;
+ *out = 8.17579891564 * exp(.0577622650 * f);
+ }
+ }
+ return (w + 4);
+}
+
+static void mtof_tilde_dsp(t_mtof_tilde *x, t_signal **sp)
+{
+ dsp_add(mtof_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+void mtof_tilde_setup(void)
+{
+ mtof_tilde_class = class_new(gensym("mtof~"), (t_newmethod)mtof_tilde_new, 0,
+ sizeof(t_mtof_tilde), 0, 0);
+ CLASS_MAINSIGNALIN(mtof_tilde_class, t_mtof_tilde, x_f);
+ class_addmethod(mtof_tilde_class, (t_method)mtof_tilde_dsp, gensym("dsp"), 0);
+}
+
+/* ------------------------------ ftom_tilde~ -------------------------- */
+
+typedef struct ftom_tilde
+{
+ t_object x_obj;
+ float x_f;
+} t_ftom_tilde;
+
+t_class *ftom_tilde_class;
+
+static void *ftom_tilde_new(void)
+{
+ t_ftom_tilde *x = (t_ftom_tilde *)pd_new(ftom_tilde_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *ftom_tilde_perform(t_int *w)
+{
+ float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2);
+ t_int n = *(t_int *)(w+3);
+ for (; n--; *in++, out++)
+ {
+ float f = *in;
+ *out = (f > 0 ? 17.3123405046 * log(.12231220585 * f) : -1500);
+ }
+ return (w + 4);
+}
+
+static void ftom_tilde_dsp(t_ftom_tilde *x, t_signal **sp)
+{
+ dsp_add(ftom_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+void ftom_tilde_setup(void)
+{
+ ftom_tilde_class = class_new(gensym("ftom~"), (t_newmethod)ftom_tilde_new, 0,
+ sizeof(t_ftom_tilde), 0, 0);
+ CLASS_MAINSIGNALIN(ftom_tilde_class, t_ftom_tilde, x_f);
+ class_addmethod(ftom_tilde_class, (t_method)ftom_tilde_dsp, gensym("dsp"), 0);
+}
+
+/* ------------------------------ dbtorms~ -------------------------- */
+
+typedef struct dbtorms_tilde
+{
+ t_object x_obj;
+ float x_f;
+} t_dbtorms_tilde;
+
+t_class *dbtorms_tilde_class;
+
+static void *dbtorms_tilde_new(void)
+{
+ t_dbtorms_tilde *x = (t_dbtorms_tilde *)pd_new(dbtorms_tilde_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *dbtorms_tilde_perform(t_int *w)
+{
+ float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2);
+ t_int n = *(t_int *)(w+3);
+ for (; n--; in++, out++)
+ {
+ float f = *in;
+ if (f <= 0) *out = 0;
+ else
+ {
+ if (f > 485)
+ f = 485;
+ *out = exp((LOGTEN * 0.05) * (f-100.));
+ }
+ }
+ return (w + 4);
+}
+
+static void dbtorms_tilde_dsp(t_dbtorms_tilde *x, t_signal **sp)
+{
+ dsp_add(dbtorms_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+void dbtorms_tilde_setup(void)
+{
+ dbtorms_tilde_class = class_new(gensym("dbtorms~"), (t_newmethod)dbtorms_tilde_new, 0,
+ sizeof(t_dbtorms_tilde), 0, 0);
+ CLASS_MAINSIGNALIN(dbtorms_tilde_class, t_dbtorms_tilde, x_f);
+ class_addmethod(dbtorms_tilde_class, (t_method)dbtorms_tilde_dsp, gensym("dsp"), 0);
+}
+
+/* ------------------------------ rmstodb~ -------------------------- */
+
+typedef struct rmstodb_tilde
+{
+ t_object x_obj;
+ float x_f;
+} t_rmstodb_tilde;
+
+t_class *rmstodb_tilde_class;
+
+static void *rmstodb_tilde_new(void)
+{
+ t_rmstodb_tilde *x = (t_rmstodb_tilde *)pd_new(rmstodb_tilde_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *rmstodb_tilde_perform(t_int *w)
+{
+ float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2);
+ t_int n = *(t_int *)(w+3);
+ for (; n--; in++, out++)
+ {
+ float f = *in;
+ if (f <= 0) *out = 0;
+ else
+ {
+ float g = 100 + 20./LOGTEN * log(f);
+ *out = (g < 0 ? 0 : g);
+ }
+ }
+ return (w + 4);
+}
+
+static void rmstodb_tilde_dsp(t_rmstodb_tilde *x, t_signal **sp)
+{
+ dsp_add(rmstodb_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+void rmstodb_tilde_setup(void)
+{
+ rmstodb_tilde_class = class_new(gensym("rmstodb~"), (t_newmethod)rmstodb_tilde_new, 0,
+ sizeof(t_rmstodb_tilde), 0, 0);
+ CLASS_MAINSIGNALIN(rmstodb_tilde_class, t_rmstodb_tilde, x_f);
+ class_addmethod(rmstodb_tilde_class, (t_method)rmstodb_tilde_dsp, gensym("dsp"), 0);
+}
+
+/* ------------------------------ dbtopow~ -------------------------- */
+
+typedef struct dbtopow_tilde
+{
+ t_object x_obj;
+ float x_f;
+} t_dbtopow_tilde;
+
+t_class *dbtopow_tilde_class;
+
+static void *dbtopow_tilde_new(void)
+{
+ t_dbtopow_tilde *x = (t_dbtopow_tilde *)pd_new(dbtopow_tilde_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *dbtopow_tilde_perform(t_int *w)
+{
+ float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2);
+ t_int n = *(t_int *)(w+3);
+ for (; n--; in++, out++)
+ {
+ float f = *in;
+ if (f <= 0) *out = 0;
+ else
+ {
+ if (f > 870)
+ f = 870;
+ *out = exp((LOGTEN * 0.1) * (f-100.));
+ }
+ }
+ return (w + 4);
+}
+
+static void dbtopow_tilde_dsp(t_dbtopow_tilde *x, t_signal **sp)
+{
+ dsp_add(dbtopow_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+void dbtopow_tilde_setup(void)
+{
+ dbtopow_tilde_class = class_new(gensym("dbtopow~"), (t_newmethod)dbtopow_tilde_new, 0,
+ sizeof(t_dbtopow_tilde), 0, 0);
+ CLASS_MAINSIGNALIN(dbtopow_tilde_class, t_dbtopow_tilde, x_f);
+ class_addmethod(dbtopow_tilde_class, (t_method)dbtopow_tilde_dsp, gensym("dsp"), 0);
+}
+
+/* ------------------------------ powtodb~ -------------------------- */
+
+typedef struct powtodb_tilde
+{
+ t_object x_obj;
+ float x_f;
+} t_powtodb_tilde;
+
+t_class *powtodb_tilde_class;
+
+static void *powtodb_tilde_new(void)
+{
+ t_powtodb_tilde *x = (t_powtodb_tilde *)pd_new(powtodb_tilde_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *powtodb_tilde_perform(t_int *w)
+{
+ float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2);
+ t_int n = *(t_int *)(w+3);
+ for (; n--; in++, out++)
+ {
+ float f = *in;
+ if (f <= 0) *out = 0;
+ else
+ {
+ float g = 100 + 10./LOGTEN * log(f);
+ *out = (g < 0 ? 0 : g);
+ }
+ }
+ return (w + 4);
+}
+
+static void powtodb_tilde_dsp(t_powtodb_tilde *x, t_signal **sp)
+{
+ dsp_add(powtodb_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+void powtodb_tilde_setup(void)
+{
+ powtodb_tilde_class = class_new(gensym("powtodb~"), (t_newmethod)powtodb_tilde_new, 0,
+ sizeof(t_powtodb_tilde), 0, 0);
+ CLASS_MAINSIGNALIN(powtodb_tilde_class, t_powtodb_tilde, x_f);
+ class_addmethod(powtodb_tilde_class, (t_method)powtodb_tilde_dsp, gensym("dsp"), 0);
+}
+
+
+/* ------------------------ global setup routine ------------------------- */
+
+void d_math_setup(void)
+{
+ t_symbol *s = gensym("acoustics~.pd");
+ clip_setup();
+ sigrsqrt_setup();
+ sigsqrt_setup();
+ sigwrap_setup();
+ mtof_tilde_setup();
+ ftom_tilde_setup();
+ dbtorms_tilde_setup();
+ rmstodb_tilde_setup();
+ dbtopow_tilde_setup();
+ powtodb_tilde_setup();
+
+ class_sethelpsymbol(mtof_tilde_class, s);
+ class_sethelpsymbol(ftom_tilde_class, s);
+ class_sethelpsymbol(dbtorms_tilde_class, s);
+ class_sethelpsymbol(rmstodb_tilde_class, s);
+ class_sethelpsymbol(dbtopow_tilde_class, s);
+ class_sethelpsymbol(powtodb_tilde_class, s);
+}
+
diff --git a/pd/src/d_mayer_fft.c b/pd/src/d_mayer_fft.c
new file mode 100644
index 00000000..c221e33e
--- /dev/null
+++ b/pd/src/d_mayer_fft.c
@@ -0,0 +1,419 @@
+/*
+** FFT and FHT routines
+** Copyright 1988, 1993; Ron Mayer
+**
+** mayer_fht(fz,n);
+** Does a hartley transform of "n" points in the array "fz".
+** mayer_fft(n,real,imag)
+** Does a fourier transform of "n" points of the "real" and
+** "imag" arrays.
+** mayer_ifft(n,real,imag)
+** Does an inverse fourier transform of "n" points of the "real"
+** and "imag" arrays.
+** mayer_realfft(n,real)
+** Does a real-valued fourier transform of "n" points of the
+** "real" array. The real part of the transform ends
+** up in the first half of the array and the imaginary part of the
+** transform ends up in the second half of the array.
+** mayer_realifft(n,real)
+** The inverse of the realfft() routine above.
+**
+**
+** NOTE: This routine uses at least 2 patented algorithms, and may be
+** under the restrictions of a bunch of different organizations.
+** Although I wrote it completely myself, it is kind of a derivative
+** of a routine I once authored and released under the GPL, so it
+** may fall under the free software foundation's restrictions;
+** it was worked on as a Stanford Univ project, so they claim
+** some rights to it; it was further optimized at work here, so
+** I think this company claims parts of it. The patents are
+** held by R. Bracewell (the FHT algorithm) and O. Buneman (the
+** trig generator), both at Stanford Univ.
+** If it were up to me, I'd say go do whatever you want with it;
+** but it would be polite to give credit to the following people
+** if you use this anywhere:
+** Euler - probable inventor of the fourier transform.
+** Gauss - probable inventor of the FFT.
+** Hartley - probable inventor of the hartley transform.
+** Buneman - for a really cool trig generator
+** Mayer(me) - for authoring this particular version and
+** including all the optimizations in one package.
+** Thanks,
+** Ron Mayer; mayer@acuson.com
+**
+*/
+
+/* This is a slightly modified version of Mayer's contribution; write
+* msp@ucsd.edu for the original code. Kudos to Mayer for a fine piece
+* of work. -msp
+*/
+
+#ifdef NT
+#pragma warning( disable : 4305 ) /* uncast const double to float */
+#pragma warning( disable : 4244 ) /* uncast double to float */
+#pragma warning( disable : 4101 ) /* unused local variables */
+#endif
+
+#define REAL float
+#define GOOD_TRIG
+
+#ifdef GOOD_TRIG
+#else
+#define FAST_TRIG
+#endif
+
+#if defined(GOOD_TRIG)
+#define FHT_SWAP(a,b,t) {(t)=(a);(a)=(b);(b)=(t);}
+#define TRIG_VARS \
+ int t_lam=0;
+#define TRIG_INIT(k,c,s) \
+ { \
+ int i; \
+ for (i=2 ; i<=k ; i++) \
+ {coswrk[i]=costab[i];sinwrk[i]=sintab[i];} \
+ t_lam = 0; \
+ c = 1; \
+ s = 0; \
+ }
+#define TRIG_NEXT(k,c,s) \
+ { \
+ int i,j; \
+ (t_lam)++; \
+ for (i=0 ; !((1<<i)&t_lam) ; i++); \
+ i = k-i; \
+ s = sinwrk[i]; \
+ c = coswrk[i]; \
+ if (i>1) \
+ { \
+ for (j=k-i+2 ; (1<<j)&t_lam ; j++); \
+ j = k - j; \
+ sinwrk[i] = halsec[i] * (sinwrk[i-1] + sinwrk[j]); \
+ coswrk[i] = halsec[i] * (coswrk[i-1] + coswrk[j]); \
+ } \
+ }
+#define TRIG_RESET(k,c,s)
+#endif
+
+#if defined(FAST_TRIG)
+#define TRIG_VARS \
+ REAL t_c,t_s;
+#define TRIG_INIT(k,c,s) \
+ { \
+ t_c = costab[k]; \
+ t_s = sintab[k]; \
+ c = 1; \
+ s = 0; \
+ }
+#define TRIG_NEXT(k,c,s) \
+ { \
+ REAL t = c; \
+ c = t*t_c - s*t_s; \
+ s = t*t_s + s*t_c; \
+ }
+#define TRIG_RESET(k,c,s)
+#endif
+
+static REAL halsec[20]=
+ {
+ 0,
+ 0,
+ .54119610014619698439972320536638942006107206337801,
+ .50979557910415916894193980398784391368261849190893,
+ .50241928618815570551167011928012092247859337193963,
+ .50060299823519630134550410676638239611758632599591,
+ .50015063602065098821477101271097658495974913010340,
+ .50003765191554772296778139077905492847503165398345,
+ .50000941253588775676512870469186533538523133757983,
+ .50000235310628608051401267171204408939326297376426,
+ .50000058827484117879868526730916804925780637276181,
+ .50000014706860214875463798283871198206179118093251,
+ .50000003676714377807315864400643020315103490883972,
+ .50000000919178552207366560348853455333939112569380,
+ .50000000229794635411562887767906868558991922348920,
+ .50000000057448658687873302235147272458812263401372
+ };
+static REAL costab[20]=
+ {
+ .00000000000000000000000000000000000000000000000000,
+ .70710678118654752440084436210484903928483593768847,
+ .92387953251128675612818318939678828682241662586364,
+ .98078528040323044912618223613423903697393373089333,
+ .99518472667219688624483695310947992157547486872985,
+ .99879545620517239271477160475910069444320361470461,
+ .99969881869620422011576564966617219685006108125772,
+ .99992470183914454092164649119638322435060646880221,
+ .99998117528260114265699043772856771617391725094433,
+ .99999529380957617151158012570011989955298763362218,
+ .99999882345170190992902571017152601904826792288976,
+ .99999970586288221916022821773876567711626389934930,
+ .99999992646571785114473148070738785694820115568892,
+ .99999998161642929380834691540290971450507605124278,
+ .99999999540410731289097193313960614895889430318945,
+ .99999999885102682756267330779455410840053741619428
+ };
+static REAL sintab[20]=
+ {
+ 1.0000000000000000000000000000000000000000000000000,
+ .70710678118654752440084436210484903928483593768846,
+ .38268343236508977172845998403039886676134456248561,
+ .19509032201612826784828486847702224092769161775195,
+ .09801714032956060199419556388864184586113667316749,
+ .04906767432741801425495497694268265831474536302574,
+ .02454122852291228803173452945928292506546611923944,
+ .01227153828571992607940826195100321214037231959176,
+ .00613588464915447535964023459037258091705788631738,
+ .00306795676296597627014536549091984251894461021344,
+ .00153398018628476561230369715026407907995486457522,
+ .00076699031874270452693856835794857664314091945205,
+ .00038349518757139558907246168118138126339502603495,
+ .00019174759731070330743990956198900093346887403385,
+ .00009587379909597734587051721097647635118706561284,
+ .00004793689960306688454900399049465887274686668768
+ };
+static REAL coswrk[20]=
+ {
+ .00000000000000000000000000000000000000000000000000,
+ .70710678118654752440084436210484903928483593768847,
+ .92387953251128675612818318939678828682241662586364,
+ .98078528040323044912618223613423903697393373089333,
+ .99518472667219688624483695310947992157547486872985,
+ .99879545620517239271477160475910069444320361470461,
+ .99969881869620422011576564966617219685006108125772,
+ .99992470183914454092164649119638322435060646880221,
+ .99998117528260114265699043772856771617391725094433,
+ .99999529380957617151158012570011989955298763362218,
+ .99999882345170190992902571017152601904826792288976,
+ .99999970586288221916022821773876567711626389934930,
+ .99999992646571785114473148070738785694820115568892,
+ .99999998161642929380834691540290971450507605124278,
+ .99999999540410731289097193313960614895889430318945,
+ .99999999885102682756267330779455410840053741619428
+ };
+static REAL sinwrk[20]=
+ {
+ 1.0000000000000000000000000000000000000000000000000,
+ .70710678118654752440084436210484903928483593768846,
+ .38268343236508977172845998403039886676134456248561,
+ .19509032201612826784828486847702224092769161775195,
+ .09801714032956060199419556388864184586113667316749,
+ .04906767432741801425495497694268265831474536302574,
+ .02454122852291228803173452945928292506546611923944,
+ .01227153828571992607940826195100321214037231959176,
+ .00613588464915447535964023459037258091705788631738,
+ .00306795676296597627014536549091984251894461021344,
+ .00153398018628476561230369715026407907995486457522,
+ .00076699031874270452693856835794857664314091945205,
+ .00038349518757139558907246168118138126339502603495,
+ .00019174759731070330743990956198900093346887403385,
+ .00009587379909597734587051721097647635118706561284,
+ .00004793689960306688454900399049465887274686668768
+ };
+
+
+#define SQRT2_2 0.70710678118654752440084436210484
+#define SQRT2 2*0.70710678118654752440084436210484
+
+void mayer_fht(REAL *fz, int n)
+{
+/* REAL a,b;
+REAL c1,s1,s2,c2,s3,c3,s4,c4;
+ REAL f0,g0,f1,g1,f2,g2,f3,g3; */
+ int k,k1,k2,k3,k4,kx;
+ REAL *fi,*fn,*gi;
+ TRIG_VARS;
+
+ for (k1=1,k2=0;k1<n;k1++)
+ {
+ REAL aa;
+ for (k=n>>1; (!((k2^=k)&k)); k>>=1);
+ if (k1>k2)
+ {
+ aa=fz[k1];fz[k1]=fz[k2];fz[k2]=aa;
+ }
+ }
+ for ( k=0 ; (1<<k)<n ; k++ );
+ k &= 1;
+ if (k==0)
+ {
+ for (fi=fz,fn=fz+n;fi<fn;fi+=4)
+ {
+ REAL f0,f1,f2,f3;
+ f1 = fi[0 ]-fi[1 ];
+ f0 = fi[0 ]+fi[1 ];
+ f3 = fi[2 ]-fi[3 ];
+ f2 = fi[2 ]+fi[3 ];
+ fi[2 ] = (f0-f2);
+ fi[0 ] = (f0+f2);
+ fi[3 ] = (f1-f3);
+ fi[1 ] = (f1+f3);
+ }
+ }
+ else
+ {
+ for (fi=fz,fn=fz+n,gi=fi+1;fi<fn;fi+=8,gi+=8)
+ {
+ REAL bs1,bc1,bs2,bc2,bs3,bc3,bs4,bc4,
+ bg0,bf0,bf1,bg1,bf2,bg2,bf3,bg3;
+ bc1 = fi[0 ] - gi[0 ];
+ bs1 = fi[0 ] + gi[0 ];
+ bc2 = fi[2 ] - gi[2 ];
+ bs2 = fi[2 ] + gi[2 ];
+ bc3 = fi[4 ] - gi[4 ];
+ bs3 = fi[4 ] + gi[4 ];
+ bc4 = fi[6 ] - gi[6 ];
+ bs4 = fi[6 ] + gi[6 ];
+ bf1 = (bs1 - bs2);
+ bf0 = (bs1 + bs2);
+ bg1 = (bc1 - bc2);
+ bg0 = (bc1 + bc2);
+ bf3 = (bs3 - bs4);
+ bf2 = (bs3 + bs4);
+ bg3 = SQRT2*bc4;
+ bg2 = SQRT2*bc3;
+ fi[4 ] = bf0 - bf2;
+ fi[0 ] = bf0 + bf2;
+ fi[6 ] = bf1 - bf3;
+ fi[2 ] = bf1 + bf3;
+ gi[4 ] = bg0 - bg2;
+ gi[0 ] = bg0 + bg2;
+ gi[6 ] = bg1 - bg3;
+ gi[2 ] = bg1 + bg3;
+ }
+ }
+ if (n<16) return;
+
+ do
+ {
+ REAL s1,c1;
+ int ii;
+ k += 2;
+ k1 = 1 << k;
+ k2 = k1 << 1;
+ k4 = k2 << 1;
+ k3 = k2 + k1;
+ kx = k1 >> 1;
+ fi = fz;
+ gi = fi + kx;
+ fn = fz + n;
+ do
+ {
+ REAL g0,f0,f1,g1,f2,g2,f3,g3;
+ f1 = fi[0 ] - fi[k1];
+ f0 = fi[0 ] + fi[k1];
+ f3 = fi[k2] - fi[k3];
+ f2 = fi[k2] + fi[k3];
+ fi[k2] = f0 - f2;
+ fi[0 ] = f0 + f2;
+ fi[k3] = f1 - f3;
+ fi[k1] = f1 + f3;
+ g1 = gi[0 ] - gi[k1];
+ g0 = gi[0 ] + gi[k1];
+ g3 = SQRT2 * gi[k3];
+ g2 = SQRT2 * gi[k2];
+ gi[k2] = g0 - g2;
+ gi[0 ] = g0 + g2;
+ gi[k3] = g1 - g3;
+ gi[k1] = g1 + g3;
+ gi += k4;
+ fi += k4;
+ } while (fi<fn);
+ TRIG_INIT(k,c1,s1);
+ for (ii=1;ii<kx;ii++)
+ {
+ REAL c2,s2;
+ TRIG_NEXT(k,c1,s1);
+ c2 = c1*c1 - s1*s1;
+ s2 = 2*(c1*s1);
+ fn = fz + n;
+ fi = fz +ii;
+ gi = fz +k1-ii;
+ do
+ {
+ REAL a,b,g0,f0,f1,g1,f2,g2,f3,g3;
+ b = s2*fi[k1] - c2*gi[k1];
+ a = c2*fi[k1] + s2*gi[k1];
+ f1 = fi[0 ] - a;
+ f0 = fi[0 ] + a;
+ g1 = gi[0 ] - b;
+ g0 = gi[0 ] + b;
+ b = s2*fi[k3] - c2*gi[k3];
+ a = c2*fi[k3] + s2*gi[k3];
+ f3 = fi[k2] - a;
+ f2 = fi[k2] + a;
+ g3 = gi[k2] - b;
+ g2 = gi[k2] + b;
+ b = s1*f2 - c1*g3;
+ a = c1*f2 + s1*g3;
+ fi[k2] = f0 - a;
+ fi[0 ] = f0 + a;
+ gi[k3] = g1 - b;
+ gi[k1] = g1 + b;
+ b = c1*g2 - s1*f3;
+ a = s1*g2 + c1*f3;
+ gi[k2] = g0 - a;
+ gi[0 ] = g0 + a;
+ fi[k3] = f1 - b;
+ fi[k1] = f1 + b;
+ gi += k4;
+ fi += k4;
+ } while (fi<fn);
+ }
+ TRIG_RESET(k,c1,s1);
+ } while (k4<n);
+}
+
+void mayer_fft(int n, REAL *real, REAL *imag)
+{
+ REAL a,b,c,d;
+ REAL q,r,s,t;
+ int i,j,k;
+ for (i=1,j=n-1,k=n/2;i<k;i++,j--) {
+ a = real[i]; b = real[j]; q=a+b; r=a-b;
+ c = imag[i]; d = imag[j]; s=c+d; t=c-d;
+ real[i] = (q+t)*.5; real[j] = (q-t)*.5;
+ imag[i] = (s-r)*.5; imag[j] = (s+r)*.5;
+ }
+ mayer_fht(real,n);
+ mayer_fht(imag,n);
+}
+
+void mayer_ifft(int n, REAL *real, REAL *imag)
+{
+ REAL a,b,c,d;
+ REAL q,r,s,t;
+ int i,j,k;
+ mayer_fht(real,n);
+ mayer_fht(imag,n);
+ for (i=1,j=n-1,k=n/2;i<k;i++,j--) {
+ a = real[i]; b = real[j]; q=a+b; r=a-b;
+ c = imag[i]; d = imag[j]; s=c+d; t=c-d;
+ imag[i] = (s+r)*0.5; imag[j] = (s-r)*0.5;
+ real[i] = (q-t)*0.5; real[j] = (q+t)*0.5;
+ }
+}
+
+void mayer_realfft(int n, REAL *real)
+{
+ REAL a,b,c,d;
+ int i,j,k;
+ mayer_fht(real,n);
+ for (i=1,j=n-1,k=n/2;i<k;i++,j--) {
+ a = real[i];
+ b = real[j];
+ real[j] = (a-b)*0.5;
+ real[i] = (a+b)*0.5;
+ }
+}
+
+void mayer_realifft(int n, REAL *real)
+{
+ REAL a,b,c,d;
+ int i,j,k;
+ for (i=1,j=n-1,k=n/2;i<k;i++,j--) {
+ a = real[i];
+ b = real[j];
+ real[j] = (a-b);
+ real[i] = (a+b);
+ }
+ mayer_fht(real,n);
+}
diff --git a/pd/src/d_misc.c b/pd/src/d_misc.c
new file mode 100644
index 00000000..35f4af0b
--- /dev/null
+++ b/pd/src/d_misc.c
@@ -0,0 +1,260 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* miscellaneous: print~; more to come.
+*/
+
+#include "m_pd.h"
+#include <stdio.h>
+#include <string.h>
+
+/* ------------------------- print~ -------------------------- */
+static t_class *print_class;
+
+typedef struct _print
+{
+ t_object x_obj;
+ float x_f;
+ t_symbol *x_sym;
+ int x_count;
+} t_print;
+
+static t_int *print_perform(t_int *w)
+{
+ t_print *x = (t_print *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ if (x->x_count)
+ {
+ post("%s:", x->x_sym->s_name);
+ if (n == 1) post("%8g", in[0]);
+ else if (n == 2) post("%8g %8g", in[0], in[1]);
+ else if (n == 4) post("%8g %8g %8g %8g",
+ in[0], in[1], in[2], in[3]);
+ else while (n > 0)
+ {
+ post("%-8.5g %-8.5g %-8.5g %-8.5g %-8.5g %-8.5g %-8.5g %-8.5g",
+ in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7]);
+ n -= 8;
+ in += 8;
+ }
+ x->x_count--;
+ }
+ return (w+4);
+}
+
+static void print_dsp(t_print *x, t_signal **sp)
+{
+ dsp_add(print_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
+}
+
+static void print_float(t_print *x, t_float f)
+{
+ if (f < 0) f = 0;
+ x->x_count = f;
+}
+
+static void print_bang(t_print *x)
+{
+ x->x_count = 1;
+}
+
+static void *print_new(t_symbol *s)
+{
+ t_print *x = (t_print *)pd_new(print_class);
+ x->x_sym = (s->s_name[0]? s : gensym("print~"));
+ x->x_count = 0;
+ x->x_f = 0;
+ return (x);
+}
+
+static void print_setup(void)
+{
+ print_class = class_new(gensym("print~"), (t_newmethod)print_new, 0,
+ sizeof(t_print), 0, A_DEFSYM, 0);
+ CLASS_MAINSIGNALIN(print_class, t_print, x_f);
+ class_addmethod(print_class, (t_method)print_dsp, gensym("dsp"), 0);
+ class_addbang(print_class, print_bang);
+ class_addfloat(print_class, print_float);
+}
+
+/* ------------------------- scope~ -------------------------- */
+/* this has been replaced by arrays; to be deleted later */
+
+#include "g_canvas.h"
+
+static t_class *scope_class;
+
+#define SCOPESIZE 256
+
+typedef struct _scope
+{
+ t_object x_obj;
+ t_sample x_samps[SCOPESIZE];
+ int x_phase;
+ int x_drawn;
+ void *x_canvas;
+} t_scope;
+
+static t_int *scope_perform(t_int *w)
+{
+ t_scope *x = (t_scope *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ int n = (int)(w[3]), phase = x->x_phase;
+ while (n--)
+ {
+ x->x_samps[phase] = *in++;
+ phase = (phase + 1) & (SCOPESIZE-1);
+ }
+ x->x_phase = phase;
+ return (w+4);
+}
+
+static void scope_dsp(t_scope *x, t_signal **sp)
+{
+ dsp_add(scope_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
+}
+
+static void scope_erase(t_scope *x)
+{
+ if (x->x_drawn) sys_vgui(".x%x.c delete gumbo\n", x->x_canvas);
+}
+
+#define X1 10.
+#define X2 20.
+#define YC 5.
+static void scope_bang(t_scope *x)
+{
+ int n, phase;
+ char hugebuf[10000], *s = hugebuf;
+ scope_erase(x);
+ sys_vgui(".x%x.c create line 10c 5c 20c 5c -tags gumbo\n", x->x_canvas);
+ sprintf(s, ".x%x.c create line ", (t_int)x->x_canvas);
+ s += strlen(s);
+ for (n = 0, phase = x->x_phase;
+ n < SCOPESIZE; phase = ((phase+1) & (SCOPESIZE-1)), n++)
+ {
+ sprintf(s, "%fc %fc ", X1 + (X2 - X1) * (float)n * (1./SCOPESIZE),
+ YC - 5 * x->x_samps[phase]);
+ s += strlen(s);
+ /* post("phase %d", phase); */
+ }
+ sprintf(s, "-tags gumbo\n");
+ sys_gui(hugebuf);
+ x->x_drawn = 1;
+}
+
+static void scope_free(t_scope *x)
+{
+ scope_erase(x);
+}
+
+static void *scope_new(t_symbol *s)
+{
+ t_scope *x = (t_scope *)pd_new(scope_class);
+ error("scope: this is now obsolete; use arrays and tabwrite~ instead");
+ x->x_phase = 0;
+ x->x_drawn = 0;
+ x->x_canvas = canvas_getcurrent();
+ return (x);
+}
+
+static void scope_setup(void)
+{
+ scope_class = class_new(gensym("scope~"), (t_newmethod)scope_new,
+ (t_method)scope_free, sizeof(t_scope), 0, A_DEFSYM, 0);
+ class_addmethod(scope_class, nullfn, gensym("signal"), 0);
+ class_addmethod(scope_class, (t_method)scope_dsp, gensym("dsp"), 0);
+ class_addbang(scope_class, scope_bang);
+}
+
+/* ------------------------ bang~ -------------------------- */
+
+static t_class *bang_tilde_class;
+
+typedef struct _bang
+{
+ t_object x_obj;
+ t_clock *x_clock;
+} t_bang;
+
+static t_int *bang_tilde_perform(t_int *w)
+{
+ t_bang *x = (t_bang *)(w[1]);
+ clock_delay(x->x_clock, 0);
+ return (w+2);
+}
+
+static void bang_tilde_dsp(t_bang *x, t_signal **sp)
+{
+ dsp_add(bang_tilde_perform, 1, x);
+}
+
+static void bang_tilde_tick(t_bang *x)
+{
+ outlet_bang(x->x_obj.ob_outlet);
+}
+
+static void bang_tilde_free(t_bang *x)
+{
+ clock_free(x->x_clock);
+}
+
+static void *bang_tilde_new(t_symbol *s)
+{
+ t_bang *x = (t_bang *)pd_new(bang_tilde_class);
+ x->x_clock = clock_new(x, (t_method)bang_tilde_tick);
+ outlet_new(&x->x_obj, &s_bang);
+ return (x);
+}
+
+static void bang_tilde_setup(void)
+{
+ bang_tilde_class = class_new(gensym("bang~"), (t_newmethod)bang_tilde_new,
+ (t_method)bang_tilde_free, sizeof(t_bang), 0, 0);
+ class_addmethod(bang_tilde_class, (t_method)bang_tilde_dsp,
+ gensym("dsp"), 0);
+}
+
+/* ------------------------ samplerate~~ -------------------------- */
+
+static t_class *samplerate_tilde_class;
+
+typedef struct _samplerate
+{
+ t_object x_obj;
+} t_samplerate;
+
+static void samplerate_tilde_bang(t_samplerate *x)
+{
+ outlet_float(x->x_obj.ob_outlet, sys_getsr());
+}
+
+static void *samplerate_tilde_new(t_symbol *s)
+{
+ t_samplerate *x = (t_samplerate *)pd_new(samplerate_tilde_class);
+ outlet_new(&x->x_obj, &s_float);
+ return (x);
+}
+
+static void samplerate_tilde_setup(void)
+{
+ samplerate_tilde_class = class_new(gensym("samplerate~"),
+ (t_newmethod)samplerate_tilde_new, 0, sizeof(t_samplerate), 0, 0);
+ class_addbang(samplerate_tilde_class, samplerate_tilde_bang);
+}
+
+/* ------------------------ global setup routine ------------------------- */
+
+void d_misc_setup(void)
+{
+ print_setup();
+ scope_setup();
+ bang_tilde_setup();
+ samplerate_tilde_setup();
+}
+
+
+
+
diff --git a/pd/src/d_osc.c b/pd/src/d_osc.c
new file mode 100644
index 00000000..27dceae9
--- /dev/null
+++ b/pd/src/d_osc.c
@@ -0,0 +1,531 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* sinusoidal oscillator and table lookup; see also tabosc4~ in d_array.c.
+*/
+
+#include "m_pd.h"
+#include "math.h"
+
+#define UNITBIT32 1572864. /* 3*2^19; bit 32 has place value 1 */
+
+ /* machine-dependent definitions. These ifdefs really
+ should have been by CPU type and not by operating system! */
+#ifdef IRIX
+ /* big-endian. Most significant byte is at low address in memory */
+#define HIOFFSET 0 /* word offset to find MSB */
+#define LOWOFFSET 1 /* word offset to find LSB */
+#define int32 long /* a data type that has 32 bits */
+#else
+#ifdef NT
+ /* little-endian; most significant byte is at highest address */
+#define HIOFFSET 1
+#define LOWOFFSET 0
+#define int32 long
+#else
+#ifdef __FreeBSD__
+#include <machine/endian.h>
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define HIOFFSET 1
+#define LOWOFFSET 0
+#else
+#define HIOFFSET 0 /* word offset to find MSB */
+#define LOWOFFSET 1 /* word offset to find LSB */
+#endif /* BYTE_ORDER */
+#include <sys/types.h>
+#define int32 int32_t
+#endif
+#ifdef __linux__
+
+#include <endian.h>
+
+#if !defined(__BYTE_ORDER) || !defined(__LITTLE_ENDIAN)
+#error No byte order defined
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define HIOFFSET 1
+#define LOWOFFSET 0
+#else
+#define HIOFFSET 0 /* word offset to find MSB */
+#define LOWOFFSET 1 /* word offset to find LSB */
+#endif /* __BYTE_ORDER */
+
+#include <sys/types.h>
+#define int32 int32_t
+
+#else
+#ifdef MACOSX
+#define HIOFFSET 0 /* word offset to find MSB */
+#define LOWOFFSET 1 /* word offset to find LSB */
+#define int32 int /* a data type that has 32 bits */
+
+#endif /* MACOSX */
+#endif /* __linux__ */
+#endif /* NT */
+#endif /* SGI */
+
+union tabfudge
+{
+ double tf_d;
+ int32 tf_i[2];
+};
+
+
+/* -------------------------- phasor~ ------------------------------ */
+static t_class *phasor_class, *scalarphasor_class;
+
+#if 1 /* in the style of R. Hoeldrich (ICMC 1995 Banff) */
+
+typedef struct _phasor
+{
+ t_object x_obj;
+ double x_phase;
+ float x_conv;
+ float x_f; /* scalar frequency */
+} t_phasor;
+
+static void *phasor_new(t_floatarg f)
+{
+ t_phasor *x = (t_phasor *)pd_new(phasor_class);
+ x->x_f = f;
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1"));
+ x->x_phase = 0;
+ x->x_conv = 0;
+ outlet_new(&x->x_obj, gensym("signal"));
+ return (x);
+}
+
+static t_int *phasor_perform(t_int *w)
+{
+ t_phasor *x = (t_phasor *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ double dphase = x->x_phase + UNITBIT32;
+ union tabfudge tf;
+ int normhipart;
+ float conv = x->x_conv;
+
+ tf.tf_d = UNITBIT32;
+ normhipart = tf.tf_i[HIOFFSET];
+ tf.tf_d = dphase;
+
+ while (n--)
+ {
+ tf.tf_i[HIOFFSET] = normhipart;
+ dphase += *in++ * conv;
+ *out++ = tf.tf_d - UNITBIT32;
+ tf.tf_d = dphase;
+ }
+ tf.tf_i[HIOFFSET] = normhipart;
+ x->x_phase = tf.tf_d - UNITBIT32;
+ return (w+5);
+}
+
+static void phasor_dsp(t_phasor *x, t_signal **sp)
+{
+ x->x_conv = 1./sp[0]->s_sr;
+ dsp_add(phasor_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+static void phasor_ft1(t_phasor *x, t_float f)
+{
+ x->x_phase = f;
+}
+
+static void phasor_setup(void)
+{
+ phasor_class = class_new(gensym("phasor~"), (t_newmethod)phasor_new, 0,
+ sizeof(t_phasor), 0, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(phasor_class, t_phasor, x_f);
+ class_addmethod(phasor_class, (t_method)phasor_dsp, gensym("dsp"), 0);
+ class_addmethod(phasor_class, (t_method)phasor_ft1,
+ gensym("ft1"), A_FLOAT, 0);
+}
+
+#endif /* Hoeldrich version */
+
+/* ------------------------ cos~ ----------------------------- */
+
+float *cos_table;
+
+static t_class *cos_class;
+
+typedef struct _cos
+{
+ t_object x_obj;
+ float x_f;
+} t_cos;
+
+static void *cos_new(void)
+{
+ t_cos *x = (t_cos *)pd_new(cos_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_f = 0;
+ return (x);
+}
+
+static t_int *cos_perform(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ float *tab = cos_table, *addr, f1, f2, frac;
+ double dphase;
+ int normhipart;
+ union tabfudge tf;
+
+ tf.tf_d = UNITBIT32;
+ normhipart = tf.tf_i[HIOFFSET];
+
+#if 0 /* this is the readable version of the code. */
+ while (n--)
+ {
+ dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32;
+ tf.tf_d = dphase;
+ addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1));
+ tf.tf_i[HIOFFSET] = normhipart;
+ frac = tf.tf_d - UNITBIT32;
+ f1 = addr[0];
+ f2 = addr[1];
+ *out++ = f1 + frac * (f2 - f1);
+ }
+#endif
+#if 1 /* this is the same, unwrapped by hand. */
+ dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32;
+ tf.tf_d = dphase;
+ addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1));
+ tf.tf_i[HIOFFSET] = normhipart;
+ while (--n)
+ {
+ dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32;
+ frac = tf.tf_d - UNITBIT32;
+ tf.tf_d = dphase;
+ f1 = addr[0];
+ f2 = addr[1];
+ addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1));
+ *out++ = f1 + frac * (f2 - f1);
+ tf.tf_i[HIOFFSET] = normhipart;
+ }
+ frac = tf.tf_d - UNITBIT32;
+ f1 = addr[0];
+ f2 = addr[1];
+ *out++ = f1 + frac * (f2 - f1);
+#endif
+ return (w+4);
+}
+
+static void cos_dsp(t_cos *x, t_signal **sp)
+{
+ dsp_add(cos_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+static void cos_maketable(void)
+{
+ int i;
+ float *fp, phase, phsinc = (2. * 3.14159) / COSTABSIZE;
+ union tabfudge tf;
+
+ if (cos_table) return;
+ cos_table = (float *)getbytes(sizeof(float) * (COSTABSIZE+1));
+ for (i = COSTABSIZE + 1, fp = cos_table, phase = 0; i--;
+ fp++, phase += phsinc)
+ *fp = cos(phase);
+
+ /* here we check at startup whether the byte alignment
+ is as we declared it. If not, the code has to be
+ recompiled the other way. */
+ tf.tf_d = UNITBIT32 + 0.5;
+ if ((unsigned)tf.tf_i[LOWOFFSET] != 0x80000000)
+ bug("cos~: unexpected machine alignment");
+}
+
+static void cos_setup(void)
+{
+ cos_class = class_new(gensym("cos~"), (t_newmethod)cos_new, 0,
+ sizeof(t_cos), 0, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(cos_class, t_cos, x_f);
+ class_addmethod(cos_class, (t_method)cos_dsp, gensym("dsp"), 0);
+ cos_maketable();
+}
+
+/* ------------------------ osc~ ----------------------------- */
+
+static t_class *osc_class, *scalarosc_class;
+
+typedef struct _osc
+{
+ t_object x_obj;
+ double x_phase;
+ float x_conv;
+ float x_f; /* frequency if scalar */
+} t_osc;
+
+static void *osc_new(t_floatarg f)
+{
+ t_osc *x = (t_osc *)pd_new(osc_class);
+ x->x_f = f;
+ outlet_new(&x->x_obj, gensym("signal"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1"));
+ x->x_phase = 0;
+ x->x_conv = 0;
+ return (x);
+}
+
+static t_int *osc_perform(t_int *w)
+{
+ t_osc *x = (t_osc *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ float *tab = cos_table, *addr, f1, f2, frac;
+ double dphase = x->x_phase + UNITBIT32;
+ int normhipart;
+ union tabfudge tf;
+ float conv = x->x_conv;
+
+ tf.tf_d = UNITBIT32;
+ normhipart = tf.tf_i[HIOFFSET];
+#if 0
+ while (n--)
+ {
+ tf.tf_d = dphase;
+ dphase += *in++ * conv;
+ addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1));
+ tf.tf_i[HIOFFSET] = normhipart;
+ frac = tf.tf_d - UNITBIT32;
+ f1 = addr[0];
+ f2 = addr[1];
+ *out++ = f1 + frac * (f2 - f1);
+ }
+#endif
+#if 1
+ tf.tf_d = dphase;
+ dphase += *in++ * conv;
+ addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1));
+ tf.tf_i[HIOFFSET] = normhipart;
+ frac = tf.tf_d - UNITBIT32;
+ while (--n)
+ {
+ tf.tf_d = dphase;
+ f1 = addr[0];
+ dphase += *in++ * conv;
+ f2 = addr[1];
+ addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1));
+ tf.tf_i[HIOFFSET] = normhipart;
+ *out++ = f1 + frac * (f2 - f1);
+ frac = tf.tf_d - UNITBIT32;
+ }
+ f1 = addr[0];
+ f2 = addr[1];
+ *out++ = f1 + frac * (f2 - f1);
+#endif
+
+ tf.tf_d = UNITBIT32 * COSTABSIZE;
+ normhipart = tf.tf_i[HIOFFSET];
+ tf.tf_d = dphase + (UNITBIT32 * COSTABSIZE - UNITBIT32);
+ tf.tf_i[HIOFFSET] = normhipart;
+ x->x_phase = tf.tf_d - UNITBIT32 * COSTABSIZE;
+ return (w+5);
+}
+
+static void osc_dsp(t_osc *x, t_signal **sp)
+{
+ x->x_conv = COSTABSIZE/sp[0]->s_sr;
+ dsp_add(osc_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+static void osc_ft1(t_osc *x, t_float f)
+{
+ x->x_phase = COSTABSIZE * f;
+}
+
+static void osc_setup(void)
+{
+ osc_class = class_new(gensym("osc~"), (t_newmethod)osc_new, 0,
+ sizeof(t_osc), 0, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(osc_class, t_osc, x_f);
+ class_addmethod(osc_class, (t_method)osc_dsp, gensym("dsp"), 0);
+ class_addmethod(osc_class, (t_method)osc_ft1, gensym("ft1"), A_FLOAT, 0);
+
+ cos_maketable();
+}
+
+/* ---------------- vcf~ - 2-pole bandpass filter. ----------------- */
+
+typedef struct vcfctl
+{
+ float c_re;
+ float c_im;
+ float c_q;
+ float c_isr;
+} t_vcfctl;
+
+typedef struct sigvcf
+{
+ t_object x_obj;
+ t_vcfctl x_cspace;
+ t_vcfctl *x_ctl;
+ float x_f;
+} t_sigvcf;
+
+t_class *sigvcf_class;
+
+static void *sigvcf_new(t_floatarg q)
+{
+ t_sigvcf *x = (t_sigvcf *)pd_new(sigvcf_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1"));
+ outlet_new(&x->x_obj, gensym("signal"));
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_ctl = &x->x_cspace;
+ x->x_cspace.c_re = 0;
+ x->x_cspace.c_im = 0;
+ x->x_cspace.c_q = q;
+ x->x_cspace.c_isr = 0;
+ x->x_f = 0;
+ return (x);
+}
+
+static void sigvcf_ft1(t_sigvcf *x, t_floatarg f)
+{
+ x->x_ctl->c_q = (f > 0 ? f : 0.f);
+}
+
+static t_int *sigvcf_perform(t_int *w)
+{
+ float *in1 = (float *)(w[1]);
+ float *in2 = (float *)(w[2]);
+ float *out1 = (float *)(w[3]);
+ float *out2 = (float *)(w[4]);
+ t_vcfctl *c = (t_vcfctl *)(w[5]);
+ int n = (t_int)(w[6]);
+ int i;
+ float re = c->c_re, re2;
+ float im = c->c_im;
+ float q = c->c_q;
+ float qinv = (q > 0? 1.0f/q : 0);
+ float ampcorrect = 2.0f - 2.0f / (q + 2.0f);
+ float isr = c->c_isr;
+ float coefr, coefi;
+ float *tab = cos_table, *addr, f1, f2, frac;
+ double dphase;
+ int normhipart, tabindex;
+ union tabfudge tf;
+
+ tf.tf_d = UNITBIT32;
+ normhipart = tf.tf_i[HIOFFSET];
+
+ for (i = 0; i < n; i++)
+ {
+ float cf, cfindx, r, oneminusr;
+ cf = *in2++ * isr;
+ if (cf < 0) cf = 0;
+ cfindx = cf * (float)(COSTABSIZE/6.28318f);
+ r = (qinv > 0 ? 1 - cf * qinv : 0);
+ if (r < 0) r = 0;
+ oneminusr = 1.0f - r;
+ dphase = ((double)(cfindx)) + UNITBIT32;
+ tf.tf_d = dphase;
+ tabindex = tf.tf_i[HIOFFSET] & (COSTABSIZE-1);
+ addr = tab + tabindex;
+ tf.tf_i[HIOFFSET] = normhipart;
+ frac = tf.tf_d - UNITBIT32;
+ f1 = addr[0];
+ f2 = addr[1];
+ coefr = r * (f1 + frac * (f2 - f1));
+
+ addr = tab + ((tabindex - (COSTABSIZE/4)) & (COSTABSIZE-1));
+ f1 = addr[0];
+ f2 = addr[1];
+ coefi = r * (f1 + frac * (f2 - f1));
+
+ f1 = *in1++;
+ re2 = re;
+ *out1++ = re = ampcorrect * oneminusr * f1
+ + coefr * re2 - coefi * im;
+ *out2++ = im = coefi * re2 + coefr * im;
+ }
+ c->c_re = re;
+ c->c_im = im;
+ return (w+7);
+}
+
+static void sigvcf_dsp(t_sigvcf *x, t_signal **sp)
+{
+ x->x_ctl->c_isr = 6.28318f/sp[0]->s_sr;
+ dsp_add(sigvcf_perform, 6,
+ sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec,
+ x->x_ctl, sp[0]->s_n);
+
+}
+
+void sigvcf_setup(void)
+{
+ sigvcf_class = class_new(gensym("vcf~"), (t_newmethod)sigvcf_new, 0,
+ sizeof(t_sigvcf), 0, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(sigvcf_class, t_sigvcf, x_f);
+ class_addmethod(sigvcf_class, (t_method)sigvcf_dsp, gensym("dsp"), 0);
+ class_addmethod(sigvcf_class, (t_method)sigvcf_ft1,
+ gensym("ft1"), A_FLOAT, 0);
+}
+
+/* -------------------------- noise~ ------------------------------ */
+static t_class *noise_class;
+
+typedef struct _noise
+{
+ t_object x_obj;
+ int x_val;
+} t_noise;
+
+static void *noise_new(void)
+{
+ t_noise *x = (t_noise *)pd_new(noise_class);
+ static int init = 307;
+ x->x_val = (init *= 1319);
+ outlet_new(&x->x_obj, gensym("signal"));
+ return (x);
+}
+
+static t_int *noise_perform(t_int *w)
+{
+ t_float *out = (t_float *)(w[1]);
+ int *vp = (int *)(w[2]);
+ int n = (int)(w[3]);
+ int val = *vp;
+ while (n--)
+ {
+ *out++ = ((float)((val & 0x7fffffff) - 0x40000000)) *
+ (float)(1.0 / 0x40000000);
+ val = val * 435898247 + 382842987;
+ }
+ *vp = val;
+ return (w+4);
+}
+
+static void noise_dsp(t_noise *x, t_signal **sp)
+{
+ dsp_add(noise_perform, 3, sp[0]->s_vec, &x->x_val, sp[0]->s_n);
+}
+
+static void noise_setup(void)
+{
+ noise_class = class_new(gensym("noise~"), (t_newmethod)noise_new, 0,
+ sizeof(t_noise), 0, 0);
+ class_addmethod(noise_class, (t_method)noise_dsp, gensym("dsp"), 0);
+}
+
+
+/* ----------------------- global setup routine ---------------- */
+void d_osc_setup(void)
+{
+ phasor_setup();
+ cos_setup();
+ osc_setup();
+ sigvcf_setup();
+ noise_setup();
+}
+
diff --git a/pd/src/d_resample.c b/pd/src/d_resample.c
new file mode 100644
index 00000000..83ed7498
--- /dev/null
+++ b/pd/src/d_resample.c
@@ -0,0 +1,225 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* upsampling/downsampling methods for inlet~/outlet~
+ *
+ * mfg.gfd.uil
+ * IOhannes
+ *
+ * 2509:forum::für::umläute:2001
+ */
+
+
+
+#include "m_pd.h"
+
+/* --------------------- up/down-sampling --------------------- */
+t_int *downsampling_perform_0(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]); /* original signal */
+ t_float *out = (t_float *)(w[2]); /* downsampled signal */
+ int down = (int)(w[3]); /* downsampling factor */
+ int parent = (int)(w[4]); /* original vectorsize */
+
+ int n=parent/down;
+
+ while(n--){
+ *out++=*in;
+ in+=down;
+ }
+
+ return (w+5);
+}
+
+t_int *upsampling_perform_0(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]); /* original signal */
+ t_float *out = (t_float *)(w[2]); /* upsampled signal */
+ int up = (int)(w[3]); /* upsampling factor */
+ int parent = (int)(w[4]); /* original vectorsize */
+
+ int n=parent*up;
+ t_float *dummy = out;
+
+ while(n--)*out++=0;
+
+ n = parent;
+ out = dummy;
+ while(n--){
+ *out=*in++;
+ out+=up;
+ }
+
+ return (w+5);
+}
+
+t_int *upsampling_perform_hold(t_int *w)
+{
+ t_float *in = (t_float *)(w[1]); /* original signal */
+ t_float *out = (t_float *)(w[2]); /* upsampled signal */
+ int up = (int)(w[3]); /* upsampling factor */
+ int parent = (int)(w[4]); /* original vectorsize */
+ int i=up;
+
+ int n=parent;
+ t_float *dum_out = out;
+ t_float *dum_in = in;
+
+ while (i--) {
+ n = parent;
+ out = dum_out+i;
+ in = dum_in;
+ while(n--){
+ *out=*in++;
+ out+=up;
+ }
+ }
+ return (w+5);
+}
+
+t_int *upsampling_perform_linear(t_int *w)
+{
+ t_resample *x= (t_resample *)(w[1]);
+ t_float *in = (t_float *)(w[2]); /* original signal */
+ t_float *out = (t_float *)(w[3]); /* upsampled signal */
+ int up = (int)(w[4]); /* upsampling factor */
+ int parent = (int)(w[5]); /* original vectorsize */
+ int length = parent*up;
+ int n;
+ t_float *fp;
+ t_float a=*x->buffer, b=*in;
+
+
+ for (n=0; n<length; n++) {
+ t_float findex = (t_float)(n+1)/up;
+ int index = findex;
+ t_float frac=findex - index;
+ if (frac==0.)frac=1.;
+ *out++ = frac * b + (1.-frac) * a;
+ fp = in+index;
+ b=*fp;
+ a=(index)?*(fp-1):a;
+ }
+
+ *x->buffer = a;
+ return (w+6);
+}
+
+/* ----------------------- public -------------------------------- */
+
+/* utils */
+
+void resample_init(t_resample *x)
+{
+ x->method=0;
+
+ x->downsample=x->upsample=1;
+
+ x->s_n = x->coefsize = x->bufsize = 0;
+ x->s_vec = x->coeffs = x->buffer = 0;
+}
+
+void resample_free(t_resample *x)
+{
+ if (x->s_n) t_freebytes(x->s_vec, x->s_n*sizeof(*x->s_vec));
+ if (x->coefsize) t_freebytes(x->coeffs, x->coefsize*sizeof(*x->coeffs));
+ if (x->bufsize) t_freebytes(x->buffer, x->bufsize*sizeof(*x->buffer));
+
+ x->s_n = x->coefsize = x->bufsize = 0;
+ x->s_vec = x->coeffs = x->buffer = 0;
+}
+
+
+/* dsp-adding */
+
+void resample_dsp(t_resample *x,
+ t_sample* in, int insize,
+ t_sample* out, int outsize,
+ int method)
+{
+ if (insize == outsize){
+ bug("nothing to be done");
+ return;
+ }
+
+ if (insize > outsize) { /* downsampling */
+ if (insize % outsize) {
+ error("bad downsampling factor");
+ return;
+ }
+ switch (method) {
+ default:
+ dsp_add(downsampling_perform_0, 4, in, out, insize/outsize, insize);
+ }
+
+
+ } else { /* upsampling */
+ if (outsize % insize) {
+ error("bad upsampling factor");
+ return;
+ }
+ switch (method) {
+ case 1:
+ dsp_add(upsampling_perform_hold, 4, in, out, outsize/insize, insize);
+ break;
+ case 2:
+ if (x->bufsize != 1) {
+ t_freebytes(x->buffer, x->bufsize*sizeof(*x->buffer));
+ x->bufsize = 1;
+ x->buffer = t_getbytes(x->bufsize*sizeof(*x->buffer));
+ }
+ dsp_add(upsampling_perform_linear, 5, x, in, out, outsize/insize, insize);
+ break;
+ default:
+ dsp_add(upsampling_perform_0, 4, in, out, outsize/insize, insize);
+ }
+ }
+}
+
+void resamplefrom_dsp(t_resample *x,
+ t_sample *in,
+ int insize, int outsize, int method)
+{
+ if (insize==outsize) {
+ t_freebytes(x->s_vec, x->s_n * sizeof(*x->s_vec));
+ x->s_n = 0;
+ x->s_vec = in;
+ return;
+ }
+
+ if (x->s_n != outsize) {
+ t_float *buf=x->s_vec;
+ t_freebytes(buf, x->s_n * sizeof(*buf));
+ buf = (t_float *)t_getbytes(outsize * sizeof(*buf));
+ x->s_vec = buf;
+ x->s_n = outsize;
+ }
+
+ resample_dsp(x, in, insize, x->s_vec, x->s_n, method);
+ return;
+}
+
+void resampleto_dsp(t_resample *x,
+ t_sample *out,
+ int insize, int outsize, int method)
+{
+ if (insize==outsize) {
+ if (x->s_n)t_freebytes(x->s_vec, x->s_n * sizeof(*x->s_vec));
+ x->s_n = 0;
+ x->s_vec = out;
+ return;
+ }
+
+ if (x->s_n != insize) {
+ t_float *buf=x->s_vec;
+ t_freebytes(buf, x->s_n * sizeof(*buf));
+ buf = (t_float *)t_getbytes(insize * sizeof(*buf));
+ x->s_vec = buf;
+ x->s_n = insize;
+ }
+
+ resample_dsp(x, x->s_vec, x->s_n, out, outsize, method);
+
+ return;
+}
diff --git a/pd/src/d_soundfile.c b/pd/src/d_soundfile.c
new file mode 100644
index 00000000..33de90b7
--- /dev/null
+++ b/pd/src/d_soundfile.c
@@ -0,0 +1,2200 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* this file contains, first, a collection of soundfile access routines, a
+sort of soundfile library. Second, the "soundfiler" object is defined which
+uses the routines to read or write soundfiles, synchronously, from garrays.
+These operations are not to be done in "real time" as they may have to wait
+for disk accesses (even the write routine.) Finally, the realtime objects
+readsf~ and writesf~ are defined which confine disk operations to a separate
+thread so that they can be used in real time. The real-time disk access
+objects are available for linux only so far, although they could be compiled
+for Windows if someone were willing to find a Pthreads package for it. */
+
+#ifdef UNIX
+#include <unistd.h>
+#include <fcntl.h>
+#endif
+#include <pthread.h>
+#ifdef NT
+#include <io.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "m_pd.h"
+
+#define MAXSFCHANS 64
+
+/***************** soundfile header structures ************************/
+
+typedef unsigned short uint16;
+typedef unsigned long uint32;
+
+#define FORMAT_WAVE 0
+#define FORMAT_AIFF 1
+#define FORMAT_NEXT 2
+
+/* the NeXTStep sound header structure; can be big or little endian */
+
+typedef struct _nextstep
+{
+ char ns_fileid[4]; /* magic number '.snd' if file is big-endian */
+ uint32 ns_onset; /* byte offset of first sample */
+ uint32 ns_length; /* length of sound in bytes */
+ uint32 ns_format; /* format; see below */
+ uint32 ns_sr; /* sample rate */
+ uint32 ns_nchans; /* number of channels */
+ char ns_info[4]; /* comment */
+} t_nextstep;
+
+#define NS_FORMAT_LINEAR_16 3
+#define NS_FORMAT_LINEAR_24 4
+#define NS_FORMAT_FLOAT 6
+#define SCALE (1./(1024. * 1024. * 1024. * 2.))
+
+/* the WAVE header. All Wave files are little endian. We assume
+ the "fmt" chunk comes first which is usually the case but perhaps not
+ always; same for AIFF and the "COMM" chunk. */
+
+typedef unsigned word;
+typedef unsigned long dword;
+
+typedef struct _wave
+{
+ char w_fileid[4]; /* chunk id 'RIFF' */
+ uint32 w_chunksize; /* chunk size */
+ char w_waveid[4]; /* wave chunk id 'WAVE' */
+ char w_fmtid[4]; /* format chunk id 'fmt ' */
+ uint32 w_fmtchunksize; /* format chunk size */
+ uint16 w_fmttag; /* format tag, 1 for PCM */
+ uint16 w_nchannels; /* number of channels */
+ uint32 w_samplespersec; /* sample rate in hz */
+ uint32 w_navgbytespersec; /* average bytes per second */
+ uint16 w_nblockalign; /* number of bytes per frame */
+ uint16 w_nbitspersample; /* number of bits in a sample */
+ char w_datachunkid[4]; /* data chunk id 'data' */
+ uint32 w_datachunksize; /* length of data chunk */
+} t_wave;
+
+typedef struct _fmt /* format chunk */
+{
+ uint16 f_fmttag; /* format tag, 1 for PCM */
+ uint16 f_nchannels; /* number of channels */
+ uint32 f_samplespersec; /* sample rate in hz */
+ uint32 f_navgbytespersec; /* average bytes per second */
+ uint16 f_nblockalign; /* number of bytes per frame */
+ uint16 f_nbitspersample; /* number of bits in a sample */
+} t_fmt;
+
+typedef struct _wavechunk /* ... and the last two items */
+{
+ char wc_id[4]; /* data chunk id, e.g., 'data' or 'fmt ' */
+ uint32 wc_size; /* length of data chunk */
+} t_wavechunk;
+
+/* the AIFF header. I'm assuming AIFC is compatible but don't really know
+ that. */
+
+typedef struct _datachunk
+{
+ char dc_id[4]; /* data chunk id 'SSND' */
+ uint32 dc_size; /* length of data chunk */
+} t_datachunk;
+
+typedef struct _comm
+{
+ uint16 c_nchannels; /* number of channels */
+ uint16 c_nframeshi; /* # of sample frames (hi) */
+ uint16 c_nframeslo; /* # of sample frames (lo) */
+ uint16 c_bitspersamp; /* bits per sample */
+ unsigned char c_samprate[10]; /* sample rate, 80-bit float! */
+} t_comm;
+
+ /* this version is more convenient for writing them out: */
+typedef struct _aiff
+{
+ char a_fileid[4]; /* chunk id 'FORM' */
+ uint32 a_chunksize; /* chunk size */
+ char a_aiffid[4]; /* aiff chunk id 'AIFF' */
+ char a_fmtid[4]; /* format chunk id 'COMM' */
+ uint32 a_fmtchunksize; /* format chunk size, 18 */
+ uint16 a_nchannels; /* number of channels */
+ uint16 a_nframeshi; /* # of sample frames (hi) */
+ uint16 a_nframeslo; /* # of sample frames (lo) */
+ uint16 a_bitspersamp; /* bits per sample */
+ unsigned char a_samprate[10]; /* sample rate, 80-bit float! */
+} t_aiff;
+
+#define AIFFHDRSIZE 38 /* probably not what sizeof() gives */
+
+
+#define AIFFPLUS (AIFFHDRSIZE + 8) /* header size including first chunk hdr */
+
+#define WHDR1 sizeof(t_nextstep)
+#define WHDR2 (sizeof(t_wave) > WHDR1 ? sizeof (t_wave) : WHDR1)
+#define WRITEHDRSIZE (AIFFPLUS > WHDR2 ? AIFFPLUS : WHDR2)
+
+#define READHDRSIZE (16 > WHDR2 + 2 ? 16 : WHDR2 + 2)
+
+#define OBUFSIZE MAXPDSTRING /* assume MAXPDSTRING is bigger than headers */
+
+#ifdef NT
+#include <fcntl.h>
+#define BINCREATE _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY
+#else
+#define BINCREATE O_WRONLY | O_CREAT | O_TRUNC
+#endif
+
+/* this routine returns 1 if the high order byte comes at the lower
+address on our architecture (big-endianness.). It's 1 for Motorola,
+0 for Intel: */
+
+extern int garray_ambigendian(void);
+
+/* byte swappers */
+
+static uint32 swap4(uint32 n, int doit)
+{
+ if (doit)
+ return (((n & 0xff) << 24) | ((n & 0xff00) << 8) |
+ ((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24));
+ else return (n);
+}
+
+static uint16 swap2(uint32 n, int doit)
+{
+ if (doit)
+ return (((n & 0xff) << 8) | ((n & 0xff00) >> 8));
+ else return (n);
+}
+
+static void swapstring(char *foo, int doit)
+{
+ if (doit)
+ {
+ char a = foo[0], b = foo[1], c = foo[2], d = foo[3];
+ foo[0] = d; foo[1] = c; foo[2] = b; foo[3] = a;
+ }
+}
+
+/******************** soundfile access routines **********************/
+
+void readsf_banana( void); /* debugging */
+
+/* This routine opens a file, looks for either a nextstep or "wave" header,
+* seeks to end of it, and fills in bytes per sample and number of channels.
+* Only 2- and 3-byte fixed-point samples and 4-byte floating point samples
+* are supported. If "headersize" is nonzero, the
+* caller should supply the number of channels, endinanness, and bytes per
+* sample; the header is ignored. Otherwise, the routine tries to read the
+* header and fill in the properties.
+*/
+
+int open_soundfile(const char *dirname, const char *filename, int headersize,
+ int *p_bytespersamp, int *p_bigendian, int *p_nchannels, long *p_bytelimit,
+ long skipframes)
+{
+ char buf[OBUFSIZE], *bufptr;
+ int fd, format, nchannels, bigendian, bytespersamp, swap, sysrtn;
+ long bytelimit = 0x7fffffff;
+ errno = 0;
+ fd = open_via_path(dirname, filename,
+ "", buf, &bufptr, MAXPDSTRING, 1);
+ if (fd < 0)
+ return (-1);
+ if (headersize >= 0) /* header detection overridden */
+ {
+ bigendian = *p_bigendian;
+ nchannels = *p_nchannels;
+ bytespersamp = *p_bytespersamp;
+ bytelimit = *p_bytelimit;
+ }
+ else
+ {
+ int bytesread = read(fd, buf, READHDRSIZE);
+ int format;
+ if (bytesread < 4)
+ goto badheader;
+ if (!strncmp(buf, ".snd", 4))
+ format = FORMAT_NEXT, bigendian = 1;
+ else if (!strncmp(buf, "dns.", 4))
+ format = FORMAT_NEXT, bigendian = 0;
+ else if (!strncmp(buf, "RIFF", 4))
+ {
+ if (bytesread < 12 || strncmp(buf + 8, "WAVE", 4))
+ goto badheader;
+ format = FORMAT_WAVE, bigendian = 0;
+ }
+ else if (!strncmp(buf, "FORM", 4))
+ {
+ if (bytesread < 12 || strncmp(buf + 8, "AIFF", 4))
+ goto badheader;
+ format = FORMAT_AIFF, bigendian = 1;
+ }
+ else
+ goto badheader;
+ swap = (bigendian != garray_ambigendian());
+ if (format == FORMAT_NEXT) /* nextstep header */
+ {
+ uint32 param;
+ if (bytesread < (int)sizeof(t_nextstep))
+ goto badheader;
+ nchannels = swap4(((t_nextstep *)buf)->ns_nchans, swap);
+ format = swap4(((t_nextstep *)buf)->ns_format, swap);
+ headersize = swap4(((t_nextstep *)buf)->ns_onset, swap);
+ if (format == NS_FORMAT_LINEAR_16)
+ bytespersamp = 2;
+ else if (format == NS_FORMAT_LINEAR_24)
+ bytespersamp = 3;
+ else if (format == NS_FORMAT_FLOAT)
+ bytespersamp = 4;
+ else goto badheader;
+ bytelimit = 0x7fffffff;
+ }
+ else if (format == FORMAT_WAVE) /* wave header */
+ {
+ /* This is awful. You have to skip over chunks,
+ except that if one happens to be a "fmt" chunk, you want to
+ find out the format from that one. The case where the
+ "fmt" chunk comes after the audio isn't handled. */
+ headersize = 12;
+ if (bytesread < 20)
+ goto badheader;
+ /* First we guess a number of channels, etc., in case there's
+ no "fmt" chunk to follow. */
+ nchannels = 1;
+ bytespersamp = 2;
+ /* copy the first chunk header to beginnning of buffer. */
+ memcpy(buf, buf + headersize, sizeof(t_wavechunk));
+ /* post("chunk %c %c %c %c",
+ ((t_wavechunk *)buf)->wc_id[0],
+ ((t_wavechunk *)buf)->wc_id[1],
+ ((t_wavechunk *)buf)->wc_id[2],
+ ((t_wavechunk *)buf)->wc_id[3]); */
+ /* read chunks in loop until we get to the data chunk */
+ while (strncmp(((t_wavechunk *)buf)->wc_id, "data", 4))
+ {
+ long chunksize = swap4(((t_wavechunk *)buf)->wc_size,
+ swap), seekto = headersize + chunksize + 8, seekout;
+
+ if (!strncmp(((t_wavechunk *)buf)->wc_id, "fmt ", 4))
+ {
+ long commblockonset = headersize + 8;
+ seekout = lseek(fd, commblockonset, SEEK_SET);
+ if (seekout != commblockonset)
+ goto badheader;
+ if (read(fd, buf, sizeof(t_fmt)) < (int) sizeof(t_fmt))
+ goto badheader;
+ nchannels = swap2(((t_fmt *)buf)->f_nchannels, swap);
+ format = swap2(((t_fmt *)buf)->f_nbitspersample, swap);
+ if (format == 16)
+ bytespersamp = 2;
+ else if (format == 24)
+ bytespersamp = 3;
+ else if (format == 32)
+ bytespersamp = 4;
+ else goto badheader;
+ }
+ seekout = lseek(fd, seekto, SEEK_SET);
+ if (seekout != seekto)
+ goto badheader;
+ if (read(fd, buf, sizeof(t_wavechunk)) <
+ (int) sizeof(t_wavechunk))
+ goto badheader;
+ /* post("new chunk %c %c %c %c at %d",
+ ((t_wavechunk *)buf)->wc_id[0],
+ ((t_wavechunk *)buf)->wc_id[1],
+ ((t_wavechunk *)buf)->wc_id[2],
+ ((t_wavechunk *)buf)->wc_id[3], seekto); */
+ headersize = seekto;
+ }
+ bytelimit = swap4(((t_wavechunk *)buf)->wc_size, swap);
+ headersize += 8;
+ }
+ else
+ {
+ /* AIFF. same as WAVE; actually predates it. Disgusting. */
+ headersize = 12;
+ if (bytesread < 20)
+ goto badheader;
+ /* First we guess a number of channels, etc., in case there's
+ no COMM block to follow. */
+ nchannels = 1;
+ bytespersamp = 2;
+ /* copy the first chunk header to beginnning of buffer. */
+ memcpy(buf, buf + headersize, sizeof(t_datachunk));
+ /* read chunks in loop until we get to the data chunk */
+ while (strncmp(((t_datachunk *)buf)->dc_id, "SSND", 4))
+ {
+ long chunksize = swap4(((t_datachunk *)buf)->dc_size,
+ swap), seekto = headersize + chunksize + 8, seekout;
+ /* post("chunk %c %c %c %c seek %d",
+ ((t_datachunk *)buf)->dc_id[0],
+ ((t_datachunk *)buf)->dc_id[1],
+ ((t_datachunk *)buf)->dc_id[2],
+ ((t_datachunk *)buf)->dc_id[3], seekto); */
+ if (!strncmp(((t_datachunk *)buf)->dc_id, "COMM", 4))
+ {
+ long commblockonset = headersize + 8;
+ seekout = lseek(fd, commblockonset, SEEK_SET);
+ if (seekout != commblockonset)
+ goto badheader;
+ if (read(fd, buf, sizeof(t_comm)) <
+ (int) sizeof(t_comm))
+ goto badheader;
+ nchannels = swap2(((t_comm *)buf)->c_nchannels, swap);
+ format = swap2(((t_comm *)buf)->c_bitspersamp, swap);
+ if (format == 16)
+ bytespersamp = 2;
+ else if (format == 24)
+ bytespersamp = 3;
+ else goto badheader;
+ }
+ seekout = lseek(fd, seekto, SEEK_SET);
+ if (seekout != seekto)
+ goto badheader;
+ if (read(fd, buf, sizeof(t_datachunk)) <
+ (int) sizeof(t_datachunk))
+ goto badheader;
+ headersize = seekto;
+ }
+ bytelimit = swap4(((t_datachunk *)buf)->dc_size, swap);
+ headersize += 8;
+ }
+ }
+ /* seek past header and any sample frames to skip */
+ sysrtn = lseek(fd, nchannels * bytespersamp * skipframes + headersize, 0);
+ if (sysrtn != nchannels * bytespersamp * skipframes + headersize)
+ return (-1);
+ /* copy sample format back to caller */
+ *p_bigendian = bigendian;
+ *p_nchannels = nchannels;
+ *p_bytespersamp = bytespersamp;
+ *p_bytelimit = bytelimit;
+ return (fd);
+badheader:
+ /* the header wasn't recognized. We're threadable here so let's not
+ print out the error... */
+ errno = EIO;
+ return (-1);
+}
+
+static void soundfile_xferin(int sfchannels, int nvecs, float **vecs,
+ long itemsread, unsigned char *buf, int nitems, int bytespersamp,
+ int bigendian)
+{
+ int i, j;
+ unsigned char *sp, *sp2;
+ float *fp;
+ int nchannels = (sfchannels < nvecs ? sfchannels : nvecs);
+ int bytesperframe = bytespersamp * sfchannels;
+ for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp)
+ {
+ if (bytespersamp == 2)
+ {
+ if (bigendian)
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16));
+ }
+ else
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ *fp = SCALE * ((sp2[1] << 24) | (sp2[0] << 16));
+ }
+ }
+ else if (bytespersamp == 3)
+ {
+ if (bigendian)
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16)
+ | (sp2[2] << 8));
+ }
+ else
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ *fp = SCALE * ((sp2[2] << 24) | (sp2[1] << 16)
+ | (sp2[0] << 8));
+ }
+ }
+ else if (bytespersamp == 4)
+ {
+ if (bigendian)
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ *(long *)fp = ((sp2[0] << 24) | (sp2[1] << 16)
+ | (sp2[2] << 8) | sp2[3]);
+ }
+ else
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ *(long *)fp = ((sp2[3] << 24) | (sp2[2] << 16)
+ | (sp2[1] << 8) | sp2[0]);
+ }
+ }
+ }
+ /* zero out other outputs */
+ for (i = sfchannels; i < nvecs; i++)
+ for (j = nitems, fp = vecs[i]; j--; )
+ *fp++ = 0;
+
+}
+
+ /* soundfiler_write ...
+
+ usage: write [flags] filename table ...
+ flags:
+ -nframes <frames>
+ -skip <frames>
+ -bytes <bytes per sample>
+ -normalize
+ -nextstep
+ -wave
+ -big
+ -little
+ */
+
+ /* the routine which actually does the work should LATER also be called
+ from garray_write16. */
+
+
+ /* Parse arguments for writing. The "obj" argument is only for flagging
+ errors. For streaming to a file the "normalize", "onset" and "nframes"
+ arguments shouldn't be set but the calling routine flags this. */
+
+static int soundfiler_writeargparse(void *obj, int *p_argc, t_atom **p_argv,
+ t_symbol **p_filesym,
+ int *p_filetype, int *p_bytespersamp, int *p_swap, int *p_bigendian,
+ int *p_normalize, long *p_onset, long *p_nframes)
+{
+ int argc = *p_argc;
+ t_atom *argv = *p_argv;
+ int bytespersamp = 2, bigendian = 0,
+ endianness = -1, swap, filetype = FORMAT_WAVE, normalize = 0;
+ long onset = 0, nframes = 0x7fffffff;
+ t_symbol *filesym;
+ while (argc > 0 && argv->a_type == A_SYMBOL &&
+ *argv->a_w.w_symbol->s_name == '-')
+ {
+ char *flag = argv->a_w.w_symbol->s_name + 1;
+ if (!strcmp(flag, "skip"))
+ {
+ if (argc < 2 || argv[1].a_type != A_FLOAT ||
+ ((onset = argv[1].a_w.w_float) < 0))
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(flag, "nframes"))
+ {
+ if (argc < 2 || argv[1].a_type != A_FLOAT ||
+ ((nframes = argv[1].a_w.w_float) < 0))
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(flag, "bytes"))
+ {
+ if (argc < 2 || argv[1].a_type != A_FLOAT ||
+ ((bytespersamp = argv[1].a_w.w_float) < 2) ||
+ bytespersamp > 4)
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(flag, "normalize"))
+ {
+ normalize = 1;
+ argc -= 1; argv += 1;
+ }
+ else if (!strcmp(flag, "wave"))
+ {
+ filetype = FORMAT_WAVE;
+ argc -= 1; argv += 1;
+ }
+ else if (!strcmp(flag, "nextstep"))
+ {
+ filetype = FORMAT_NEXT;
+ argc -= 1; argv += 1;
+ }
+ else if (!strcmp(flag, "aiff"))
+ {
+ filetype = FORMAT_AIFF;
+ argc -= 1; argv += 1;
+ }
+ else if (!strcmp(flag, "big"))
+ {
+ endianness = 1;
+ argc -= 1; argv += 1;
+ }
+ else if (!strcmp(flag, "little"))
+ {
+ endianness = 1;
+ argc -= 1; argv += 1;
+ }
+ else goto usage;
+ }
+ /* only NextStep handles floating point samples */
+ if (bytespersamp == 4)
+ filetype = FORMAT_NEXT;
+
+ /* for WAVE force little endian; for nextstep use machine native */
+ if (filetype == FORMAT_WAVE)
+ {
+ bigendian = 0;
+ if (endianness == 1)
+ pd_error(obj, "WAVE file forced to little endian");
+ }
+ else if (filetype == FORMAT_AIFF)
+ {
+ bigendian = 1;
+ if (endianness == 0)
+ pd_error(obj, "AIFF file forced to big endian");
+ }
+ else if (endianness == -1)
+ {
+ bigendian = garray_ambigendian();
+ }
+ swap = (bigendian != garray_ambigendian());
+ if (!argc || argv->a_type != A_SYMBOL)
+ goto usage;
+ filesym = argv->a_w.w_symbol;
+ argc--; argv++;
+
+ *p_argc = argc;
+ *p_argv = argv;
+ *p_filesym = filesym;
+ *p_filetype = filetype;
+ *p_bytespersamp = bytespersamp;
+ *p_swap = swap;
+ *p_normalize = normalize;
+ *p_onset = onset;
+ *p_nframes = nframes;
+ *p_bigendian = bigendian;
+ return (0);
+usage:
+ return (-1);
+}
+
+static int create_soundfile(t_canvas *canvas, const char *filename,
+ int filetype, int nframes, int bytespersamp,
+ int bigendian, int nchannels, int swap)
+{
+ char filenamebuf[MAXPDSTRING], buf2[MAXPDSTRING];
+ char headerbuf[WRITEHDRSIZE];
+ t_wave *wavehdr = (t_wave *)headerbuf;
+ t_nextstep *nexthdr = (t_nextstep *)headerbuf;
+ t_aiff *aiffhdr = (t_aiff *)headerbuf;
+ int fd, headersize = 0;
+
+ strncpy(filenamebuf, filename, MAXPDSTRING-10);
+ filenamebuf[MAXPDSTRING-10] = 0;
+
+ if (filetype == FORMAT_NEXT)
+ {
+ if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".snd"))
+ strcat(filenamebuf, ".snd");
+ if (bigendian)
+ strncpy(nexthdr->ns_fileid, ".snd", 4);
+ else strncpy(nexthdr->ns_fileid, "dns.", 4);
+ nexthdr->ns_onset = swap4(sizeof(*nexthdr), swap);
+ nexthdr->ns_length = 0;
+ nexthdr->ns_format = swap4((bytespersamp == 3 ? NS_FORMAT_LINEAR_24 :
+ (bytespersamp == 4 ? NS_FORMAT_FLOAT : NS_FORMAT_LINEAR_16)), swap);;
+ nexthdr->ns_sr = swap4(44100, swap); /* lie */
+ nexthdr->ns_nchans = swap4(nchannels, swap);
+ strcpy(nexthdr->ns_info, "Pd ");
+ swapstring(nexthdr->ns_info, swap);
+ headersize = sizeof(t_nextstep);
+ }
+ else if (filetype == FORMAT_AIFF)
+ {
+ long datasize = nframes * nchannels * bytespersamp;
+ long longtmp;
+ static unsigned char dogdoo[] =
+ {0x40, 0x0e, 0xac, 0x44, 0, 0, 0, 0, 0, 0, 'S', 'S', 'N', 'D'};
+ if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".aif") &&
+ strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff"))
+ strcat(filenamebuf, ".aif");
+ strncpy(aiffhdr->a_fileid, "FORM", 4);
+ aiffhdr->a_chunksize = swap4(datasize + sizeof(*aiffhdr) + 4, swap);
+ strncpy(aiffhdr->a_aiffid, "AIFF", 4);
+ strncpy(aiffhdr->a_fmtid, "COMM", 4);
+ aiffhdr->a_fmtchunksize = swap4(18, swap);
+ aiffhdr->a_nchannels = swap2(nchannels, swap);
+ longtmp = swap4(nframes, swap);
+ memcpy(&aiffhdr->a_nframeshi, &longtmp, 4);
+ aiffhdr->a_bitspersamp = swap2(8 * bytespersamp, swap);
+ memcpy(aiffhdr->a_samprate, dogdoo, sizeof(dogdoo));
+ longtmp = swap4(datasize, swap);
+ memcpy(aiffhdr->a_samprate + sizeof(dogdoo), &longtmp, 4);
+ headersize = AIFFPLUS;
+ }
+ else /* WAVE format */
+ {
+ long datasize = nframes * nchannels * bytespersamp;
+ if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav"))
+ strcat(filenamebuf, ".wav");
+ strncpy(wavehdr->w_fileid, "RIFF", 4);
+ wavehdr->w_chunksize = swap4(datasize + sizeof(*wavehdr) - 8, swap);
+ strncpy(wavehdr->w_waveid, "WAVE", 4);
+ strncpy(wavehdr->w_fmtid, "fmt ", 4);
+ wavehdr->w_fmtchunksize = swap4(16, swap);
+ wavehdr->w_fmttag = swap2(1, swap);
+ wavehdr->w_nchannels = swap2(nchannels, swap);
+ wavehdr->w_samplespersec = swap4(44100, swap);
+ wavehdr->w_navgbytespersec = swap4(44100 * nchannels * bytespersamp, swap);
+ wavehdr->w_nblockalign = swap2(bytespersamp, swap);
+ wavehdr->w_nbitspersample = swap2(8 * bytespersamp, swap);
+ strncpy(wavehdr->w_datachunkid, "data", 4);
+ wavehdr->w_datachunksize = swap4(datasize, swap);
+ headersize = sizeof(t_wave);
+ }
+
+ canvas_makefilename(canvas, filenamebuf, buf2, MAXPDSTRING);
+ sys_bashfilename(buf2, buf2);
+ if ((fd = open(buf2, BINCREATE, 0666)) < 0)
+ return (-1);
+
+ if (write(fd, headerbuf, headersize) < headersize)
+ {
+ close (fd);
+ return (-1);
+ }
+ return (fd);
+}
+
+static void soundfile_finishwrite(void *obj, char *filename, int fd,
+ int filetype, long nframes, long itemswritten, int bytesperframe, int swap)
+{
+ if (itemswritten < nframes)
+ {
+ if (nframes < 0x7fffffff)
+ pd_error(obj, "soundfiler_write: %d out of %d bytes written",
+ itemswritten, nframes);
+ /* try to fix size fields in header */
+ if (filetype == FORMAT_WAVE)
+ {
+ long datasize = itemswritten * bytesperframe, mofo;
+
+ if (lseek(fd,
+ ((char *)(&((t_wave *)0)->w_chunksize)) - (char *)0,
+ SEEK_SET) == 0)
+ goto baddonewrite;
+ mofo = swap4(datasize + sizeof(t_wave) - 8, swap);
+ if (write(fd, (char *)(&mofo), 4) < 4)
+ goto baddonewrite;
+ if (lseek(fd,
+ ((char *)(&((t_wave *)0)->w_datachunksize)) - (char *)0,
+ SEEK_SET) == 0)
+ goto baddonewrite;
+ mofo = swap4(datasize, swap);
+ if (write(fd, (char *)(&mofo), 4) < 4)
+ goto baddonewrite;
+ }
+ if (filetype == FORMAT_AIFF)
+ {
+ long mofo;
+ if (lseek(fd,
+ ((char *)(&((t_aiff *)0)->a_nframeshi)) - (char *)0,
+ SEEK_SET) == 0)
+ goto baddonewrite;
+ mofo = swap4(nframes, swap);
+ if (write(fd, (char *)(&mofo), 4) < 4)
+ goto baddonewrite;
+ }
+ }
+ return;
+baddonewrite:
+ post("%s: %s", filename, strerror(errno));
+}
+
+static void soundfile_xferout(int nchannels, float **vecs,
+ unsigned char *buf, int nitems, long onset, int bytespersamp,
+ int bigendian, float normalfactor)
+{
+ int i, j;
+ unsigned char *sp, *sp2;
+ float *fp;
+ int bytesperframe = bytespersamp * nchannels;
+ long xx;
+ for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp)
+ {
+ if (bytespersamp == 2)
+ {
+ float ff = normalfactor * 32768.;
+ if (bigendian)
+ {
+ for (j = 0, sp2 = sp, fp = vecs[i] + onset;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ {
+ int xx = 32768. + (*fp * ff);
+ xx -= 32768;
+ if (xx < -32767)
+ xx = -32767;
+ if (xx > 32767)
+ xx = 32767;
+ sp2[0] = (xx >> 8);
+ sp2[1] = xx;
+ }
+ }
+ else
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + onset;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ {
+ int xx = 32768. + (*fp * ff);
+ xx -= 32768;
+ if (xx < -32767)
+ xx = -32767;
+ if (xx > 32767)
+ xx = 32767;
+ sp2[1] = (xx >> 8);
+ sp2[0] = xx;
+ }
+ }
+ }
+ else if (bytespersamp == 3)
+ {
+ float ff = normalfactor * 8388608.;
+ if (bigendian)
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + onset;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ {
+ int xx = 8388608. + (*fp * ff);
+ xx -= 8388608;
+ if (xx < -8388607)
+ xx = -8388607;
+ if (xx > 8388607)
+ xx = 8388607;
+ sp2[0] = (xx >> 16);
+ sp2[1] = (xx >> 8);
+ sp2[2] = xx;
+ }
+ }
+ else
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + onset;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ {
+ int xx = 8388608. + (*fp * ff);
+ xx -= 8388608;
+ if (xx < -8388607)
+ xx = -8388607;
+ if (xx > 8388607)
+ xx = 8388607;
+ sp2[2] = (xx >> 16);
+ sp2[1] = (xx >> 8);
+ sp2[0] = xx;
+ }
+ }
+ }
+ else if (bytespersamp == 4)
+ {
+ if (bigendian)
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + onset;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ {
+ float f2 = *fp * normalfactor;
+ xx = *(long *)&f2;
+ sp2[0] = (xx >> 24); sp2[1] = (xx >> 24);
+ sp2[2] = (xx >> 24); sp2[3] = xx;
+ }
+ }
+ else
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + onset;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ {
+ float f2 = *fp * normalfactor;
+ xx = *(long *)&f2;
+ sp2[3] = (xx >> 24); sp2[2] = (xx >> 24);
+ sp2[1] = (xx >> 24); sp2[0] = xx;
+ }
+ }
+ }
+ }
+}
+
+
+/* ------- soundfiler - reads and writes soundfiles to/from "garrays" ---- */
+#define DEFMAXSIZE 4000000 /* default maximum 16 MB per channel */
+#define SAMPBUFSIZE 1024
+
+
+static t_class *soundfiler_class;
+
+typedef struct _soundfiler
+{
+ t_object x_obj;
+ t_canvas *x_canvas;
+} t_soundfiler;
+
+static t_soundfiler *soundfiler_new(void)
+{
+ t_soundfiler *x = (t_soundfiler *)pd_new(soundfiler_class);
+ x->x_canvas = canvas_getcurrent();
+ outlet_new(&x->x_obj, &s_float);
+ return (x);
+}
+
+ /* soundfiler_read ...
+
+ usage: read [flags] filename table ...
+ flags:
+ -skip <frames> ... frames to skip in file
+ -nframes <frames>
+ -onset <frames> ... onset in table to read into (NOT DONE YET)
+ -raw <headersize channels bytes endian>
+ -resize
+ -maxsize <max-size>
+ */
+
+static void soundfiler_read(t_soundfiler *x, t_symbol *s,
+ int argc, t_atom *argv)
+{
+ int headersize = -1, channels = 0, bytespersamp = 0, bigendian = 0,
+ resize = 0, i, j;
+ long skipframes = 0, nframes = 0, finalsize = 0, itemsleft,
+ maxsize = DEFMAXSIZE, itemsread = 0, bytelimit = 0x7fffffff;
+ int fd = -1;
+ char endianness, *filename;
+ t_garray *garrays[MAXSFCHANS];
+ t_float *vecs[MAXSFCHANS];
+ char sampbuf[SAMPBUFSIZE];
+ int bufframes, nitems;
+ FILE *fp;
+ while (argc > 0 && argv->a_type == A_SYMBOL &&
+ *argv->a_w.w_symbol->s_name == '-')
+ {
+ char *flag = argv->a_w.w_symbol->s_name + 1;
+ if (!strcmp(flag, "skip"))
+ {
+ if (argc < 2 || argv[1].a_type != A_FLOAT ||
+ ((skipframes = argv[1].a_w.w_float) < 0))
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(flag, "nframes"))
+ {
+ if (argc < 2 || argv[1].a_type != A_FLOAT ||
+ ((nframes = argv[1].a_w.w_float) < 0))
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(flag, "raw"))
+ {
+ if (argc < 5 ||
+ argv[1].a_type != A_FLOAT ||
+ ((headersize = argv[1].a_w.w_float) < 0) ||
+ argv[2].a_type != A_FLOAT ||
+ ((channels = argv[2].a_w.w_float) < 1) ||
+ (channels > MAXSFCHANS) ||
+ argv[3].a_type != A_FLOAT ||
+ ((bytespersamp = argv[3].a_w.w_float) < 2) ||
+ (bytespersamp > 4) ||
+ argv[4].a_type != A_SYMBOL ||
+ ((endianness = argv[4].a_w.w_symbol->s_name[0]) != 'b'
+ && endianness != 'l' && endianness != 'n'))
+ goto usage;
+ if (endianness == 'b')
+ bigendian = 1;
+ else if (endianness == 'l')
+ bigendian = 0;
+ else
+ bigendian = garray_ambigendian();
+ argc -= 5; argv += 5;
+ }
+ else if (!strcmp(flag, "resize"))
+ {
+ resize = 1;
+ argc -= 1; argv += 1;
+ }
+ else if (!strcmp(flag, "maxsize"))
+ {
+ if (argc < 2 || argv[1].a_type != A_FLOAT ||
+ ((maxsize = argv[1].a_w.w_float) < 0))
+ goto usage;
+ resize = 1; /* maxsize implies resize. */
+ argc -= 2; argv += 2;
+ }
+ else goto usage;
+ }
+ if (argc < 2 || argc > MAXSFCHANS + 1 || argv[0].a_type != A_SYMBOL)
+ goto usage;
+ filename = argv[0].a_w.w_symbol->s_name;
+ argc--; argv++;
+
+ for (i = 0; i < argc; i++)
+ {
+ int vecsize;
+ if (argv[i].a_type != A_SYMBOL)
+ goto usage;
+ if (!(garrays[i] =
+ (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class)))
+ {
+ pd_error(x, "%s: no such table", argv[i].a_w.w_symbol->s_name);
+ goto done;
+ }
+ else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i]))
+ error("%s: bad template for tabwrite",
+ argv[i].a_w.w_symbol->s_name);
+ if (finalsize && finalsize != vecsize && !resize)
+ {
+ post("soundfiler_read: arrays have different lengths; resizing...");
+ resize = 1;
+ }
+ finalsize = vecsize;
+ }
+ fd = open_soundfile(canvas_getdir(x->x_canvas)->s_name, filename,
+ headersize, &bytespersamp, &bigendian, &channels, &bytelimit,
+ skipframes);
+
+ if (fd < 0)
+ {
+ pd_error(x, "soundfiler_read: %s: %s", filename, (errno == EIO ?
+ "unknown or bad header format" : strerror(errno)));
+ goto done;
+ }
+
+ if (resize)
+ {
+ /* figure out what to resize to */
+ long poswas, eofis, framesinfile;
+
+ poswas = lseek(fd, 0, SEEK_CUR);
+ eofis = lseek(fd, 0, SEEK_END);
+ if (poswas < 0 || eofis < 0)
+ {
+ pd_error(x, "lseek failed");
+ goto done;
+ }
+ lseek(fd, poswas, SEEK_SET);
+ framesinfile = (eofis - poswas) / (channels * bytespersamp);
+ if (framesinfile > maxsize)
+ {
+ pd_error(x, "soundfiler_read: truncated to %d elements", maxsize);
+ framesinfile = maxsize;
+ }
+ if (framesinfile > bytelimit / bytespersamp)
+ framesinfile = bytelimit / bytespersamp;
+ finalsize = framesinfile;
+ for (i = 0; i < argc; i++)
+ {
+ int vecsize;
+
+ garray_resize(garrays[i], finalsize);
+ /* for sanity's sake let's clear the save-in-patch flag here */
+ garray_setsaveit(garrays[i], 0);
+ garray_getfloatarray(garrays[i], &vecsize, &vecs[i]);
+ /* if the resize failed, garray_resize reported the error */
+ if (vecsize != framesinfile)
+ {
+ pd_error(x, "resize failed");
+ goto done;
+ }
+ }
+ }
+ if (!finalsize) finalsize = 0x7fffffff;
+ if (finalsize > bytelimit / bytespersamp)
+ finalsize = bytelimit / bytespersamp;
+ fp = fdopen(fd, "rb");
+ bufframes = SAMPBUFSIZE / (channels * bytespersamp);
+
+ for (itemsread = 0; itemsread < finalsize; )
+ {
+ int thisread = finalsize - itemsread;
+ thisread = (thisread > bufframes ? bufframes : thisread);
+ nitems = fread(sampbuf, channels * bytespersamp, thisread, fp);
+ if (nitems <= 0) break;
+ soundfile_xferin(channels, argc, vecs, itemsread,
+ (unsigned char *)sampbuf, nitems, bytespersamp, bigendian);
+ itemsread += nitems;
+ }
+ /* zero out remaining elements of vectors */
+
+ for (i = 0; i < argc; i++)
+ {
+ int nzero, vecsize;
+ garray_getfloatarray(garrays[i], &vecsize, &vecs[i]);
+ for (j = itemsread; j < vecsize; j++)
+ vecs[i][j] = 0;
+ }
+ /* zero out vectors in excess of number of channels */
+ for (i = channels; i < argc; i++)
+ {
+ int vecsize;
+ float *foo;
+ garray_getfloatarray(garrays[i], &vecsize, &foo);
+ for (j = 0; j < vecsize; j++)
+ foo[j] = 0;
+ }
+ /* do all graphics updates */
+ for (i = 0; i < argc; i++)
+ garray_redraw(garrays[i]);
+ fclose(fp);
+ fd = -1;
+ goto done;
+usage:
+ pd_error(x, "usage: read [flags] filename tablename...");
+ post("flags: -skip <n> -nframes <n> -resize -maxsize <n> ...");
+ post("-raw <headerbytes> <channels> <bytespersamp> <endian (b, l, or n)>.");
+done:
+ if (fd >= 0)
+ close (fd);
+ outlet_float(x->x_obj.ob_outlet, (float)itemsread);
+}
+
+ /* this is broken out from soundfiler_write below so garray_write can
+ call it too... not done yet though. */
+
+long soundfiler_dowrite(void *obj, t_canvas *canvas,
+ int argc, t_atom *argv)
+{
+ int headersize, bytespersamp, bigendian,
+ endianness, swap, filetype, normalize, i, j, nchannels;
+ long onset, nframes, itemsleft,
+ maxsize = DEFMAXSIZE, itemswritten = 0;
+ t_garray *garrays[MAXSFCHANS];
+ t_float *vecs[MAXSFCHANS];
+ char sampbuf[SAMPBUFSIZE];
+ int bufframes, nitems;
+ int fd = -1;
+ float normfactor, biggest = 0;
+ t_symbol *filesym;
+
+ if (soundfiler_writeargparse(obj, &argc, &argv, &filesym, &filetype,
+ &bytespersamp, &swap, &bigendian, &normalize, &onset, &nframes))
+ goto usage;
+ nchannels = argc;
+ if (nchannels < 1 || nchannels > MAXSFCHANS)
+ goto usage;
+
+ for (i = 0; i < nchannels; i++)
+ {
+ int vecsize;
+ if (argv[i].a_type != A_SYMBOL)
+ goto usage;
+ if (!(garrays[i] =
+ (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class)))
+ {
+ pd_error(obj, "%s: no such table", argv[i].a_w.w_symbol->s_name);
+ goto fail;
+ }
+ else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i]))
+ error("%s: bad template for tabwrite",
+ argv[i].a_w.w_symbol->s_name);
+ if (nframes > vecsize - onset)
+ nframes = vecsize - onset;
+
+ for (j = 0; j < vecsize; j++)
+ {
+ if (vecs[i][j] > biggest)
+ biggest = vecs[i][j];
+ else if (-vecs[i][j] > biggest)
+ biggest = -vecs[i][j];
+ }
+ }
+ if (nframes <= 0)
+ {
+ pd_error(obj, "soundfiler_write: no samples at onset %ld", onset);
+ goto fail;
+ }
+
+ if ((fd = create_soundfile(canvas, filesym->s_name, filetype,
+ nframes, bytespersamp, bigendian, nchannels,
+ swap)) < 0)
+ {
+ post("%s: %s\n", filesym->s_name, strerror(errno));
+ goto fail;
+ }
+ if (!normalize)
+ {
+ if ((bytespersamp != 4) && (biggest > 1))
+ {
+ post("%s: normalizing max amplitude %f to 1", filesym->s_name, biggest);
+ normalize = 1;
+ }
+ else post("%s: biggest amplitude = %f", filesym->s_name, biggest);
+ }
+ if (normalize)
+ normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1);
+ else normfactor = 1;
+
+ bufframes = SAMPBUFSIZE / (nchannels * bytespersamp);
+
+ for (itemswritten = 0; itemswritten < nframes; )
+ {
+ int thiswrite = nframes - itemswritten, nitems, nbytes;
+ thiswrite = (thiswrite > bufframes ? bufframes : thiswrite);
+ soundfile_xferout(argc, vecs, (unsigned char *)sampbuf, thiswrite,
+ onset, bytespersamp, bigendian, normfactor);
+ nbytes = write(fd, sampbuf, nchannels * bytespersamp * thiswrite);
+ if (nbytes < nchannels * bytespersamp * thiswrite)
+ {
+ post("%s: %s", filesym->s_name, strerror(errno));
+ if (nbytes > 0)
+ itemswritten += nbytes / (nchannels * bytespersamp);
+ break;
+ }
+ itemswritten += thiswrite;
+ onset += thiswrite;
+ }
+ if (fd >= 0)
+ {
+ soundfile_finishwrite(obj, filesym->s_name, fd,
+ filetype, nframes, itemswritten, nchannels * bytespersamp, swap);
+ close (fd);
+ }
+ return ((float)itemswritten);
+usage:
+ pd_error(obj, "usage: write [flags] filename tablename...");
+ post("flags: -skip <n> -nframes <n> -bytes <n> -wave -aiff -nextstep ...");
+ post("-big -little -normalize");
+ post("(defaults to a 16-bit wave file).");
+fail:
+ if (fd >= 0)
+ close (fd);
+ return (0);
+}
+
+static void soundfiler_write(t_soundfiler *x, t_symbol *s,
+ int argc, t_atom *argv)
+{
+ long bozo = soundfiler_dowrite(x, x->x_canvas,
+ argc, argv);
+ outlet_float(x->x_obj.ob_outlet, (float)bozo);
+}
+
+static void soundfiler_setup(void)
+{
+ soundfiler_class = class_new(gensym("soundfiler"), (t_newmethod)soundfiler_new,
+ 0, sizeof(t_soundfiler), 0, 0);
+ class_addmethod(soundfiler_class, (t_method)soundfiler_read, gensym("read"),
+ A_GIMME, 0);
+ class_addmethod(soundfiler_class, (t_method)soundfiler_write,
+ gensym("write"), A_GIMME, 0);
+}
+
+
+/************************* readsf object ******************************/
+
+/* READSF uses the Posix threads package; for the moment we're Linux
+only although this should be portable to the other platforms.
+
+Each instance of readsf~ owns a "child" thread for doing the UNIX (NT?) file
+reading. The parent thread signals the child each time:
+ (1) a file wants opening or closing;
+ (2) we've eaten another 1/16 of the shared buffer (so that the
+ child thread should check if it's time to read some more.)
+The child signals the parent whenever a read has completed. Signalling
+is done by setting "conditions" and putting data in mutex-controlled common
+areas.
+*/
+
+#define MAXBYTESPERSAMPLE 4
+#define MAXVECSIZE 128
+
+#define READSIZE 65536
+#define WRITESIZE 65536
+#define DEFBUFPERCHAN 262144
+#define MINBUFSIZE (4 * READSIZE)
+#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */
+
+#define REQUEST_NOTHING 0
+#define REQUEST_OPEN 1
+#define REQUEST_CLOSE 2
+#define REQUEST_QUIT 3
+#define REQUEST_BUSY 4
+
+#define STATE_IDLE 0
+#define STATE_STARTUP 1
+#define STATE_STREAM 2
+
+static t_class *readsf_class;
+
+typedef struct _readsf
+{
+ t_object x_obj;
+ t_canvas *x_canvas;
+ t_clock *x_clock;
+ char *x_buf; /* soundfile buffer */
+ int x_bufsize; /* buffer size in bytes */
+ int x_noutlets; /* number of audio outlets */
+ t_sample *(x_outvec[MAXSFCHANS]); /* audio vectors */
+ int x_vecsize; /* vector size for transfers */
+ t_outlet *x_bangout; /* bang-on-done outlet */
+ int x_state; /* opened, running, or idle */
+ /* parameters to communicate with subthread */
+ int x_requestcode; /* pending request from parent to I/O thread */
+ char *x_filename; /* file to open (string is permanently allocated) */
+ int x_fileerror; /* slot for "errno" return */
+ int x_skipheaderbytes; /* size of header we'll skip */
+ int x_bytespersample; /* bytes per sample (2 or 3) */
+ int x_bigendian; /* true if file is big-endian */
+ int x_sfchannels; /* number of channels in soundfile */
+ long x_onsetframes; /* number of sample frames to skip */
+ long x_bytelimit; /* max number of data bytes to read */
+ int x_fd; /* filedesc */
+ int x_fifosize; /* buffer size appropriately rounded down */
+ int x_fifohead; /* index of next byte to get from file */
+ int x_fifotail; /* index of next byte the ugen will read */
+ int x_eof; /* true if fifohead has stopped changing */
+ int x_sigcountdown; /* counter for signalling child for more data */
+ int x_sigperiod; /* number of ticks per signal */
+ int x_filetype; /* writesf~ only; type of file to create */
+ int x_itemswritten; /* writesf~ only; items writen */
+ int x_swap; /* writesf~ only; true if byte swapping */
+ float x_f; /* writesf~ only; scalar for signal inlet */
+ pthread_mutex_t x_mutex;
+ pthread_cond_t x_requestcondition;
+ pthread_cond_t x_answercondition;
+ pthread_t x_childthread;
+} t_readsf;
+
+
+/************** the child thread which performs file I/O ***********/
+
+#if 0
+static void pute(char *s) /* debug routine */
+{
+ write(2, s, strlen(s));
+}
+#else
+#define pute(x)
+#endif
+
+#if 1
+#define sfread_cond_wait pthread_cond_wait
+#define sfread_cond_signal pthread_cond_signal
+#else
+#include <sys/time.h> /* debugging version... */
+#include <sys/types.h>
+static void readsf_fakewait(pthread_mutex_t *b)
+{
+ struct timeval timout;
+ timout.tv_sec = 0;
+ timout.tv_usec = 1000000;
+ pthread_mutex_unlock(b);
+ select(0, 0, 0, 0, &timout);
+ pthread_mutex_lock(b);
+}
+
+void readsf_banana( void)
+{
+ struct timeval timout;
+ timout.tv_sec = 0;
+ timout.tv_usec = 200000;
+ pute("banana1\n");
+ select(0, 0, 0, 0, &timout);
+ pute("banana2\n");
+}
+
+
+#define sfread_cond_wait(a,b) readsf_fakewait(b)
+#define sfread_cond_signal(a)
+#endif
+
+static void *readsf_child_main(void *zz)
+{
+ t_readsf *x = zz;
+ pute("1\n");
+ pthread_mutex_lock(&x->x_mutex);
+ while (1)
+ {
+ int fd, fifohead;
+ char *buf;
+ pute("0\n");
+ if (x->x_requestcode == REQUEST_NOTHING)
+ {
+ pute("wait 2\n");
+ sfread_cond_signal(&x->x_answercondition);
+ sfread_cond_wait(&x->x_requestcondition, &x->x_mutex);
+ pute("3\n");
+ }
+ else if (x->x_requestcode == REQUEST_OPEN)
+ {
+ char boo[80];
+ int sysrtn, wantbytes;
+
+ /* copy file stuff out of the data structure so we can
+ relinquish the mutex while we're in open_soundfile(). */
+ long onsetframes = x->x_onsetframes;
+ long bytelimit = 0x7fffffff;
+ int skipheaderbytes = x->x_skipheaderbytes;
+ int bytespersample = x->x_bytespersample;
+ int sfchannels = x->x_sfchannels;
+ int bigendian = x->x_bigendian;
+ char *filename = x->x_filename;
+ char *dirname = canvas_getdir(x->x_canvas)->s_name;
+ /* alter the request code so that an ensuing "open" will get
+ noticed. */
+ pute("4\n");
+ x->x_requestcode = REQUEST_BUSY;
+ x->x_fileerror = 0;
+
+ /* if there's already a file open, close it */
+ if (x->x_fd >= 0)
+ {
+ fd = x->x_fd;
+ pthread_mutex_unlock(&x->x_mutex);
+ close (fd);
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_fd = -1;
+ if (x->x_requestcode != REQUEST_BUSY)
+ goto lost;
+ }
+ /* open the soundfile with the mutex unlocked */
+ pthread_mutex_unlock(&x->x_mutex);
+ fd = open_soundfile(dirname, filename,
+ skipheaderbytes, &bytespersample, &bigendian,
+ &sfchannels, &bytelimit, onsetframes);
+ pthread_mutex_lock(&x->x_mutex);
+
+ pute("5\n");
+ /* copy back into the instance structure. */
+ x->x_bytespersample = bytespersample;
+ x->x_sfchannels = sfchannels;
+ x->x_bigendian = bigendian;
+ x->x_fd = fd;
+ x->x_bytelimit = bytelimit;
+ if (fd < 0)
+ {
+ x->x_fileerror = errno;
+ x->x_eof = 1;
+ pute("open failed\n");
+ pute(filename);
+ pute(dirname);
+ goto lost;
+ }
+ /* check if another request has been made; if so, field it */
+ if (x->x_requestcode != REQUEST_BUSY)
+ goto lost;
+ pute("6\n");
+ x->x_fifohead = 0;
+ /* set fifosize from bufsize. fifosize must be a
+ multiple of the number of bytes eaten for each DSP
+ tick. We pessimistically assume MAXVECSIZE samples
+ per tick since that could change. There could be a
+ problem here if the vector size increases while a
+ soundfile is being played... */
+ x->x_fifosize = x->x_bufsize - (x->x_bufsize %
+ (x->x_bytespersample * x->x_sfchannels * MAXVECSIZE));
+ /* arrange for the "request" condition to be signalled 16
+ times per buffer */
+ sprintf(boo, "fifosize %d\n",
+ x->x_fifosize);
+ pute(boo);
+ x->x_sigcountdown = x->x_sigperiod =
+ (x->x_fifosize /
+ (16 * x->x_bytespersample * x->x_sfchannels *
+ x->x_vecsize));
+ /* in a loop, wait for the fifo to get hungry and feed it */
+
+ while (x->x_requestcode == REQUEST_BUSY)
+ {
+ int fifosize = x->x_fifosize;
+ pute("77\n");
+ if (x->x_eof)
+ break;
+ if (x->x_fifohead >= x->x_fifotail)
+ {
+ /* if the head is >= the tail, we can immediately read
+ to the end of the fifo. Unless, that is, we would
+ read all the way to the end of the buffer and the
+ "tail" is zero; this would fill the buffer completely
+ which isn't allowed because you can't tell a completely
+ full buffer from an empty one. */
+ if (x->x_fifotail || (fifosize - x->x_fifohead > READSIZE))
+ {
+ wantbytes = fifosize - x->x_fifohead;
+ if (wantbytes > READSIZE)
+ wantbytes = READSIZE;
+ if (wantbytes > x->x_bytelimit)
+ wantbytes = x->x_bytelimit;
+ sprintf(boo, "head %d, tail %d, size %d\n",
+ x->x_fifohead, x->x_fifotail, wantbytes);
+ pute(boo);
+ }
+ else
+ {
+ pute("wait 7a ...\n");
+ sfread_cond_signal(&x->x_answercondition);
+ pute("signalled\n");
+ sfread_cond_wait(&x->x_requestcondition,
+ &x->x_mutex);
+ pute("7a done\n");
+ continue;
+ }
+ }
+ else
+ {
+ /* otherwise check if there are at least READSIZE
+ bytes to read. If not, wait and loop back. */
+ wantbytes = x->x_fifotail - x->x_fifohead - 1;
+ if (wantbytes < READSIZE)
+ {
+ pute("wait 7...\n");
+ sfread_cond_signal(&x->x_answercondition);
+ sfread_cond_wait(&x->x_requestcondition,
+ &x->x_mutex);
+ pute("7 done\n");
+ continue;
+ }
+ else wantbytes = READSIZE;
+ }
+ pute("8\n");
+ fd = x->x_fd;
+ buf = x->x_buf;
+ fifohead = x->x_fifohead;
+ pthread_mutex_unlock(&x->x_mutex);
+ sysrtn = read(fd, buf + fifohead, wantbytes);
+ pthread_mutex_lock(&x->x_mutex);
+ if (x->x_requestcode != REQUEST_BUSY)
+ break;
+ if (sysrtn < 0)
+ {
+ pute("fileerror\n");
+ x->x_fileerror = errno;
+ break;
+ }
+ else if (sysrtn == 0)
+ {
+ x->x_eof = 1;
+ break;
+ }
+ else
+ {
+ x->x_fifohead += sysrtn;
+ x->x_bytelimit -= sysrtn;
+ if (x->x_bytelimit <= 0)
+ {
+ x->x_eof = 1;
+ break;
+ }
+ if (x->x_fifohead == fifosize)
+ x->x_fifohead = 0;
+ }
+ sprintf(boo, "after: head %d, tail %d\n",
+ x->x_fifohead, x->x_fifotail);
+ pute(boo);
+ /* signal parent in case it's waiting for data */
+ sfread_cond_signal(&x->x_answercondition);
+ }
+ lost:
+
+ if (x->x_requestcode == REQUEST_BUSY)
+ x->x_requestcode = REQUEST_NOTHING;
+ /* fell out of read loop: close file if necessary,
+ set EOF and signal once more */
+ if (x->x_fd >= 0)
+ {
+ fd = x->x_fd;
+ pthread_mutex_unlock(&x->x_mutex);
+ close (fd);
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_fd = -1;
+ }
+ sfread_cond_signal(&x->x_answercondition);
+
+ }
+ else if (x->x_requestcode == REQUEST_CLOSE)
+ {
+ if (x->x_fd >= 0)
+ {
+ fd = x->x_fd;
+ pthread_mutex_unlock(&x->x_mutex);
+ close (fd);
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_fd = -1;
+ }
+ if (x->x_requestcode == REQUEST_CLOSE)
+ x->x_requestcode = REQUEST_NOTHING;
+ sfread_cond_signal(&x->x_answercondition);
+ }
+ else if (x->x_requestcode == REQUEST_QUIT)
+ {
+ if (x->x_fd >= 0)
+ {
+ fd = x->x_fd;
+ pthread_mutex_unlock(&x->x_mutex);
+ close (fd);
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_fd = -1;
+ }
+ x->x_requestcode = REQUEST_NOTHING;
+ sfread_cond_signal(&x->x_answercondition);
+ break;
+ }
+ else
+ {
+ pute("13\n");
+ }
+ }
+ pute("thread exit\n");
+ pthread_mutex_unlock(&x->x_mutex);
+ return (0);
+}
+
+/******** the object proper runs in the calling (parent) thread ****/
+
+static void readsf_tick(t_readsf *x);
+
+static void *readsf_new(t_floatarg fnchannels, t_floatarg fbufsize)
+{
+ t_readsf *x;
+ int nchannels = fnchannels, bufsize = fbufsize, i;
+ char *buf;
+
+ if (nchannels < 1)
+ nchannels = 1;
+ else if (nchannels > MAXSFCHANS)
+ nchannels = MAXSFCHANS;
+ if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels;
+ else if (bufsize < MINBUFSIZE)
+ bufsize = MINBUFSIZE;
+ else if (bufsize > MAXBUFSIZE)
+ bufsize = MAXBUFSIZE;
+ buf = getbytes(bufsize);
+ if (!buf) return (0);
+
+ x = (t_readsf *)pd_new(readsf_class);
+
+ for (i = 0; i < nchannels; i++)
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_noutlets = nchannels;
+ x->x_bangout = outlet_new(&x->x_obj, &s_bang);
+ pthread_mutex_init(&x->x_mutex, 0);
+ pthread_cond_init(&x->x_requestcondition, 0);
+ pthread_cond_init(&x->x_answercondition, 0);
+ x->x_vecsize = MAXVECSIZE;
+ x->x_state = STATE_IDLE;
+ x->x_clock = clock_new(x, (t_method)readsf_tick);
+ x->x_canvas = canvas_getcurrent();
+ x->x_bytespersample = 2;
+ x->x_sfchannels = 1;
+ x->x_fd = -1;
+ x->x_buf = buf;
+ x->x_bufsize = bufsize;
+ x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0;
+ pthread_create(&x->x_childthread, 0, readsf_child_main, x);
+ return (x);
+}
+
+static void readsf_tick(t_readsf *x)
+{
+ outlet_bang(x->x_bangout);
+}
+
+static t_int *readsf_perform(t_int *w)
+{
+ t_readsf *x = (t_readsf *)(w[1]);
+ int vecsize = x->x_vecsize, noutlets = x->x_noutlets, i, j,
+ bytespersample = x->x_bytespersample,
+ bigendian = x->x_bigendian;
+ float *fp;
+ if (x->x_state == STATE_STREAM)
+ {
+ int wantbytes, nchannels, sfchannels = x->x_sfchannels;
+ pthread_mutex_lock(&x->x_mutex);
+ wantbytes = sfchannels * vecsize * bytespersample;
+ while (
+ !x->x_eof && x->x_fifohead >= x->x_fifotail &&
+ x->x_fifohead < x->x_fifotail + wantbytes-1)
+ {
+ pute("wait...\n");
+ sfread_cond_signal(&x->x_requestcondition);
+ sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
+ pute("done\n");
+ }
+ if (x->x_eof && x->x_fifohead >= x->x_fifotail &&
+ x->x_fifohead < x->x_fifotail + wantbytes-1)
+ {
+ if (x->x_fileerror)
+ {
+ pd_error(x, "dsp: %s: %s", x->x_filename,
+ (x->x_fileerror == EIO ?
+ "unknown or bad header format" :
+ strerror(x->x_fileerror)));
+ }
+ clock_delay(x->x_clock, 0);
+ x->x_state = STATE_IDLE;
+ sfread_cond_signal(&x->x_requestcondition);
+ pthread_mutex_unlock(&x->x_mutex);
+ goto idle;
+ }
+
+ soundfile_xferin(sfchannels, noutlets, x->x_outvec, 0,
+ (unsigned char *)(x->x_buf + x->x_fifotail), vecsize,
+ bytespersample, bigendian);
+
+ x->x_fifotail += wantbytes;
+ if (x->x_fifotail >= x->x_fifosize)
+ x->x_fifotail = 0;
+ if ((--x->x_sigcountdown) <= 0)
+ {
+ sfread_cond_signal(&x->x_requestcondition);
+ x->x_sigcountdown = x->x_sigperiod;
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+ }
+ else
+ {
+ idle:
+ for (i = 0; i < noutlets; i++)
+ for (j = vecsize, fp = x->x_outvec[i]; j--; )
+ *fp++ = 0;
+ }
+ return (w+2);
+}
+
+static void readsf_start(t_readsf *x)
+{
+ /* start making output. If we're in the "startup" state change
+ to the "running" state. */
+ if (x->x_state == STATE_STARTUP)
+ x->x_state = STATE_STREAM;
+ else pd_error(x, "readsf: start requested with no prior 'open'");
+}
+
+static void readsf_stop(t_readsf *x)
+{
+ /* LATER rethink whether you need the mutex just to set a variable? */
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_state = STATE_IDLE;
+ x->x_requestcode = REQUEST_CLOSE;
+ sfread_cond_signal(&x->x_requestcondition);
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+static void readsf_float(t_readsf *x, t_floatarg f)
+{
+ if (f != 0)
+ readsf_start(x);
+ else readsf_stop(x);
+}
+
+ /* open method. Called as:
+ open filename [skipframes headersize channels bytespersamp endianness]
+ (if headersize is zero, header is taken to be automatically
+ detected; thus, use the special "-1" to mean a truly headerless file.)
+ */
+
+static void readsf_open(t_readsf *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *filesym = atom_getsymbolarg(0, argc, argv);
+ t_float onsetframes = atom_getfloatarg(1, argc, argv);
+ t_float headerbytes = atom_getfloatarg(2, argc, argv);
+ t_float channels = atom_getfloatarg(3, argc, argv);
+ t_float bytespersamp = atom_getfloatarg(4, argc, argv);
+ t_symbol *endian = atom_getsymbolarg(5, argc, argv);
+ if (!*filesym->s_name)
+ return;
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_requestcode = REQUEST_OPEN;
+ x->x_filename = filesym->s_name;
+ x->x_fifotail = 0;
+ x->x_fifohead = 0;
+ if (*endian->s_name == 'b')
+ x->x_bigendian = 1;
+ else if (*endian->s_name == 'l')
+ x->x_bigendian = 0;
+ else if (*endian->s_name)
+ pd_error(x, "endianness neither 'b' nor 'l'");
+ else x->x_bigendian = garray_ambigendian();
+ x->x_onsetframes = (onsetframes > 0 ? onsetframes : 0);
+ x->x_skipheaderbytes = (headerbytes > 0 ? headerbytes :
+ (headerbytes == 0 ? -1 : 0));
+ x->x_sfchannels = (channels >= 1 ? channels : 1);
+ x->x_bytespersample = (bytespersamp > 2 ? bytespersamp : 2);
+ x->x_eof = 0;
+ x->x_fileerror = 0;
+ x->x_state = STATE_STARTUP;
+ sfread_cond_signal(&x->x_requestcondition);
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+static void readsf_dsp(t_readsf *x, t_signal **sp)
+{
+ int i, noutlets = x->x_noutlets;
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_vecsize = sp[0]->s_n;
+
+ x->x_sigperiod = (x->x_fifosize /
+ (x->x_bytespersample * x->x_sfchannels * x->x_vecsize));
+ for (i = 0; i < noutlets; i++)
+ x->x_outvec[i] = sp[i]->s_vec;
+ pthread_mutex_unlock(&x->x_mutex);
+ dsp_add(readsf_perform, 1, x);
+}
+
+static void readsf_print(t_readsf *x)
+{
+ post("state %d", x->x_state);
+ post("fifo head %d", x->x_fifohead);
+ post("fifo tail %d", x->x_fifotail);
+ post("fifo size %d", x->x_fifosize);
+ post("fd %d", x->x_fd);
+ post("eof %d", x->x_eof);
+}
+
+static void readsf_free(t_readsf *x)
+{
+ /* request QUIT and wait for acknowledge */
+ void *threadrtn;
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_requestcode = REQUEST_QUIT;
+ post("stopping readsf thread...");
+ sfread_cond_signal(&x->x_requestcondition);
+ while (x->x_requestcode != REQUEST_NOTHING)
+ {
+ post("signalling...");
+ sfread_cond_signal(&x->x_requestcondition);
+ sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+ if (pthread_join(x->x_childthread, &threadrtn))
+ error("readsf_free: join failed");
+ post("... done.");
+
+ pthread_cond_destroy(&x->x_requestcondition);
+ pthread_cond_destroy(&x->x_answercondition);
+ pthread_mutex_destroy(&x->x_mutex);
+ freebytes(x->x_buf, x->x_bufsize);
+ clock_free(x->x_clock);
+}
+
+static void readsf_setup(void)
+{
+ readsf_class = class_new(gensym("readsf~"), (t_newmethod)readsf_new,
+ (t_method)readsf_free, sizeof(t_readsf), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_addfloat(readsf_class, (t_method)readsf_float);
+ class_addmethod(readsf_class, (t_method)readsf_start, gensym("start"), 0);
+ class_addmethod(readsf_class, (t_method)readsf_stop, gensym("stop"), 0);
+ class_addmethod(readsf_class, (t_method)readsf_dsp, gensym("dsp"), 0);
+ class_addmethod(readsf_class, (t_method)readsf_open, gensym("open"),
+ A_GIMME, 0);
+ class_addmethod(readsf_class, (t_method)readsf_print, gensym("print"), 0);
+}
+
+/******************************* writesf *******************/
+
+static t_class *writesf_class;
+
+#define t_writesf t_readsf /* just re-use the structure */
+
+/************** the child thread which performs file I/O ***********/
+
+static void *writesf_child_main(void *zz)
+{
+ t_writesf *x = zz;
+ pute("1\n");
+ pthread_mutex_lock(&x->x_mutex);
+ while (1)
+ {
+ pute("0\n");
+ if (x->x_requestcode == REQUEST_NOTHING)
+ {
+ pute("wait 2\n");
+ sfread_cond_signal(&x->x_answercondition);
+ sfread_cond_wait(&x->x_requestcondition, &x->x_mutex);
+ pute("3\n");
+ }
+ else if (x->x_requestcode == REQUEST_OPEN)
+ {
+ char boo[80];
+ int fd, sysrtn, writebytes;
+
+ /* copy file stuff out of the data structure so we can
+ relinquish the mutex while we're in open_soundfile(). */
+ long onsetframes = x->x_onsetframes;
+ long bytelimit = 0x7fffffff;
+ int skipheaderbytes = x->x_skipheaderbytes;
+ int bytespersample = x->x_bytespersample;
+ int sfchannels = x->x_sfchannels;
+ int bigendian = x->x_bigendian;
+ int filetype = x->x_filetype;
+ char *filename = x->x_filename;
+ t_canvas *canvas = x->x_canvas;
+
+ /* alter the request code so that an ensuing "open" will get
+ noticed. */
+ pute("4\n");
+ x->x_requestcode = REQUEST_BUSY;
+ x->x_fileerror = 0;
+
+ /* if there's already a file open, close it */
+ if (x->x_fd >= 0)
+ {
+ pthread_mutex_unlock(&x->x_mutex);
+ close (x->x_fd);
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_fd = -1;
+ if (x->x_requestcode != REQUEST_BUSY)
+ continue;
+ }
+ /* open the soundfile with the mutex unlocked */
+ pthread_mutex_unlock(&x->x_mutex);
+ fd = create_soundfile(canvas, filename, filetype, 0,
+ bytespersample, bigendian, sfchannels,
+ garray_ambigendian() != bigendian);
+ pthread_mutex_lock(&x->x_mutex);
+
+ pute("5\n");
+
+ if (fd < 0)
+ {
+ x->x_fd = -1;
+ x->x_eof = 1;
+ x->x_fileerror = errno;
+ pute("open failed\n");
+ pute(filename);
+ x->x_requestcode = REQUEST_NOTHING;
+ continue;
+ }
+ /* check if another request has been made; if so, field it */
+ if (x->x_requestcode != REQUEST_BUSY)
+ continue;
+ pute("6\n");
+ x->x_fd = fd;
+ x->x_fifotail = 0;
+ x->x_itemswritten = 0;
+ x->x_swap = garray_ambigendian() != bigendian;
+ /* in a loop, wait for the fifo to have data and write it
+ to disk */
+ while (x->x_requestcode == REQUEST_BUSY ||
+ (x->x_requestcode == REQUEST_CLOSE &&
+ x->x_fifohead != x->x_fifotail))
+ {
+ int fifosize = x->x_fifosize, fifotail;
+ char *buf = x->x_buf;
+ pute("77\n");
+
+ /* if the head is < the tail, we can immediately write
+ from tail to end of fifo to disk; otherwise we hold off
+ writing until there are at least WRITESIZE bytes in the
+ buffer */
+ if (x->x_fifohead < x->x_fifotail ||
+ x->x_fifohead >= x->x_fifotail + WRITESIZE
+ || (x->x_requestcode == REQUEST_CLOSE &&
+ x->x_fifohead != x->x_fifotail))
+ {
+ writebytes = (x->x_fifohead < x->x_fifotail ?
+ fifosize : x->x_fifohead) - x->x_fifotail;
+ if (writebytes > READSIZE)
+ writebytes = READSIZE;
+ }
+ else
+ {
+ pute("wait 7a ...\n");
+ sfread_cond_signal(&x->x_answercondition);
+ pute("signalled\n");
+ sfread_cond_wait(&x->x_requestcondition,
+ &x->x_mutex);
+ pute("7a done\n");
+ continue;
+ }
+ pute("8\n");
+ fifotail = x->x_fifotail;
+ fd = x->x_fd;
+ pthread_mutex_unlock(&x->x_mutex);
+ sysrtn = write(fd, buf + fifotail, writebytes);
+ pthread_mutex_lock(&x->x_mutex);
+ if (x->x_requestcode != REQUEST_BUSY &&
+ x->x_requestcode != REQUEST_CLOSE)
+ break;
+ if (sysrtn < writebytes)
+ {
+ pute("fileerror\n");
+ x->x_fileerror = errno;
+ break;
+ }
+ else
+ {
+ x->x_fifotail += sysrtn;
+ if (x->x_fifotail == fifosize)
+ x->x_fifotail = 0;
+ }
+ x->x_itemswritten +=
+ sysrtn / (x->x_bytespersample * x->x_sfchannels);
+ sprintf(boo, "after: head %d, tail %d\n",
+ x->x_fifohead, x->x_fifotail);
+ pute(boo);
+ /* signal parent in case it's waiting for data */
+ sfread_cond_signal(&x->x_answercondition);
+ }
+ }
+ else if (x->x_requestcode == REQUEST_CLOSE ||
+ x->x_requestcode == REQUEST_QUIT)
+ {
+ int quit = (x->x_requestcode == REQUEST_QUIT);
+ if (x->x_fd >= 0)
+ {
+ int bytesperframe = x->x_bytespersample * x->x_sfchannels;
+ int bigendian = x->x_bigendian;
+ char *filename = x->x_filename;
+ int fd = x->x_fd;
+ int filetype = x->x_filetype;
+ int itemswritten = x->x_itemswritten;
+ int swap = x->x_swap;
+ pthread_mutex_unlock(&x->x_mutex);
+
+ soundfile_finishwrite(x, filename, fd,
+ filetype, 0x7fffffff, itemswritten,
+ bytesperframe, swap);
+ close (fd);
+
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_fd = -1;
+ }
+ x->x_requestcode = REQUEST_NOTHING;
+ sfread_cond_signal(&x->x_answercondition);
+ if (quit)
+ break;
+ }
+ else
+ {
+ pute("13\n");
+ }
+ }
+ pute("thread exit\n");
+ pthread_mutex_unlock(&x->x_mutex);
+ return (0);
+}
+
+/******** the object proper runs in the calling (parent) thread ****/
+
+static void writesf_tick(t_writesf *x);
+
+static void *writesf_new(t_floatarg fnchannels, t_floatarg fbufsize)
+{
+ t_writesf *x;
+ int nchannels = fnchannels, bufsize = fbufsize, i;
+ char *buf;
+
+ if (nchannels < 1)
+ nchannels = 1;
+ else if (nchannels > MAXSFCHANS)
+ nchannels = MAXSFCHANS;
+ if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels;
+ else if (bufsize < MINBUFSIZE)
+ bufsize = MINBUFSIZE;
+ else if (bufsize > MAXBUFSIZE)
+ bufsize = MAXBUFSIZE;
+ buf = getbytes(bufsize);
+ if (!buf) return (0);
+
+ x = (t_writesf *)pd_new(writesf_class);
+
+ for (i = 1; i < nchannels; i++)
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+
+ x->x_f = 0;
+ x->x_sfchannels = nchannels;
+ pthread_mutex_init(&x->x_mutex, 0);
+ pthread_cond_init(&x->x_requestcondition, 0);
+ pthread_cond_init(&x->x_answercondition, 0);
+ x->x_vecsize = MAXVECSIZE;
+ x->x_state = STATE_IDLE;
+ x->x_clock = 0; /* no callback needed here */
+ x->x_canvas = canvas_getcurrent();
+ x->x_bytespersample = 2;
+ x->x_fd = -1;
+ x->x_buf = buf;
+ x->x_bufsize = bufsize;
+ x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0;
+ pthread_create(&x->x_childthread, 0, writesf_child_main, x);
+ return (x);
+}
+
+static t_int *writesf_perform(t_int *w)
+{
+ t_writesf *x = (t_writesf *)(w[1]);
+ int vecsize = x->x_vecsize, sfchannels = x->x_sfchannels, i, j,
+ bytespersample = x->x_bytespersample,
+ bigendian = x->x_bigendian;
+ float *fp;
+ if (x->x_state == STATE_STREAM)
+ {
+ int wantbytes;
+ pthread_mutex_lock(&x->x_mutex);
+ wantbytes = sfchannels * vecsize * bytespersample;
+ while (x->x_fifotail > x->x_fifohead &&
+ x->x_fifotail < x->x_fifohead + wantbytes + 1)
+ {
+ pute("wait...\n");
+ sfread_cond_signal(&x->x_requestcondition);
+ sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
+ pute("done\n");
+ }
+
+ soundfile_xferout(sfchannels, x->x_outvec,
+ (unsigned char *)(x->x_buf + x->x_fifohead), vecsize, 0,
+ bytespersample, bigendian, 1.);
+
+ x->x_fifohead += wantbytes;
+ if (x->x_fifohead >= x->x_fifosize)
+ x->x_fifohead = 0;
+ if ((--x->x_sigcountdown) <= 0)
+ {
+ pute("signal 1\n");
+ sfread_cond_signal(&x->x_requestcondition);
+ x->x_sigcountdown = x->x_sigperiod;
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+ }
+ return (w+2);
+}
+
+static void writesf_start(t_writesf *x)
+{
+ /* start making output. If we're in the "startup" state change
+ to the "running" state. */
+ if (x->x_state == STATE_STARTUP)
+ x->x_state = STATE_STREAM;
+ else
+ pd_error(x, "writesf: start requested with no prior 'open'");
+}
+
+static void writesf_stop(t_writesf *x)
+{
+ /* LATER rethink whether you need the mutex just to set a Svariable? */
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_state = STATE_IDLE;
+ x->x_requestcode = REQUEST_CLOSE;
+ pute("signal 2\n");
+ sfread_cond_signal(&x->x_requestcondition);
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+
+ /* open method. Called as: open [args] filename with args as in
+ soundfiler_writeargparse().
+ */
+
+static void writesf_open(t_writesf *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *filesym;
+ int filetype, bytespersamp, swap, bigendian, normalize;
+ long onset, nframes;
+ if (soundfiler_writeargparse(x, &argc,
+ &argv, &filesym, &filetype, &bytespersamp, &swap, &bigendian,
+ &normalize, &onset, &nframes))
+ {
+ pd_error(x,
+ "writesf~: usage: open [-bytes [234]] [-wave,-nextstep,-aiff] ...");
+ post("... [-big,-little] filename");
+ }
+ if (normalize || onset || (nframes != 0x7fffffff))
+ pd_error(x, "normalize/onset/nframes argument to writesf~: ignored");
+ if (argc)
+ pd_error(x, "extra argument(s) to writesf~: ignored");
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_bytespersample = bytespersamp;
+ x->x_swap = swap;
+ x->x_bigendian = bigendian;
+ x->x_filename = filesym->s_name;
+ x->x_filetype = filetype;
+ x->x_itemswritten = 0;
+ x->x_requestcode = REQUEST_OPEN;
+ x->x_fifotail = 0;
+ x->x_fifohead = 0;
+ x->x_eof = 0;
+ x->x_fileerror = 0;
+ x->x_state = STATE_STARTUP;
+ x->x_bytespersample = (bytespersamp > 2 ? bytespersamp : 2);
+ /* set fifosize from bufsize. fifosize must be a
+ multiple of the number of bytes eaten for each DSP
+ tick. */
+ x->x_fifosize = x->x_bufsize - (x->x_bufsize %
+ (x->x_bytespersample * x->x_sfchannels * MAXVECSIZE));
+ /* arrange for the "request" condition to be signalled 16
+ times per buffer */
+ x->x_sigcountdown = x->x_sigperiod =
+ (x->x_fifosize /
+ (16 * x->x_bytespersample * x->x_sfchannels *
+ x->x_vecsize));
+ sfread_cond_signal(&x->x_requestcondition);
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+static void writesf_dsp(t_writesf *x, t_signal **sp)
+{
+ int i, ninlets = x->x_sfchannels;
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_vecsize = sp[0]->s_n;
+
+ x->x_sigperiod = (x->x_fifosize /
+ (x->x_bytespersample * ninlets * x->x_vecsize));
+ for (i = 0; i < ninlets; i++)
+ x->x_outvec[i] = sp[i]->s_vec;
+ pthread_mutex_unlock(&x->x_mutex);
+ dsp_add(writesf_perform, 1, x);
+}
+
+static void writesf_print(t_writesf *x)
+{
+ post("state %d", x->x_state);
+ post("fifo head %d", x->x_fifohead);
+ post("fifo tail %d", x->x_fifotail);
+ post("fifo size %d", x->x_fifosize);
+ post("fd %d", x->x_fd);
+ post("eof %d", x->x_eof);
+}
+
+static void writesf_free(t_writesf *x)
+{
+ /* request QUIT and wait for acknowledge */
+ void *threadrtn;
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_requestcode = REQUEST_QUIT;
+ /* post("stopping writesf thread..."); */
+ sfread_cond_signal(&x->x_requestcondition);
+ while (x->x_requestcode != REQUEST_NOTHING)
+ {
+ /* post("signalling..."); */
+ sfread_cond_signal(&x->x_requestcondition);
+ sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+ if (pthread_join(x->x_childthread, &threadrtn))
+ error("writesf_free: join failed");
+ /* post("... done."); */
+
+ pthread_cond_destroy(&x->x_requestcondition);
+ pthread_cond_destroy(&x->x_answercondition);
+ pthread_mutex_destroy(&x->x_mutex);
+ freebytes(x->x_buf, x->x_bufsize);
+}
+
+static void writesf_setup(void)
+{
+ writesf_class = class_new(gensym("writesf~"), (t_newmethod)writesf_new,
+ (t_method)writesf_free, sizeof(t_writesf), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_addmethod(writesf_class, (t_method)writesf_start, gensym("start"), 0);
+ class_addmethod(writesf_class, (t_method)writesf_stop, gensym("stop"), 0);
+ class_addmethod(writesf_class, (t_method)writesf_dsp, gensym("dsp"), 0);
+ class_addmethod(writesf_class, (t_method)writesf_open, gensym("open"),
+ A_GIMME, 0);
+ class_addmethod(writesf_class, (t_method)writesf_print, gensym("print"), 0);
+ CLASS_MAINSIGNALIN(writesf_class, t_writesf, x_f);
+}
+
+/* ------------------------ global setup routine ------------------------- */
+
+void d_soundfile_setup(void)
+{
+ soundfiler_setup();
+ readsf_setup();
+ writesf_setup();
+}
+
diff --git a/pd/src/d_ugen.c b/pd/src/d_ugen.c
new file mode 100644
index 00000000..68e931e6
--- /dev/null
+++ b/pd/src/d_ugen.c
@@ -0,0 +1,1078 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* These routines build a copy of the DSP portion of a graph, which is
+ then sorted into a linear list of DSP operations which are added to
+ the DSP duty cycle called by the scheduler. Once that's been done,
+ we delete the copy. The DSP objects are represented by "ugenbox"
+ structures which are parallel to the DSP objects in the graph and
+ have vectors of siginlets and sigoutlets which record their
+ interconnections.
+*/
+
+/* hacked to run subpatches with different samplerates
+ * only samplerates that are a power_of_2-multiple of the
+ *
+ * mfg.gfd.uil
+ * IOhannes
+ *
+ * edited lines are marked with "IOhannes"
+ *
+ */
+
+
+#include "m_pd.h"
+#include <stdlib.h>
+#include <stdarg.h>
+
+extern t_class *vinlet_class, *voutlet_class, *canvas_class;
+int obj_nsiginlets(t_object *x);
+int obj_siginletindex(t_object *x, int m);
+int obj_nsigoutlets(t_object *x);
+int obj_sigoutletindex(t_object *x, int m);
+t_sample *obj_findsignalscalar(t_object *x, int m);
+static int ugen_loud;
+EXTERN_STRUCT _vinlet;
+EXTERN_STRUCT _voutlet;
+
+void vinlet_dspprolog(struct _vinlet *x, t_signal **parentsigs,
+ int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock,
+ int switched);
+void voutlet_dspprolog(struct _voutlet *x, t_signal **parentsigs,
+ int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock,
+ int switched);
+void voutlet_dspepilog(struct _voutlet *x, t_signal **parentsigs,
+ int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock,
+ int switched);
+
+t_int *zero_perform(t_int *w) /* zero out a vector */
+{
+ t_float *out = (t_float *)(w[1]);
+ int n = (int)(w[2]);
+ while (n--) *out++ = 0;
+ return (w+3);
+}
+
+t_int *zero_perf8(t_int *w)
+{
+ t_float *out = (t_float *)(w[1]);
+ int n = (int)(w[2]);
+
+ for (; n; n -= 8, out += 8)
+ {
+ out[0] = 0;
+ out[1] = 0;
+ out[2] = 0;
+ out[3] = 0;
+ out[4] = 0;
+ out[5] = 0;
+ out[6] = 0;
+ out[7] = 0;
+ }
+ return (w+3);
+}
+
+void dsp_add_zero(t_sample *out, int n)
+{
+ if (n&7)
+ dsp_add(zero_perform, 2, out, n);
+ else
+ dsp_add(zero_perf8, 2, out, n);
+}
+
+/* ---------------------------- block~ ----------------------------- */
+
+/* The "block~ object maintains the containing canvas's DSP computation,
+calling it at a super- or sub-multiple of the containing canvas's
+calling frequency. The block~'s creation arguments specify block size
+and overlap. Block~ does no "dsp" computation in its own right, but it
+adds prolog and epilog code before and after the canvas's unit generators.
+
+A subcanvas need not have a block~ at all; if there's none, its
+ugens are simply put on the list without any prolog or epilog code.
+
+Block~ may be invoked as switch~, in which case it also acts to switch the
+subcanvas on and off. The overall order of scheduling for a subcanvas
+is thus,
+
+ inlet and outlet prologue code (1)
+ block prologue (2)
+ the objects in the subcanvas, including inlets and outlets
+ block epilogue (2)
+ outlet epilogue code (2)
+
+where (1) means, "if reblocked" and (2) means, "if reblocked or switched".
+
+If we're reblocked, the inlet prolog and outlet epilog code takes care of
+overlapping and buffering to deal with vector size changes. If we're switched
+but not reblocked, the inlet prolog is not needed, and the output epilog is
+ONLY run when the block is switched off; in this case the epilog code simply
+copies zeros to all signal outlets.
+*/
+
+static int dsp_phase;
+static t_class *block_class;
+
+typedef struct _block
+{
+ t_object x_obj;
+ int x_vecsize;
+ int x_overlap;
+ int x_phase; /* from 0 to period-1; when zero we run the block */
+ int x_period; /* submultiple of containing canvas */
+ int x_frequency; /* supermultiple of comtaining canvas */
+ int x_count;
+ int x_blocklength; /* length of dspchain for this block */
+ int x_epiloglength; /* length of epilog */
+ char x_switched; /* true if we're acting as a a switch */
+ char x_switchon; /* true if we're switched on */
+ char x_reblock; /* true if inlets and outlets are reblocking */
+
+ int x_upsample; /* IOhannes: upsampling-factor */
+ int x_downsample; /* IOhannes: downsampling-factor */
+
+} t_block;
+
+static void *block_new(t_floatarg fvecsize, t_floatarg foverlap,
+ t_floatarg fupsample) /* IOhannes */
+{
+ int vecsize = fvecsize;
+ int overlap = foverlap;
+ int upsample, downsample; /* IOhannes */
+ t_block *x = (t_block *)pd_new(block_class);
+ if (overlap < 1)
+ overlap = 1;
+ if (vecsize < 0)
+ vecsize = 0; /* this means we'll get it from parent later. */
+
+ /* IOhannes { */
+ if (fupsample <= 0) upsample = downsample = 1;
+ else if (fupsample >= 1) {
+ upsample = fupsample;
+ downsample = 1;
+ } else {
+ downsample = 1.0 / fupsample;
+ upsample = 1;
+ }
+ /* } IOhannes */
+
+ if (vecsize && (vecsize != (1 << ilog2(vecsize))))
+ {
+ pd_error(x, "block~: vector size not a power of 2");
+ vecsize = 64;
+ }
+ if (overlap != (1 << ilog2(overlap)))
+ {
+ pd_error(x, "block~: overlap not a power of 2");
+ overlap = 1;
+ }
+ /* IOhannes { */
+ if (downsample != (1 << ilog2(downsample)))
+ {
+ pd_error(x, "block~: downsampling not a power of 2");
+ downsample = 1;
+ }
+ if (upsample != (1 << ilog2(upsample)))
+ {
+ pd_error(x, "block~: upsampling not a power of 2");
+ upsample = 1;
+ }
+ /* } IOhannes */
+
+
+ x->x_vecsize = vecsize;
+ x->x_overlap = overlap;
+ x->x_phase = 0;
+ x->x_period = 1;
+ x->x_frequency = 1;
+ x->x_switched = 0;
+ x->x_switchon = 1;
+ /* IOhannes { */
+ x->x_upsample = upsample;
+ x->x_downsample = downsample;
+ /* } IOhannes */
+ return (x);
+}
+
+static void *switch_new(t_floatarg fvecsize, t_floatarg foverlap,
+ t_floatarg fupsample) /* IOhannes */
+{
+ t_block *x = (t_block *)(block_new(fvecsize, foverlap, fupsample)); /* IOhannes */
+ x->x_switched = 1;
+ x->x_switchon = 0;
+ return (x);
+}
+
+static void block_float(t_block *x, t_floatarg f)
+{
+ if (x->x_switched)
+ x->x_switchon = (f != 0);
+}
+#define PROLOGCALL 2
+#define EPILOGCALL 2
+
+static t_int *block_prolog(t_int *w)
+{
+ t_block *x = (t_block *)w[1];
+ int phase = x->x_phase;
+ /* if we're switched off, jump past the epilog code */
+ if (!x->x_switchon)
+ return (w + x->x_blocklength);
+ if (phase)
+ {
+ phase++;
+ if (phase == x->x_period) phase = 0;
+ x->x_phase = phase;
+ return (w + x->x_blocklength); /* skip block; jump past epilog */
+ }
+ else
+ {
+ x->x_count = x->x_frequency;
+ x->x_phase = (x->x_period > 1 ? 1 : 0);
+ return (w + PROLOGCALL); /* beginning of block is next ugen */
+ }
+}
+
+static t_int *block_epilog(t_int *w)
+{
+ t_block *x = (t_block *)w[1];
+ int count = x->x_count - 1;
+ if (!x->x_reblock)
+ return (w + x->x_epiloglength + EPILOGCALL);
+ if (count)
+ {
+ x->x_count = count;
+ return (w - (x->x_blocklength -
+ (PROLOGCALL + EPILOGCALL))); /* go to ugen after prolog */
+ }
+ else return (w + EPILOGCALL);
+}
+
+static void block_dsp(t_block *x, t_signal **sp)
+{
+ /* do nothing here */
+}
+
+/* ------------------ DSP call list ----------------------- */
+
+static t_int *dsp_chain;
+static int dsp_chainsize;
+
+void dsp_add(t_perfroutine f, int n, ...)
+{
+ int newsize = dsp_chainsize + n+1, i;
+ va_list ap;
+
+ dsp_chain = t_resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int),
+ newsize * sizeof (t_int));
+ dsp_chain[dsp_chainsize-1] = (t_int)f;
+ va_start(ap, n);
+ for (i = 0; i < n; i++)
+ dsp_chain[dsp_chainsize + i] = va_arg(ap, t_int);
+ va_end(ap);
+ dsp_chain[newsize-1] = 0;
+ dsp_chainsize = newsize;
+}
+
+ /* at Guenter's suggestion, here's a vectorized version */
+void dsp_addv(t_perfroutine f, int n, t_int *vec)
+{
+ int newsize = dsp_chainsize + n+1, i;
+
+ dsp_chain = t_resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int),
+ newsize * sizeof (t_int));
+ dsp_chain[dsp_chainsize-1] = (t_int)f;
+ for (i = 0; i < n; i++)
+ dsp_chain[dsp_chainsize + i] = vec[i];
+ dsp_chain[newsize-1] = 0;
+ dsp_chainsize = newsize;
+}
+
+void dsp_tick(void)
+{
+ if (dsp_chain)
+ {
+ t_int *ip;
+ for (ip = dsp_chain; *ip; ) ip = (*(t_perfroutine)(*ip))(ip);
+ dsp_phase++;
+ }
+}
+
+/* ---------------- signals ---------------------------- */
+
+int ilog2(int n)
+{
+ int r = -1;
+ if (n <= 0) return(0);
+ while (n)
+ {
+ r++;
+ n >>= 1;
+ }
+ return (r);
+}
+
+ /* list of signals which can be reused, sorted by buffer size */
+static t_signal *signal_freelist[MAXLOGSIG+1];
+ /* list of reusable "borrowed" signals */
+static t_signal *signal_freeborrowed;
+ /* list of all signals allocated (not including "borrowed" ones) */
+static t_signal *signal_usedlist;
+
+ /* call this when DSP is stopped to free all the signals */
+void signal_cleanup(void)
+{
+ t_signal **svec, *sig, *sig2;
+ int i;
+ while (sig = signal_usedlist)
+ {
+ signal_usedlist = sig->s_nextused;
+ if (!sig->s_isborrowed)
+ t_freebytes(sig->s_vec, sig->s_n * sizeof (*sig->s_vec));
+ t_freebytes(sig, sizeof *sig);
+ }
+ for (i = 0; i <= MAXLOGSIG; i++)
+ signal_freelist[i] = 0;
+ signal_freeborrowed = 0;
+}
+
+ /* mark the signal "reusable." */
+void signal_makereusable(t_signal *sig)
+{
+ int logn = ilog2(sig->s_n);
+#if 1
+ t_signal *s5;
+ for (s5 = signal_freeborrowed; s5; s5 = s5->s_nextfree)
+ {
+ if (s5 == sig)
+ {
+ bug("signal_free 3");
+ return;
+ }
+ }
+ for (s5 = signal_freelist[logn]; s5; s5 = s5->s_nextfree)
+ {
+ if (s5 == sig)
+ {
+ bug("signal_free 4");
+ return;
+ }
+ }
+#endif
+ if (ugen_loud) post("free %x: %d", sig, sig->s_isborrowed);
+ if (sig->s_isborrowed)
+ {
+ /* if the signal is borrowed, decrement the borowee's reference
+ count, possibly marking it reusable too */
+ t_signal *s2 = sig->s_borrowedfrom;
+ if ((s2 == sig) || !s2)
+ bug("signal_free");
+ s2->s_refcount--;
+ if (!s2->s_refcount)
+ signal_makereusable(s2);
+ sig->s_nextfree = signal_freeborrowed;
+ signal_freeborrowed = sig;
+ }
+ else
+ {
+ /* if it's a real signal, put it on the free list so we can
+ reuse it. */
+ if (signal_freelist[logn] == sig) bug("signal_free 2");
+ sig->s_nextfree = signal_freelist[logn];
+ signal_freelist[logn] = sig;
+ }
+}
+
+ /* reclaim or make an audio signal. If n is zero, return a "borrowed"
+ signal whose buffer and size will be obtained later via
+ signal_setborrowed(). */
+
+t_signal *signal_new(int n, float sr)
+{
+ int logn, n2;
+ t_signal *ret, **whichlist;
+ t_sample *fp;
+ logn = ilog2(n);
+ if (n)
+ {
+ if (n != (1 << logn))
+ bug("signal buffer not a power of 2");
+ if (logn > MAXLOGSIG)
+ bug("signal buffer too large");
+ whichlist = signal_freelist + logn;
+ }
+ else
+ whichlist = &signal_freeborrowed;
+
+ /* first try to reclaim one from the free list */
+ if (ret = *whichlist)
+ *whichlist = ret->s_nextfree;
+ else
+ {
+ /* LATER figure out what to do for out-of-space here! */
+ ret = (t_signal *)t_getbytes(sizeof *ret);
+ if (n)
+ {
+ ret->s_vec = (t_sample *)getbytes(n * sizeof (*ret->s_vec));
+ ret->s_isborrowed = 0;
+ }
+ else
+ {
+ ret->s_vec = 0;
+ ret->s_isborrowed = 1;
+ }
+ ret->s_nextused = signal_usedlist;
+ signal_usedlist = ret;
+ }
+ ret->s_n = n;
+ ret->s_sr = sr;
+ ret->s_refcount = 0;
+ ret->s_borrowedfrom = 0;
+ if (ugen_loud) post("new %x: %d", ret, ret->s_isborrowed);
+ return (ret);
+}
+
+static t_signal *signal_newlike(const t_signal *sig)
+{
+ return (signal_new(sig->s_n, sig->s_sr));
+}
+
+void signal_setborrowed(t_signal *sig, t_signal *sig2)
+{
+ if (!sig->s_isborrowed || sig->s_borrowedfrom)
+ bug("signal_setborrowed");
+ sig->s_borrowedfrom = sig2;
+ sig->s_vec = sig2->s_vec;
+ sig->s_n = sig2->s_n;
+}
+
+int signal_compatible(t_signal *s1, t_signal *s2)
+{
+ return (s1->s_n == s2->s_n && s1->s_sr == s2->s_sr);
+}
+
+/* ------------------ ugen ("unit generator") sorting ----------------- */
+
+typedef struct _ugenbox
+{
+ struct _siginlet *u_in;
+ int u_nin;
+ struct _sigoutlet *u_out;
+ int u_nout;
+ int u_phase;
+ struct _ugenbox *u_next;
+ t_object *u_obj;
+ int u_done;
+} t_ugenbox;
+
+typedef struct _siginlet
+{
+ int i_nconnect;
+ int i_ngot;
+ t_signal *i_signal;
+} t_siginlet;
+
+typedef struct _sigoutconnect
+{
+ t_ugenbox *oc_who;
+ int oc_inno;
+ struct _sigoutconnect *oc_next;
+} t_sigoutconnect;
+
+typedef struct _sigoutlet
+{
+ int o_nconnect;
+ int o_nsent;
+ t_signal *o_signal;
+ t_sigoutconnect *o_connections;
+} t_sigoutlet;
+
+
+struct _dspcontext
+{
+ struct _ugenbox *dc_ugenlist;
+ struct _dspcontext *dc_parentcontext;
+ int dc_ninlets;
+ int dc_noutlets;
+ t_signal **dc_iosigs;
+ float dc_srate;
+ int dc_vecsize;
+ char dc_toplevel; /* true if "iosigs" is invalid. */
+ char dc_reblock; /* true if we have to reblock inlets/outlets */
+ char dc_switched; /* true if we're switched */
+
+};
+
+#define t_dspcontext struct _dspcontext
+
+static int ugen_sortno = 0;
+static t_dspcontext *ugen_currentcontext;
+
+void ugen_stop(void)
+{
+ t_signal *s;
+ int i;
+ if (dsp_chain)
+ {
+ freebytes(dsp_chain, dsp_chainsize * sizeof (t_int));
+ dsp_chain = 0;
+ }
+ signal_cleanup();
+
+}
+
+void ugen_start(void)
+{
+ ugen_stop();
+ ugen_sortno++;
+ dsp_chain = (t_int *)getbytes(sizeof(*dsp_chain));
+ dsp_chain[0] = 0;
+ dsp_chainsize = 1;
+ if (ugen_currentcontext) bug("ugen_start");
+}
+
+int ugen_getsortno(void)
+{
+ return (ugen_sortno);
+}
+
+#if 0
+void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv)
+{
+ int i, count;
+ t_signal *sig;
+ for (count = 0, sig = signal_usedlist; sig;
+ count++, sig = sig->s_nextused)
+ ;
+ post("used signals %d", count);
+ for (i = 0; i < MAXLOGSIG; i++)
+ {
+ for (count = 0, sig = signal_freelist[i]; sig;
+ count++, sig = sig->s_nextfree)
+ ;
+ if (count)
+ post("size %d: free %d", (1 << i), count);
+ }
+ for (count = 0, sig = signal_freeborrowed; sig;
+ count++, sig = sig->s_nextfree)
+ ;
+ post("free borrowed %d", count);
+
+ ugen_loud = argc;
+}
+#endif
+
+ /* start building the graph for a canvas */
+t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp,
+ int ninlets, int noutlets)
+{
+ t_dspcontext *dc = (t_dspcontext *)getbytes(sizeof(*dc));
+ float parent_srate, srate;
+ int parent_vecsize, vecsize;
+
+ if (ugen_loud) post("ugen_start_graph...");
+
+ dc->dc_ugenlist = 0;
+ dc->dc_toplevel = toplevel;
+ dc->dc_iosigs = sp;
+ dc->dc_ninlets = ninlets;
+ dc->dc_noutlets = noutlets;
+ dc->dc_parentcontext = ugen_currentcontext;
+ ugen_currentcontext = dc;
+ return (dc);
+}
+
+ /* first the canvas calls this to create all the boxes... */
+void ugen_add(t_dspcontext *dc, t_object *obj, int nextjump)
+{
+ t_ugenbox *x = (t_ugenbox *)getbytes(sizeof *x);
+ int i;
+ t_sigoutlet *uout;
+ t_siginlet *uin;
+
+ x->u_next = dc->dc_ugenlist;
+ dc->dc_ugenlist = x;
+ x->u_obj = obj;
+ x->u_nin = obj_nsiginlets(obj);
+ x->u_in = getbytes(x->u_nin * sizeof (*x->u_in));
+ for (uin = x->u_in, i = x->u_nin; i--; uin++)
+ uin->i_nconnect = 0;
+ x->u_nout = obj_nsigoutlets(obj);
+ x->u_out = getbytes(x->u_nout * sizeof (*x->u_out));
+ for (uout = x->u_out, i = x->u_nout; i--; uout++)
+ uout->o_connections = 0, uout->o_nconnect = 0;
+}
+
+ /* and then this to make all the connections. */
+void ugen_connect(t_dspcontext *dc, t_object *x1, int outno, t_object *x2,
+ int inno)
+{
+ t_ugenbox *u1, *u2;
+ t_sigoutlet *uout;
+ t_siginlet *uin;
+ t_sigoutconnect *oc;
+ int sigoutno = obj_sigoutletindex(x1, outno);
+ int siginno = obj_siginletindex(x2, inno);
+ if (ugen_loud)
+ post("%s -> %s: %d->%d",
+ class_getname(x1->ob_pd),
+ class_getname(x2->ob_pd), outno, inno);
+ for (u1 = dc->dc_ugenlist; u1 && u1->u_obj != x1; u1 = u1->u_next);
+ for (u2 = dc->dc_ugenlist; u2 && u2->u_obj != x2; u2 = u2->u_next);
+ if (!u1 || !u2 || siginno < 0)
+ {
+ pd_error(u1->u_obj, "signal outlet connect to nonsignal inlet (ignored)");
+ return;
+ }
+ if (sigoutno < 0 || sigoutno >= u1->u_nout || siginno >= u2->u_nin)
+ {
+ bug("ugen_connect %s %s %d %d (%d %d)",
+ class_getname(x1->ob_pd),
+ class_getname(x2->ob_pd), sigoutno, siginno, u1->u_nout,
+ u2->u_nin);
+ }
+ uout = u1->u_out + sigoutno;
+ uin = u2->u_in + siginno;
+
+ /* add a new connection to the outlet's list */
+ oc = (t_sigoutconnect *)getbytes(sizeof *oc);
+ oc->oc_next = uout->o_connections;
+ uout->o_connections = oc;
+ oc->oc_who = u2;
+ oc->oc_inno = siginno;
+ /* update inlet and outlet counts */
+ uout->o_nconnect++;
+ uin->i_nconnect++;
+}
+
+ /* get the index of a ugenbox or -1 if it's not on the list */
+static int ugen_index(t_dspcontext *dc, t_ugenbox *x)
+{
+ int ret;
+ t_ugenbox *u;
+ for (u = dc->dc_ugenlist, ret = 0; u; u = u->u_next, ret++)
+ if (u == x) return (ret);
+ return (-1);
+}
+
+ /* put a ugenbox on the chain, recursively putting any others on that
+ this one might uncover. */
+static void ugen_doit(t_dspcontext *dc, t_ugenbox *u)
+{
+ t_sigoutlet *uout;
+ t_siginlet *uin;
+ t_sigoutconnect *oc, *oc2;
+ t_class *class = pd_class(&u->u_obj->ob_pd);
+ int i, n;
+ /* suppress creating new signals for the outputs of signal
+ inlets and subpatchs; except in the case we're an inlet and "blocking"
+ is set. We don't yet know if a subcanvas will be "blocking" so there
+ we delay new signal creation, which will be handled by calling
+ signal_setborrowed in the ugen_done_graph routine below. */
+ int nonewsigs = (class == canvas_class ||
+ (class == vinlet_class) && !(dc->dc_reblock));
+ /* when we encounter a subcanvas or a signal outlet, suppress freeing
+ the input signals as they may be "borrowed" for the super or sub
+ patch; same exception as above, but also if we're "switched" we
+ have to do a copy rather than a borrow. */
+ int nofreesigs = (class == canvas_class ||
+ (class == voutlet_class) && !(dc->dc_reblock || dc->dc_switched));
+ t_signal **insig, **outsig, **sig, *s1, *s2, *s3;
+ t_ugenbox *u2;
+
+ if (ugen_loud) post("doit %s %d %d", class_getname(class), nofreesigs,
+ nonewsigs);
+ for (i = 0, uin = u->u_in; i < u->u_nin; i++, uin++)
+ {
+ if (!uin->i_nconnect)
+ {
+ t_sample *scalar;
+ s3 = signal_new(dc->dc_vecsize, dc->dc_srate);
+ /* post("%s: unconnected signal inlet set to zero",
+ class_getname(u->u_obj->ob_pd)); */
+ if (scalar = obj_findsignalscalar(u->u_obj, i))
+ dsp_add_scalarcopy(scalar, s3->s_vec, s3->s_n);
+ else
+ dsp_add_zero(s3->s_vec, s3->s_n);
+ uin->i_signal = s3;
+ s3->s_refcount = 1;
+ }
+ }
+ insig = (t_signal **)getbytes((u->u_nin + u->u_nout) * sizeof(t_signal *));
+ outsig = insig + u->u_nin;
+ for (sig = insig, uin = u->u_in, i = u->u_nin; i--; sig++, uin++)
+ {
+ int newrefcount;
+ *sig = uin->i_signal;
+ newrefcount = --(*sig)->s_refcount;
+ /* if the reference count went to zero, we free the signal now,
+ unless it's a subcanvas or outlet; these might keep the
+ signal around to send to objects connected to them. In this
+ case we increment the reference count; the corresponding decrement
+ is in sig_makereusable(). */
+ if (nofreesigs)
+ (*sig)->s_refcount++;
+ else if (!newrefcount)
+ signal_makereusable(*sig);
+ }
+ for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++)
+ {
+ /* similarly, for outlets of subcanvases we delay creating
+ them; instead we create "borrowed" ones so that the refcount
+ is known. The subcanvas replaces the fake signal with one showing
+ where the output data actually is, to avoid having to copy it.
+ For any other object, we just allocate a new output vector;
+ since we've already freed the inputs the objects might get called
+ "in place." */
+ if (nonewsigs)
+ {
+ *sig = uout->o_signal =
+ signal_new(0, dc->dc_srate);
+ }
+ else
+ *sig = uout->o_signal = signal_new(dc->dc_vecsize, dc->dc_srate);
+ (*sig)->s_refcount = uout->o_nconnect;
+ }
+ /* now call the DSP scheduling routine for the ugen. This
+ routine must fill in "borrowed" signal outputs in case it's either
+ a subcanvas or a signal inlet. */
+ mess1(&u->u_obj->ob_pd, gensym("dsp"), insig);
+
+ /* if any output signals aren't connected to anyone, free them
+ now; otherwise they'll either get freed when the reference count
+ goes back to zero, or even later as explained above. */
+
+ for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++)
+ {
+ if (!(*sig)->s_refcount)
+ signal_makereusable(*sig);
+ }
+ if (ugen_loud)
+ {
+ if (u->u_nin + u->u_nout == 0) post("put %s %d",
+ class_getname(u->u_obj->ob_pd), ugen_index(dc, u));
+ else if (u->u_nin + u->u_nout == 1) post("put %s %d (%x)",
+ class_getname(u->u_obj->ob_pd), ugen_index(dc, u), sig[0]);
+ else if (u->u_nin + u->u_nout == 2) post("put %s %d (%x %x)",
+ class_getname(u->u_obj->ob_pd), ugen_index(dc, u),
+ sig[0], sig[1]);
+ else post("put %s %d (%x %x %x ...)",
+ class_getname(u->u_obj->ob_pd), ugen_index(dc, u),
+ sig[0], sig[1], sig[2]);
+ }
+
+ /* pass it on and trip anyone whose last inlet was filled */
+ for (uout = u->u_out, i = u->u_nout; i--; uout++)
+ {
+ s1 = uout->o_signal;
+ for (oc = uout->o_connections; oc; oc = oc->oc_next)
+ {
+ u2 = oc->oc_who;
+ uin = &u2->u_in[oc->oc_inno];
+ /* if there's already someone here, sum the two */
+ if (s2 = uin->i_signal)
+ {
+ s1->s_refcount--;
+ s2->s_refcount--;
+ if (!signal_compatible(s1, s2))
+ {
+ pd_error(u->u_obj, "%s: incompatible signal inputs",
+ class_getname(u->u_obj->ob_pd));
+ return;
+ }
+ s3 = signal_newlike(s1);
+ dsp_add_plus(s1->s_vec, s2->s_vec, s3->s_vec, s1->s_n);
+ uin->i_signal = s3;
+ s3->s_refcount = 1;
+ if (!s1->s_refcount) signal_makereusable(s1);
+ if (!s2->s_refcount) signal_makereusable(s2);
+ }
+ else uin->i_signal = s1;
+ uin->i_ngot++;
+ /* if we didn't fill this inlet don't bother yet */
+ if (uin->i_ngot < uin->i_nconnect)
+ goto notyet;
+ /* if there's more than one, check them all */
+ if (u2->u_nin > 1)
+ {
+ for (uin = u2->u_in, n = u2->u_nin; n--; uin++)
+ if (uin->i_ngot < uin->i_nconnect) goto notyet;
+ }
+ /* so now we can schedule the ugen. */
+ ugen_doit(dc, u2);
+ notyet: ;
+ }
+ }
+ t_freebytes(insig,(u->u_nin + u->u_nout) * sizeof(t_signal *));
+}
+
+ /* once the DSP graph is built, we call this routine to sort it.
+ This routine also deletes the graph; later we might want to leave the
+ graph around, in case the user is editing the DSP network, to save having
+ to recreate it all the time. But not today. */
+
+void ugen_done_graph(t_dspcontext *dc)
+{
+ t_ugenbox *u, *u2;
+ t_sigoutlet *uout;
+ t_siginlet *uin;
+ t_sigoutconnect *oc, *oc2;
+ int i, n;
+ t_block *blk;
+ t_dspcontext *parent_context = dc->dc_parentcontext;
+ float parent_srate;
+ int parent_vecsize;
+ int period, frequency, phase, vecsize;
+ float srate;
+ int chainblockbegin; /* DSP chain onset before block prolog code */
+ int chainblockend; /* and after block epilog code */
+ int chainafterall; /* and after signal outlet epilog */
+ int reblock = 0, switched;
+ int downsample = 1, upsample = 1; /* IOhannes */
+ /* debugging printout */
+
+ if (ugen_loud)
+ {
+ post("ugen_done_graph...");
+ for (u = dc->dc_ugenlist; u; u = u->u_next)
+ {
+ post("ugen: %s", class_getname(u->u_obj->ob_pd));
+ for (uout = u->u_out, i = 0; i < u->u_nout; uout++, i++)
+ for (oc = uout->o_connections; oc; oc = oc->oc_next)
+ {
+ post("... out %d to %s, index %d, inlet %d", i,
+ class_getname(oc->oc_who->u_obj->ob_pd),
+ ugen_index(dc, oc->oc_who), oc->oc_inno);
+ }
+ }
+ }
+
+ /* search for an object of class "block~" */
+ for (u = dc->dc_ugenlist, blk = 0; u; u = u->u_next)
+ {
+ t_pd *zz = &u->u_obj->ob_pd;
+ if (pd_class(zz) == block_class)
+ {
+ if (blk) pd_error(blk, "conflicting block~ objects in same page");
+ else blk = (t_block *)zz;
+ }
+ }
+
+ /* figure out block size, calling frequency, sample rate */
+ if (parent_context)
+ {
+ parent_srate = parent_context->dc_srate;
+ parent_vecsize = parent_context->dc_vecsize;
+ }
+ else
+ {
+ parent_srate = sys_getsr();
+ parent_vecsize = sys_getblksize();
+ }
+ if (blk)
+ {
+ int realoverlap;
+ vecsize = blk->x_vecsize;
+ if (vecsize == 0)
+ vecsize = parent_vecsize;
+ realoverlap = blk->x_overlap;
+ if (realoverlap > vecsize) realoverlap = vecsize;
+ /* IOhannes { */
+ downsample = blk->x_downsample;
+ upsample = blk->x_upsample;
+ if (downsample > parent_vecsize) downsample=parent_vecsize;
+ period = (vecsize * downsample)/(parent_vecsize * realoverlap * upsample);
+ frequency = (parent_vecsize * realoverlap * upsample)/(vecsize * downsample);
+ /* } IOhannes*/
+ phase = blk->x_phase;
+ srate = parent_srate * realoverlap * upsample / downsample;
+ /* IOhannes */
+ if (period < 1) period = 1;
+ if (frequency < 1) frequency = 1;
+ blk->x_frequency = frequency;
+ blk->x_period = period;
+ blk->x_phase = dsp_phase & (period - 1);
+ if (! parent_context || (realoverlap != 1) || (vecsize != parent_vecsize) ||
+ (downsample != 1) || (upsample != 1)) /* IOhannes */
+ reblock = 1;
+ switched = blk->x_switched;
+ }
+ else
+ {
+ srate = parent_srate;
+ vecsize = parent_vecsize;
+ downsample = upsample = 1;/* IOhannes */
+ period = frequency = 1;
+ phase = 0;
+ if (!parent_context) reblock = 1;
+ switched = 0;
+ }
+ dc->dc_reblock = reblock;
+ dc->dc_switched = switched;
+ dc->dc_srate = srate;
+ dc->dc_vecsize = vecsize;
+
+ /* if we're reblocking or switched, we now have to create output
+ signals to fill in for the "borrowed" ones we have now. The
+ output signals will be filled by the outlet epilog code. */
+
+ if (reblock || switched)
+ {
+ t_signal **iosigs = dc->dc_iosigs;
+ if (iosigs)
+ {
+ t_signal **sigp;
+ int noutlets = dc->dc_noutlets;
+ for (i = 0, sigp = iosigs + dc->dc_ninlets; i < noutlets;
+ i++, sigp++)
+ {
+ signal_setborrowed(*sigp,
+ signal_new(parent_vecsize, parent_srate));
+ (*sigp)->s_refcount++;
+ if (ugen_loud) post("set %x->%x", *sigp, (*sigp)->s_borrowedfrom);
+ }
+ }
+ }
+ if (ugen_loud)
+ post("reblock %d, switched %d", reblock, switched);
+
+ /* schedule prologs for inlets and outlets. If the "reblock" flag
+ is set, an inlet will put code on the DSP chain to copy its input
+ into an internal buffer here, before any unit generators' DSP code
+ gets scheduled. If we don't "reblock", inlets will need to get
+ pointers to their corresponding inlets/outlets on the box we're inside,
+ if any. Outlets will also need pointers, unless we're switched, in
+ which case outlet epilog code will kick in. */
+
+ for (u = dc->dc_ugenlist; u; u = u->u_next)
+ {
+ t_pd *zz = &u->u_obj->ob_pd;
+ t_signal **insigs = dc->dc_iosigs, **outsigs = dc->dc_iosigs;
+ if (outsigs) outsigs += dc->dc_ninlets;
+
+ if (pd_class(zz) == vinlet_class)
+ vinlet_dspprolog((struct _vinlet *)zz,
+ dc->dc_iosigs, vecsize, dsp_phase, period, frequency,
+ downsample, upsample, /* IOhannes */
+ reblock, switched);
+ else if (pd_class(zz) == voutlet_class)
+ voutlet_dspprolog((struct _voutlet *)zz,
+ outsigs, vecsize, dsp_phase, period, frequency,
+ downsample, upsample, /* IOhannes */
+ reblock, switched);
+ }
+ chainblockbegin = dsp_chainsize;
+
+ if (blk && (reblock || switched)) /* add the block DSP prolog */
+ dsp_add(block_prolog, 1, blk);
+
+ /* Initialize for sorting */
+ for (u = dc->dc_ugenlist; u; u = u->u_next)
+ {
+ u->u_done = 0;
+ for (uout = u->u_out, i = u->u_nout; i--; uout++)
+ uout->o_nsent = 0;
+ for (uin = u->u_in, i = u->u_nin; i--; uin++)
+ uin->i_ngot = 0, uin->i_signal = 0;
+ }
+
+ /* Do the sort */
+
+ for (u = dc->dc_ugenlist; u; u = u->u_next)
+ {
+ /* check that we have no connected signal inlets */
+ if (u->u_done) continue;
+ for (uin = u->u_in, i = u->u_nin; i--; uin++)
+ if (uin->i_nconnect) goto next;
+
+ ugen_doit(dc, u);
+ next: ;
+ }
+
+ if (blk && (reblock || switched)) /* add block DSP epilog */
+ dsp_add(block_epilog, 1, blk);
+ chainblockend = dsp_chainsize;
+
+ /* add epilogs for outlets. */
+
+ for (u = dc->dc_ugenlist; u; u = u->u_next)
+ {
+ t_pd *zz = &u->u_obj->ob_pd;
+ if (pd_class(zz) == voutlet_class)
+ {
+ t_signal **iosigs = dc->dc_iosigs;
+ if (iosigs) iosigs += dc->dc_ninlets;
+ voutlet_dspepilog((struct _voutlet *)zz,
+ iosigs, vecsize, dsp_phase, period, frequency,
+ downsample, upsample, /* IOhannes */
+ reblock, switched);
+ }
+ }
+
+ chainafterall = dsp_chainsize;
+ if (blk)
+ {
+ blk->x_blocklength = chainblockend - chainblockbegin;
+ blk->x_epiloglength = chainafterall - chainblockend;
+ blk->x_reblock = reblock;
+ }
+
+ if (ugen_loud)
+ {
+ t_int *ip;
+ if (!dc->dc_parentcontext)
+ for (i = dsp_chainsize, ip = dsp_chain; i--; ip++)
+ post("chain %x", *ip);
+ post("... ugen_done_graph done.");
+ }
+ /* now delete everything. */
+ while (dc->dc_ugenlist)
+ {
+ for (uout = dc->dc_ugenlist->u_out, n = dc->dc_ugenlist->u_nout;
+ n--; uout++)
+ {
+ oc = uout->o_connections;
+ while (oc)
+ {
+ oc2 = oc->oc_next;
+ freebytes(oc, sizeof *oc);
+ oc = oc2;
+ }
+ }
+ freebytes(dc->dc_ugenlist->u_out, dc->dc_ugenlist->u_nout *
+ sizeof (*dc->dc_ugenlist->u_out));
+ freebytes(dc->dc_ugenlist->u_in, dc->dc_ugenlist->u_nin *
+ sizeof(*dc->dc_ugenlist->u_in));
+ u = dc->dc_ugenlist;
+ dc->dc_ugenlist = u->u_next;
+ freebytes(u, sizeof *u);
+ }
+ if (ugen_currentcontext == dc)
+ ugen_currentcontext = dc->dc_parentcontext;
+ else bug("ugen_currentcontext");
+ freebytes(dc, sizeof(*dc));
+
+}
+
+t_signal *ugen_getiosig(int index, int inout)
+{
+ if (!ugen_currentcontext) bug("ugen_getiosig");
+ if (ugen_currentcontext->dc_toplevel) return (0);
+ if (inout) index += ugen_currentcontext->dc_ninlets;
+ return (ugen_currentcontext->dc_iosigs[index]);
+}
+
+
+/* -------------------- setup routine -------------------------- */
+
+void d_ugen_setup(void) /* really just block_setup */
+{
+ block_class = class_new(gensym("block~"), (t_newmethod)block_new, 0,
+ sizeof(t_block), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT/*IOhannes*/, 0);
+ class_addcreator((t_newmethod)switch_new, gensym("switch~"),
+ A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT/*IOhannes*/, 0);
+ class_addmethod(block_class, (t_method)block_dsp, gensym("dsp"), 0);
+ class_addfloat(block_class, block_float);
+}
+
diff --git a/pd/src/g_all_guis.c b/pd/src/g_all_guis.c
new file mode 100644
index 00000000..5bbf2f4a
--- /dev/null
+++ b/pd/src/g_all_guis.c
@@ -0,0 +1,937 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */
+/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include "t_tk.h"
+#include "g_all_guis.h"
+#include <math.h>
+
+#ifdef NT
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+/* #define GGEE_HSLIDER_COMPATIBLE */
+
+/*------------------ global varaibles -------------------------*/
+
+t_symbol *iemgui_key_sym=0;
+
+int iemgui_color_hex[]=
+{
+ 16579836, 10526880, 4210752, 16572640, 16572608,
+ 16579784, 14220504, 14220540, 14476540, 16308476,
+ 14737632, 8158332, 2105376, 16525352, 16559172,
+ 15263784, 1370132, 2684148, 3952892, 16003312,
+ 12369084, 6316128, 0, 9177096, 5779456,
+ 7874580, 2641940, 17488, 5256, 5767248
+};
+
+int iemgui_vu_db2i[]=
+{
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 9, 9, 9, 9, 9,10,10,10,10,10,
+ 11,11,11,11,11,12,12,12,12,12,
+ 13,13,13,13,14,14,14,14,15,15,
+ 15,15,16,16,16,16,17,17,17,18,
+ 18,18,19,19,19,20,20,20,21,21,
+ 22,22,23,23,24,24,25,26,27,28,
+ 29,30,31,32,33,33,34,34,35,35,
+ 36,36,37,37,37,38,38,38,39,39,
+ 39,39,39,39,40,40
+};
+
+int iemgui_vu_col[]=
+{
+ 0,17,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+ 15,15,15,15,15,15,15,15,15,15,14,14,13,13,13,13,13,13,13,13,13,13,13,19,19,19
+};
+
+char *iemgui_vu_scale_str[]=
+{
+ "",
+ "<-99",
+ "",
+ "",
+ "",
+ "-50",
+ "",
+ "",
+ "",
+ "-30",
+ "",
+ "",
+ "",
+ "-20",
+ "",
+ "",
+ "",
+ "-12",
+ "",
+ "",
+ "",
+ "-6",
+ "",
+ "",
+ "",
+ "-2",
+ "",
+ "",
+ "",
+ "-0dB",
+ "",
+ "",
+ "",
+ "+2",
+ "",
+ "",
+ "",
+ "+6",
+ "",
+ "",
+ "",
+ ">+12",
+ "",
+ "",
+ "",
+ "",
+ "",
+};
+
+
+/*------------------ global functions -------------------------*/
+
+
+int iemgui_clip_size(int size)
+{
+ if(size < IEM_GUI_MINSIZE)
+ size = IEM_GUI_MINSIZE;
+ return(size);
+}
+
+int iemgui_clip_font(int size)
+{
+ if(size < IEM_FONT_MINSIZE)
+ size = IEM_FONT_MINSIZE;
+ return(size);
+}
+
+int iemgui_modulo_color(int col)
+{
+ while(col >= IEM_GUI_MAX_COLOR)
+ col -= IEM_GUI_MAX_COLOR;
+ while(col < 0)
+ col += IEM_GUI_MAX_COLOR;
+ return(col);
+}
+
+void iemgui_raute2dollar(t_symbol *s)
+{
+ if(s->s_name[0] == '#')
+ s->s_name[0] = '$';
+}
+
+void iemgui_dollar2raute(t_symbol *s)
+{
+ if(s->s_name[0] == '$')
+ s->s_name[0] = '#';
+}
+
+t_symbol *iemgui_unique2dollarzero(t_symbol *s, int unique_num, int and_unique_flag)
+{
+ if(and_unique_flag)
+ {
+ int l=0;
+ char *b, str[144];
+
+ sprintf(str, "%d", unique_num);
+ while(str[l])
+ {
+ if(s->s_name[l] != str[l])
+ return(s);
+ else
+ l++;
+ }
+ str[0] = '$';
+ str[1] = '0';
+ str[2] = 0;
+ b = s->s_name + l;
+ if(strlen(b) >= IEM_MAX_SYM_LEN)
+ strncat(str, b, IEM_MAX_SYM_LEN-1);
+ else
+ strcat(str, b);
+ return(gensym(str));
+ }
+ else
+ return(s);
+}
+
+t_symbol *iemgui_sym2dollararg(t_symbol *s, int nth_arg, int tail_len)
+{
+ if(nth_arg)
+ {
+ char *b, str[144];
+ int i=(int)strlen(s->s_name) - tail_len;
+
+ sprintf(str, "_%d", nth_arg);
+ str[0] = '$';
+ if(i < 0) i = 0;
+ b = s->s_name + i;
+ strcat(str, b);
+ return(gensym(str));
+ }
+ else
+ return(s);
+}
+
+t_symbol *iemgui_dollarzero2unique(t_symbol *s, int unique_num)
+{
+ int l;
+ char *b, str[144];
+
+ sprintf(str, "%d", unique_num);
+ l = (int)strlen(s->s_name);
+ b = s->s_name + 2;
+ if(l < 2)
+ strcat(str, "shorty");
+ else if(l >= IEM_MAX_SYM_LEN)
+ strncat(str, b, IEM_MAX_SYM_LEN-1);
+ else
+ strcat(str, b);
+ return(gensym(str));
+}
+
+t_symbol *iemgui_dollararg2sym(t_symbol *s, int nth_arg, int tail_len, int pargc, t_atom *pargv)
+{
+ int l=(int)strlen(s->s_name);
+ char *b, str[288]="0";
+ t_symbol *s2;
+
+ if(pargc <= 0){}
+ else if(nth_arg < 1){}
+ else if(nth_arg > pargc){}
+ else if(IS_A_FLOAT(pargv, nth_arg-1))
+ sprintf(str, "%d", atom_getintarg(nth_arg-1, pargc, pargv));
+ else if(IS_A_SYMBOL(pargv, nth_arg-1))
+ {
+ s2 = atom_getsymbolarg(nth_arg-1, pargc, pargv);
+ strcpy(str, s2->s_name);
+ }
+ b = s->s_name + (l - tail_len);
+ if(l <= tail_len)
+ strcat(str, "shorty");
+ else if(l >= IEM_MAX_SYM_LEN)
+ strncat(str, b, IEM_MAX_SYM_LEN-1);
+ else
+ strcat(str, b);
+ return(gensym(str));
+}
+
+int iemgui_is_dollarzero(t_symbol *s)
+{
+ char *name=s->s_name;
+
+ if((int)strlen(name) >= 2)
+ {
+ if((name[0] == '$') && (name[1] == '0') && ((name[2] < '0') || (name[2] > '9')))
+ return(1);
+ }
+ return(0);
+}
+
+int iemgui_is_dollararg(t_symbol *s, int *tail_len)
+{
+ char *name=s->s_name;
+
+ *tail_len = (int)strlen(name);
+ if(*tail_len >= 2)
+ {
+ if((name[0] == '$') && (name[1] >= '1') && (name[1] <= '9'))
+ {
+ int i=2, arg=(int)(name[1]-'0');
+
+ (*tail_len) -= 2;
+ while(name[i] && (name[i] >= '0') && (name[i] <= '9'))
+ {
+ arg *= 10;
+ arg += (int)(name[i]-'0');
+ i++;
+ (*tail_len)--;
+ }
+ return(arg);
+ }
+ }
+ return(0);
+}
+
+void iemgui_fetch_unique(t_iemgui *iemgui)
+{
+ if(!iemgui->x_unique_num)
+ {
+ pd_bind(&iemgui->x_glist->gl_gobj.g_pd, gensym("#X"));
+ iemgui->x_unique_num = canvas_getdollarzero();
+ pd_unbind(&iemgui->x_glist->gl_gobj.g_pd, gensym("#X"));
+ }
+}
+
+void iemgui_fetch_parent_args(t_iemgui *iemgui, int *pargc, t_atom **pargv)
+{
+ t_canvas *canvas=glist_getcanvas(iemgui->x_glist);
+
+ canvas_setcurrent(canvas);
+ canvas_getargs(pargc, pargv);
+ canvas_unsetcurrent(canvas);
+}
+
+void iemgui_verify_snd_ne_rcv(t_iemgui *iemgui)
+{
+ iemgui->x_fsf.x_put_in2out = 1;
+ if(iemgui->x_fsf.x_snd_able && iemgui->x_fsf.x_rcv_able)
+ {
+ if(!strcmp(iemgui->x_snd->s_name, iemgui->x_rcv->s_name))
+ iemgui->x_fsf.x_put_in2out = 0;
+ }
+}
+
+void iemgui_all_unique2dollarzero(t_iemgui *iemgui, t_symbol **srlsym)
+{
+ iemgui_fetch_unique(iemgui);
+ srlsym[0] = iemgui_unique2dollarzero(srlsym[0], iemgui->x_unique_num,
+ iemgui->x_fsf.x_snd_is_unique);
+ srlsym[1] = iemgui_unique2dollarzero(srlsym[1], iemgui->x_unique_num,
+ iemgui->x_fsf.x_rcv_is_unique);
+ srlsym[2] = iemgui_unique2dollarzero(srlsym[2], iemgui->x_unique_num,
+ iemgui->x_fsf.x_lab_is_unique);
+}
+
+void iemgui_all_sym2dollararg(t_iemgui *iemgui, t_symbol **srlsym)
+{
+ srlsym[0] = iemgui_sym2dollararg(srlsym[0], iemgui->x_isa.x_snd_is_arg_num,
+ iemgui->x_isa.x_snd_arg_tail_len);
+ srlsym[1] = iemgui_sym2dollararg(srlsym[1], iemgui->x_isa.x_rcv_is_arg_num,
+ iemgui->x_isa.x_rcv_arg_tail_len);
+ srlsym[2] = iemgui_sym2dollararg(srlsym[2], iemgui->x_fsf.x_lab_is_arg_num,
+ iemgui->x_fsf.x_lab_arg_tail_len);
+}
+
+void iemgui_all_dollarzero2unique(t_iemgui *iemgui, t_symbol **srlsym)
+{
+ iemgui_fetch_unique(iemgui);
+ if(iemgui_is_dollarzero(srlsym[0]))
+ {
+ iemgui->x_fsf.x_snd_is_unique = 1;
+ iemgui->x_isa.x_snd_is_arg_num = 0;
+ iemgui->x_isa.x_snd_arg_tail_len = 0;
+ srlsym[0] = iemgui_dollarzero2unique(srlsym[0], iemgui->x_unique_num);
+ }
+ else
+ iemgui->x_fsf.x_snd_is_unique = 0;
+ if(iemgui_is_dollarzero(srlsym[1]))
+ {
+ iemgui->x_fsf.x_rcv_is_unique = 1;
+ iemgui->x_isa.x_rcv_is_arg_num = 0;
+ iemgui->x_isa.x_rcv_arg_tail_len = 0;
+ srlsym[1] = iemgui_dollarzero2unique(srlsym[1], iemgui->x_unique_num);
+ }
+ else
+ iemgui->x_fsf.x_rcv_is_unique = 0;
+ if(iemgui_is_dollarzero(srlsym[2]))
+ {
+ iemgui->x_fsf.x_lab_is_unique = 1;
+ iemgui->x_fsf.x_lab_is_arg_num = 0;
+ iemgui->x_fsf.x_lab_arg_tail_len = 0;
+ srlsym[2] = iemgui_dollarzero2unique(srlsym[2], iemgui->x_unique_num);
+ }
+ else
+ iemgui->x_fsf.x_lab_is_unique = 0;
+}
+
+void iemgui_all_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym)
+{
+ int pargc, tail_len, nth_arg;
+ t_atom *pargv;
+
+ iemgui_fetch_parent_args(iemgui, &pargc, &pargv);
+ if(nth_arg = iemgui_is_dollararg(srlsym[0], &tail_len))
+ {
+ iemgui->x_isa.x_snd_is_arg_num = nth_arg;
+ iemgui->x_isa.x_snd_arg_tail_len = tail_len;
+ iemgui->x_fsf.x_snd_is_unique = 0;
+ srlsym[0] = iemgui_dollararg2sym(srlsym[0], nth_arg, tail_len, pargc, pargv);
+ }
+ else
+ {
+ iemgui->x_isa.x_snd_is_arg_num = 0;
+ iemgui->x_isa.x_snd_arg_tail_len = 0;
+ }
+ if(nth_arg = iemgui_is_dollararg(srlsym[1], &tail_len))
+ {
+ iemgui->x_isa.x_rcv_is_arg_num = nth_arg;
+ iemgui->x_isa.x_rcv_arg_tail_len = tail_len;
+ iemgui->x_fsf.x_rcv_is_unique = 0;
+ srlsym[1] = iemgui_dollararg2sym(srlsym[1], nth_arg, tail_len, pargc, pargv);
+ }
+ else
+ {
+ iemgui->x_isa.x_rcv_is_arg_num = 0;
+ iemgui->x_isa.x_rcv_arg_tail_len = 0;
+ }
+ if(nth_arg = iemgui_is_dollararg(srlsym[2], &tail_len))
+ {
+ iemgui->x_fsf.x_lab_is_arg_num = nth_arg;
+ iemgui->x_fsf.x_lab_arg_tail_len = tail_len;
+ iemgui->x_fsf.x_lab_is_unique = 0;
+ srlsym[2] = iemgui_dollararg2sym(srlsym[2], nth_arg, tail_len, pargc, pargv);
+ }
+ else
+ {
+ iemgui->x_fsf.x_lab_is_arg_num = 0;
+ iemgui->x_fsf.x_lab_arg_tail_len = 0;
+ }
+}
+
+void iemgui_first_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym)
+{
+ int pargc=0, tail_len, nth_arg;
+ t_atom pargv;
+ char *name;
+
+ SETFLOAT(&pargv, 0.0);
+ name = srlsym[0]->s_name;
+ if(iemgui->x_isa.x_snd_is_arg_num && (name[0] == '$')
+ && (name[1] >= '1') && (name[1] <= '9'))
+ {
+ srlsym[0] = iemgui_dollararg2sym(srlsym[0], iemgui->x_isa.x_snd_is_arg_num,
+ iemgui->x_isa.x_snd_arg_tail_len, pargc, &pargv);
+ }
+ name = srlsym[1]->s_name;
+ if(iemgui->x_isa.x_rcv_is_arg_num && (name[0] == '$')
+ && (name[1] >= '1') && (name[1] <= '9'))
+ {
+ srlsym[1] = iemgui_dollararg2sym(srlsym[1], iemgui->x_isa.x_rcv_is_arg_num,
+ iemgui->x_isa.x_rcv_arg_tail_len, pargc, &pargv);
+ }
+ name = srlsym[2]->s_name;
+ if(iemgui->x_fsf.x_lab_is_arg_num && (name[0] == '$')
+ && (name[1] >= '1') && (name[1] <= '9'))
+ {
+ srlsym[2] = iemgui_dollararg2sym(srlsym[2], iemgui->x_fsf.x_lab_is_arg_num,
+ iemgui->x_fsf.x_lab_arg_tail_len, pargc, &pargv);
+ }
+}
+
+void iemgui_all_col2save(t_iemgui *iemgui, int *bflcol)
+{
+ bflcol[0] = -1 - (((0xfc0000 & iemgui->x_bcol) >> 6)|
+ ((0xfc00 & iemgui->x_bcol) >> 4)|((0xfc & iemgui->x_bcol) >> 2));
+ bflcol[1] = -1 - (((0xfc0000 & iemgui->x_fcol) >> 6)|
+ ((0xfc00 & iemgui->x_fcol) >> 4)|((0xfc & iemgui->x_fcol) >> 2));
+ bflcol[2] = -1 - (((0xfc0000 & iemgui->x_lcol) >> 6)|
+ ((0xfc00 & iemgui->x_lcol) >> 4)|((0xfc & iemgui->x_lcol) >> 2));
+}
+
+void iemgui_all_colfromload(t_iemgui *iemgui, int *bflcol)
+{
+ if(bflcol[0] < 0)
+ {
+ bflcol[0] = -1 - bflcol[0];
+ iemgui->x_bcol = ((bflcol[0] & 0x3f000) << 6)|((bflcol[0] & 0xfc0) << 4)|
+ ((bflcol[0] & 0x3f) << 2);
+ }
+ else
+ {
+ bflcol[0] = iemgui_modulo_color(bflcol[0]);
+ iemgui->x_bcol = iemgui_color_hex[bflcol[0]];
+ }
+ if(bflcol[1] < 0)
+ {
+ bflcol[1] = -1 - bflcol[1];
+ iemgui->x_fcol = ((bflcol[1] & 0x3f000) << 6)|((bflcol[1] & 0xfc0) << 4)|
+ ((bflcol[1] & 0x3f) << 2);
+ }
+ else
+ {
+ bflcol[1] = iemgui_modulo_color(bflcol[1]);
+ iemgui->x_fcol = iemgui_color_hex[bflcol[1]];
+ }
+ if(bflcol[2] < 0)
+ {
+ bflcol[2] = -1 - bflcol[2];
+ iemgui->x_lcol = ((bflcol[2] & 0x3f000) << 6)|((bflcol[2] & 0xfc0) << 4)|
+ ((bflcol[2] & 0x3f) << 2);
+ }
+ else
+ {
+ bflcol[2] = iemgui_modulo_color(bflcol[2]);
+ iemgui->x_lcol = iemgui_color_hex[bflcol[2]];
+ }
+}
+
+int iemgui_compatible_col(int i)
+{
+ int j;
+
+ if(i >= 0)
+ {
+ j = iemgui_modulo_color(i);
+ return(iemgui_color_hex[(j)]);
+ }
+ else
+ return((-1 -i)&0xffffff);
+}
+
+void iemgui_all_dollar2raute(t_symbol **srlsym)
+{
+ if(srlsym[0]->s_name[0] == '$')
+ srlsym[0]->s_name[0] = '#';
+ if(srlsym[1]->s_name[0] == '$')
+ srlsym[1]->s_name[0] = '#';
+ if(srlsym[2]->s_name[0] == '$')
+ srlsym[2]->s_name[0] = '#';
+}
+
+void iemgui_all_raute2dollar(t_symbol **srlsym)
+{
+ if(srlsym[0]->s_name[0] == '#')
+ srlsym[0]->s_name[0] = '$';
+ if(srlsym[1]->s_name[0] == '#')
+ srlsym[1]->s_name[0] = '$';
+ if(srlsym[2]->s_name[0] == '#')
+ srlsym[2]->s_name[0] = '$';
+}
+
+void iemgui_send(void *x, t_iemgui *iemgui, t_symbol *s)
+{
+ t_symbol *snd;
+ int pargc, tail_len, nth_arg, sndable=1, oldsndrcvable=0;
+ t_atom *pargv;
+
+ if(iemgui->x_fsf.x_rcv_able)
+ oldsndrcvable += IEM_GUI_OLD_RCV_FLAG;
+ if(iemgui->x_fsf.x_snd_able)
+ oldsndrcvable += IEM_GUI_OLD_SND_FLAG;
+
+ if(!strcmp(s->s_name, "empty")) sndable = 0;
+ iemgui_raute2dollar(s);
+ iemgui_fetch_unique(iemgui);
+ snd = s;
+ if(iemgui_is_dollarzero(snd))
+ {
+ iemgui->x_fsf.x_snd_is_unique = 1;
+ iemgui->x_isa.x_snd_is_arg_num = 0;
+ iemgui->x_isa.x_snd_arg_tail_len = 0;
+ snd = iemgui_dollarzero2unique(snd, iemgui->x_unique_num);
+ }
+ else
+ iemgui->x_fsf.x_snd_is_unique = 0;
+ iemgui_fetch_parent_args(iemgui, &pargc, &pargv);
+ if(nth_arg = iemgui_is_dollararg(snd, &tail_len))
+ {
+ iemgui->x_isa.x_snd_is_arg_num = nth_arg;
+ iemgui->x_isa.x_snd_arg_tail_len = tail_len;
+ iemgui->x_fsf.x_snd_is_unique = 0;
+ snd = iemgui_dollararg2sym(snd, nth_arg, tail_len, pargc, pargv);
+ }
+ else
+ {
+ iemgui->x_isa.x_snd_is_arg_num = 0;
+ iemgui->x_isa.x_snd_arg_tail_len = 0;
+ }
+ iemgui->x_snd = snd;
+ iemgui->x_fsf.x_snd_able = sndable;
+ iemgui_verify_snd_ne_rcv(iemgui);
+ (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_IO + oldsndrcvable);
+}
+
+void iemgui_receive(void *x, t_iemgui *iemgui, t_symbol *s)
+{
+ t_symbol *rcv;
+ int pargc, tail_len, nth_arg, rcvable=1, oldsndrcvable=0;
+ t_atom *pargv;
+
+ if(iemgui->x_fsf.x_rcv_able)
+ oldsndrcvable += IEM_GUI_OLD_RCV_FLAG;
+ if(iemgui->x_fsf.x_snd_able)
+ oldsndrcvable += IEM_GUI_OLD_SND_FLAG;
+
+ if(!strcmp(s->s_name, "empty")) rcvable = 0;
+ iemgui_raute2dollar(s);
+ rcv = s;
+ iemgui_fetch_unique(iemgui);
+ if(iemgui_is_dollarzero(rcv))
+ {
+ iemgui->x_fsf.x_rcv_is_unique = 1;
+ iemgui->x_isa.x_rcv_is_arg_num = 0;
+ iemgui->x_isa.x_rcv_arg_tail_len = 0;
+ rcv = iemgui_dollarzero2unique(rcv, iemgui->x_unique_num);
+ }
+ else
+ iemgui->x_fsf.x_rcv_is_unique = 0;
+ iemgui_fetch_parent_args(iemgui, &pargc, &pargv);
+ if(nth_arg = iemgui_is_dollararg(rcv, &tail_len))
+ {
+ iemgui->x_isa.x_rcv_is_arg_num = nth_arg;
+ iemgui->x_isa.x_rcv_arg_tail_len = tail_len;
+ iemgui->x_fsf.x_rcv_is_unique = 0;
+ rcv = iemgui_dollararg2sym(rcv, nth_arg, tail_len, pargc, pargv);
+ }
+ else
+ {
+ iemgui->x_isa.x_rcv_is_arg_num = 0;
+ iemgui->x_isa.x_rcv_arg_tail_len = 0;
+ }
+ if(rcvable)
+ {
+ if(strcmp(rcv->s_name, iemgui->x_rcv->s_name))
+ {
+ if(iemgui->x_fsf.x_rcv_able)
+ pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv);
+ iemgui->x_rcv = rcv;
+ pd_bind(&iemgui->x_obj.ob_pd, iemgui->x_rcv);
+ }
+ }
+ else if(!rcvable && iemgui->x_fsf.x_rcv_able)
+ {
+ pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv);
+ iemgui->x_rcv = rcv;
+ }
+ iemgui->x_fsf.x_rcv_able = rcvable;
+ iemgui_verify_snd_ne_rcv(iemgui);
+ (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_IO + oldsndrcvable);
+}
+
+void iemgui_label(void *x, t_iemgui *iemgui, t_symbol *s)
+{
+ t_symbol *lab;
+ int pargc, tail_len, nth_arg;
+ t_atom *pargv;
+
+ iemgui_raute2dollar(s);
+ lab = s;
+ iemgui_fetch_unique(iemgui);
+
+ if(iemgui_is_dollarzero(lab))
+ {
+ iemgui->x_fsf.x_lab_is_unique = 1;
+ iemgui->x_fsf.x_lab_is_arg_num = 0;
+ iemgui->x_fsf.x_lab_arg_tail_len = 0;
+ lab = iemgui_dollarzero2unique(lab, iemgui->x_unique_num);
+ }
+ else
+ iemgui->x_fsf.x_lab_is_unique = 0;
+
+ iemgui_fetch_parent_args(iemgui, &pargc, &pargv);
+ if(nth_arg = iemgui_is_dollararg(lab, &tail_len))
+ {
+ iemgui->x_fsf.x_lab_is_arg_num = nth_arg;
+ iemgui->x_fsf.x_lab_arg_tail_len = tail_len;
+ iemgui->x_fsf.x_lab_is_unique = 0;
+ lab = iemgui_dollararg2sym(lab, nth_arg, tail_len, pargc, pargv);
+ }
+ else
+ {
+ iemgui->x_fsf.x_lab_is_arg_num = 0;
+ iemgui->x_fsf.x_lab_arg_tail_len = 0;
+ }
+ iemgui->x_lab = lab;
+ if(glist_isvisible(iemgui->x_glist))
+ sys_vgui(".x%x.c itemconfigure %xLABEL -text {%s} \n",
+ glist_getcanvas(iemgui->x_glist), x,
+ strcmp(s->s_name, "empty")?iemgui->x_lab->s_name:"");
+}
+
+void iemgui_label_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av)
+{
+ iemgui->x_ldx = (int)atom_getintarg(0, ac, av);
+ iemgui->x_ldy = (int)atom_getintarg(1, ac, av);
+ if(glist_isvisible(iemgui->x_glist))
+ sys_vgui(".x%x.c coords %xLABEL %d %d\n",
+ glist_getcanvas(iemgui->x_glist), x,
+ iemgui->x_obj.te_xpix+iemgui->x_ldx,
+ iemgui->x_obj.te_ypix+iemgui->x_ldy);
+}
+
+void iemgui_label_font(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av)
+{
+ int f = (int)atom_getintarg(0, ac, av);
+
+ if(f == 1) strcpy(iemgui->x_font, "helvetica");
+ else if(f == 2) strcpy(iemgui->x_font, "times");
+ else
+ {
+ f = 0;
+ strcpy(iemgui->x_font, "courier");
+ }
+ iemgui->x_fsf.x_font_style = f;
+ f = (int)atom_getintarg(1, ac, av);
+ if(f < 4)
+ f = 4;
+ iemgui->x_fontsize = f;
+ if(glist_isvisible(iemgui->x_glist))
+ sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold}\n",
+ glist_getcanvas(iemgui->x_glist), x, iemgui->x_font, iemgui->x_fontsize);
+}
+
+void iemgui_size(void *x, t_iemgui *iemgui)
+{
+ if(glist_isvisible(iemgui->x_glist))
+ {
+ (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(iemgui->x_glist), (t_text*)x);
+ }
+}
+
+void iemgui_delta(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av)
+{
+ iemgui->x_obj.te_xpix += (int)atom_getintarg(0, ac, av);
+ iemgui->x_obj.te_ypix += (int)atom_getintarg(1, ac, av);
+ if(glist_isvisible(iemgui->x_glist))
+ {
+ (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(iemgui->x_glist), (t_text*)x);
+ }
+}
+
+void iemgui_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av)
+{
+ iemgui->x_obj.te_xpix = (int)atom_getintarg(0, ac, av);
+ iemgui->x_obj.te_ypix = (int)atom_getintarg(1, ac, av);
+ if(glist_isvisible(iemgui->x_glist))
+ {
+ (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(iemgui->x_glist), (t_text*)x);
+ }
+}
+
+void iemgui_color(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av)
+{
+ iemgui->x_bcol = iemgui_compatible_col(atom_getintarg(0, ac, av));
+ if(ac > 2)
+ {
+ iemgui->x_fcol = iemgui_compatible_col(atom_getintarg(1, ac, av));
+ iemgui->x_lcol = iemgui_compatible_col(atom_getintarg(2, ac, av));
+ }
+ else
+ iemgui->x_lcol = iemgui_compatible_col(atom_getintarg(1, ac, av));
+ if(glist_isvisible(iemgui->x_glist))
+ (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_CONFIG);
+}
+
+int iemgui_list(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av)
+{
+ if(iemgui->x_fsf.x_selected)
+ {
+ if((ac == 2)&&IS_A_FLOAT(av,0)&&IS_A_SYMBOL(av,1))
+ {
+ t_symbol *key = atom_getsymbolarg(1, ac, av);
+ int keydown = atom_getintarg(0, ac, av);
+
+ if(keydown)
+ {
+ int refresh = 1,i,d=1;
+ static char buf[20];
+
+ buf[0] = 0;
+ if(!strcmp(key->s_name, "Shift_L")||!strcmp(key->s_name, "Shift_R"))
+ iemgui->x_fsf.x_shiftdown = 1;
+ else
+ {
+ if(iemgui->x_fsf.x_shiftdown)
+ d = 10;
+ if(!strcmp(key->s_name, "Up"))
+ iemgui->x_obj.te_ypix -= d;
+ else if(!strcmp(key->s_name, "Down"))
+ iemgui->x_obj.te_ypix += d;
+ else if(!strcmp(key->s_name, "Left"))
+ iemgui->x_obj.te_xpix -= d;
+ else if(!strcmp(key->s_name, "Right"))
+ iemgui->x_obj.te_xpix += d;
+ else
+ refresh = 0;
+ if(refresh)
+ return(1);
+ }
+ return(0);
+ }
+ else
+ {
+ if(!strcmp(key->s_name, "Shift_L")||!strcmp(key->s_name, "Shift_R"))
+ iemgui->x_fsf.x_shiftdown = 0;
+ return(0);
+ }
+ }
+ else
+ return(-1);
+ }
+ else
+ return(-1);
+}
+
+void iemgui_displace(t_gobj *z, t_glist *glist, int dx, int dy)
+{
+ t_iemguidummy *x = (t_iemguidummy *)z;
+
+ x->x_gui.x_obj.te_xpix += dx;
+ x->x_gui.x_obj.te_ypix += dy;
+ (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(glist), (t_text *)z);
+}
+
+void iemgui_select(t_gobj *z, t_glist *glist, int selected)
+{
+ t_iemguidummy *x = (t_iemguidummy *)z;
+
+ x->x_gui.x_fsf.x_selected = selected;
+ (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_SELECT);
+}
+
+void iemgui_delete(t_gobj *z, t_glist *glist)
+{
+ canvas_deletelinesfor(glist, (t_text *)z);
+}
+
+void iemgui_vis(t_gobj *z, t_glist *glist, int vis)
+{
+ t_iemguidummy *x = (t_iemguidummy *)z;
+ t_rtext *y;
+
+ if(vis)
+ {
+ y = rtext_new_without_senditup(glist, (t_text *)z, glist->gl_editor->e_rtext);
+ (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_NEW);
+ }
+ else
+ {
+ y = glist_findrtext(glist, (t_text *)z);
+ (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_ERASE);
+ rtext_free(y);
+ }
+}
+
+void iemgui_save(t_iemgui *iemgui, t_symbol **srl, int *bflcol)
+{
+ srl[0] = iemgui->x_snd;
+ srl[1] = iemgui->x_rcv;
+ srl[2] = iemgui->x_lab;
+ iemgui_all_unique2dollarzero(iemgui, srl);
+ iemgui_all_sym2dollararg(iemgui, srl);
+ iemgui_all_col2save(iemgui, bflcol);
+}
+
+void iemgui_properties(t_iemgui *iemgui, t_symbol **srl)
+{
+ srl[0] = iemgui->x_snd;
+ srl[1] = iemgui->x_rcv;
+ srl[2] = iemgui->x_lab;
+ iemgui_all_unique2dollarzero(iemgui, srl);
+ iemgui_all_sym2dollararg(iemgui, srl);
+ iemgui_all_dollar2raute(srl);
+}
+
+int iemgui_dialog(t_iemgui *iemgui, t_symbol **srl, int argc, t_atom *argv)
+{
+ char str[144];
+ int init = (int)atom_getintarg(5, argc, argv);
+ int ldx = (int)atom_getintarg(10, argc, argv);
+ int ldy = (int)atom_getintarg(11, argc, argv);
+ int f = (int)atom_getintarg(12, argc, argv);
+ int fs = (int)atom_getintarg(13, argc, argv);
+ int bcol = (int)atom_getintarg(14, argc, argv);
+ int fcol = (int)atom_getintarg(15, argc, argv);
+ int lcol = (int)atom_getintarg(16, argc, argv);
+ int sndable=1, rcvable=1, oldsndrcvable=0;
+
+ if(iemgui->x_fsf.x_rcv_able)
+ oldsndrcvable += IEM_GUI_OLD_RCV_FLAG;
+ if(iemgui->x_fsf.x_snd_able)
+ oldsndrcvable += IEM_GUI_OLD_SND_FLAG;
+ if(IS_A_SYMBOL(argv,7))
+ srl[0] = atom_getsymbolarg(7, argc, argv);
+ else if(IS_A_FLOAT(argv,7))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(7, argc, argv));
+ srl[0] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,8))
+ srl[1] = atom_getsymbolarg(8, argc, argv);
+ else if(IS_A_FLOAT(argv,8))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(8, argc, argv));
+ srl[1] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,9))
+ srl[2] = atom_getsymbolarg(9, argc, argv);
+ else if(IS_A_FLOAT(argv,9))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(9, argc, argv));
+ srl[2] = gensym(str);
+ }
+ if(init != 0) init = 1;
+ iemgui->x_isa.x_loadinit = init;
+ if(!strcmp(srl[0]->s_name, "empty")) sndable = 0;
+ if(!strcmp(srl[1]->s_name, "empty")) rcvable = 0;
+ iemgui_all_raute2dollar(srl);
+ iemgui_all_dollarzero2unique(iemgui, srl);
+ iemgui_all_dollararg2sym(iemgui, srl);
+ if(rcvable)
+ {
+ if(strcmp(srl[1]->s_name, iemgui->x_rcv->s_name))
+ {
+ if(iemgui->x_fsf.x_rcv_able)
+ pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv);
+ iemgui->x_rcv = srl[1];
+ pd_bind(&iemgui->x_obj.ob_pd, iemgui->x_rcv);
+ }
+ }
+ else if(!rcvable && iemgui->x_fsf.x_rcv_able)
+ {
+ pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv);
+ iemgui->x_rcv = srl[1];
+ }
+ iemgui->x_snd = srl[0];
+ iemgui->x_fsf.x_snd_able = sndable;
+ iemgui->x_fsf.x_rcv_able = rcvable;
+ iemgui->x_lcol = lcol & 0xffffff;
+ iemgui->x_fcol = fcol & 0xffffff;
+ iemgui->x_bcol = bcol & 0xffffff;
+ iemgui->x_lab = srl[2];
+ iemgui->x_ldx = ldx;
+ iemgui->x_ldy = ldy;
+ if(f == 1) strcpy(iemgui->x_font, "helvetica");
+ else if(f == 2) strcpy(iemgui->x_font, "times");
+ else
+ {
+ f = 0;
+ strcpy(iemgui->x_font, "courier");
+ }
+ iemgui->x_fsf.x_font_style = f;
+ if(fs < 4)
+ fs = 4;
+ iemgui->x_fontsize = fs;
+ iemgui_verify_snd_ne_rcv(iemgui);
+ return(oldsndrcvable);
+}
diff --git a/pd/src/g_all_guis.h b/pd/src/g_all_guis.h
new file mode 100644
index 00000000..54f9cef4
--- /dev/null
+++ b/pd/src/g_all_guis.h
@@ -0,0 +1,320 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+/* g_7_guis.h written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */
+
+
+#define IEM_GUI_COLNR_WHITE 0
+#define IEM_GUI_COLNR_ML_GREY 1
+#define IEM_GUI_COLNR_D_GREY 2
+#define IEM_GUI_COLNR_L_RED 3
+#define IEM_GUI_COLNR_L_ORANGE 4
+#define IEM_GUI_COLNR_L_YELLOW 5
+#define IEM_GUI_COLNR_L_GREEN 6
+#define IEM_GUI_COLNR_L_CYAN 7
+#define IEM_GUI_COLNR_L_BLUE 8
+#define IEM_GUI_COLNR_L_MAGENTA 9
+
+#define IEM_GUI_COLNR_LL_GREY 10
+#define IEM_GUI_COLNR_M_GREY 11
+#define IEM_GUI_COLNR_DD_GREY 12
+#define IEM_GUI_COLNR_RED 13
+#define IEM_GUI_COLNR_ORANGE 14
+#define IEM_GUI_COLNR_YELLOW 15
+#define IEM_GUI_COLNR_GREEN 16
+#define IEM_GUI_COLNR_CYAN 17
+#define IEM_GUI_COLNR_BLUE 18
+#define IEM_GUI_COLNR_MAGENTA 19
+
+#define IEM_GUI_COLNR_L_GREY 20
+#define IEM_GUI_COLNR_MD_GREY 21
+#define IEM_GUI_COLNR_BLACK 22
+#define IEM_GUI_COLNR_D_RED 23
+#define IEM_GUI_COLNR_D_ORANGE 24
+#define IEM_GUI_COLNR_D_YELLOW 25
+#define IEM_GUI_COLNR_D_GREEN 26
+#define IEM_GUI_COLNR_D_CYAN 27
+#define IEM_GUI_COLNR_D_BLUE 28
+#define IEM_GUI_COLNR_D_MAGENTA 29
+
+#define IEM_GUI_COLOR_SELECTED 255
+#define IEM_GUI_COLOR_NORMAL 0
+
+#define IEM_GUI_MAX_COLOR 30
+
+#define IEM_GUI_DEFAULTSIZE 15
+#define IEM_GUI_MINSIZE 8
+#define IEM_GUI_MAXSIZE 1000
+#define IEM_SL_DEFAULTSIZE 128
+#define IEM_SL_MINSIZE 20
+#define IEM_FONT_MINSIZE 4
+
+#define IEM_BNG_DEFAULTHOLDFLASHTIME 250
+#define IEM_BNG_DEFAULTBREAKFLASHTIME 50
+#define IEM_BNG_MINHOLDFLASHTIME 50
+#define IEM_BNG_MINBREAKFLASHTIME 10
+
+#define IEM_VU_DEFAULTSIZE 3
+#define IEM_VU_LARGESMALL 2
+#define IEM_VU_MINSIZE 2
+#define IEM_VU_MAXSIZE 25
+#define IEM_VU_STEPS 40
+
+#define IEM_VU_MINDB -99.9
+#define IEM_VU_MAXDB 12.0
+#define IEM_VU_OFFSET 100.0
+
+#define IEM_RADIO_MAX 128
+
+#define IEM_SYM_UNIQUE_SND 256
+#define IEM_SYM_UNIQUE_RCV 512
+#define IEM_SYM_UNIQUE_LAB 1024
+#define IEM_SYM_UNIQUE_ALL 1792
+#define IEM_FONT_STYLE_ALL 255
+
+#define IEM_MAX_SYM_LEN 127
+
+#define IEM_GUI_DRAW_MODE_UPDATE 0
+#define IEM_GUI_DRAW_MODE_MOVE 1
+#define IEM_GUI_DRAW_MODE_NEW 2
+#define IEM_GUI_DRAW_MODE_SELECT 3
+#define IEM_GUI_DRAW_MODE_ERASE 4
+#define IEM_GUI_DRAW_MODE_CONFIG 5
+#define IEM_GUI_DRAW_MODE_IO 6
+
+
+#define IS_A_POINTER(atom,index) ((atom+index)->a_type == A_POINTER)
+#define IS_A_FLOAT(atom,index) ((atom+index)->a_type == A_FLOAT)
+#define IS_A_SYMBOL(atom,index) ((atom+index)->a_type == A_SYMBOL)
+#define IS_A_DOLLAR(atom,index) ((atom+index)->a_type == A_DOLLAR)
+#define IS_A_DOLLSYM(atom,index) ((atom+index)->a_type == A_DOLLSYM)
+
+#define IEM_FSTYLE_FLAGS_ALL 0x007fffff
+#define IEM_INIT_ARGS_ALL 0x01ffffff
+
+#define IEM_GUI_OLD_SND_FLAG 1
+#define IEM_GUI_OLD_RCV_FLAG 2
+
+#define IEM_GUI_COLOR_EDITED 16711680
+#define IEMGUI_MAX_NUM_LEN 32
+
+typedef struct _iem_fstyle_flags
+{
+ unsigned int x_font_style:6;
+ unsigned int x_rcv_able:1;
+ unsigned int x_snd_able:1;
+ unsigned int x_lab_is_unique:1;
+ unsigned int x_rcv_is_unique:1;
+ unsigned int x_snd_is_unique:1;
+ unsigned int x_lab_arg_tail_len:6;
+ unsigned int x_lab_is_arg_num:6;
+ unsigned int x_shiftdown:1;
+ unsigned int x_selected:1;
+ unsigned int x_finemoved:1;
+ unsigned int x_put_in2out:1;
+ unsigned int x_change:1;
+ unsigned int x_thick:1;
+ unsigned int x_lin0_log1:1;
+ unsigned int x_steady:1;
+ unsigned int dummy:1;
+} t_iem_fstyle_flags;
+
+typedef struct _iem_init_symargs
+{
+ unsigned int x_loadinit:1;
+ unsigned int x_rcv_arg_tail_len:6;
+ unsigned int x_snd_arg_tail_len:6;
+ unsigned int x_rcv_is_arg_num:6;
+ unsigned int x_snd_is_arg_num:6;
+ unsigned int x_scale:1;
+ unsigned int x_flashed:1;
+ unsigned int x_locked:1;
+ unsigned int x_reverse:1; /* bugfix */
+ unsigned int dummy:3;
+} t_iem_init_symargs;
+
+typedef void (*t_iemfunptr)(void *x, t_glist *glist, int mode);
+
+typedef struct _iemgui
+{
+ t_object x_obj;
+ t_glist *x_glist;
+ t_iemfunptr x_draw;
+ int x_h;
+ int x_w;
+ int x_ldx;
+ int x_ldy;
+ char x_font[16];
+ t_iem_fstyle_flags x_fsf;
+ int x_fontsize;
+ t_iem_init_symargs x_isa;
+ int x_fcol;
+ int x_bcol;
+ int x_lcol;
+ int x_unique_num;
+ t_symbol *x_snd;
+ t_symbol *x_rcv;
+ t_symbol *x_lab;
+} t_iemgui;
+
+typedef struct _iemguidummy
+{
+ t_iemgui x_gui;
+ int x_dum1;
+ int x_dum2;
+ int x_dum3;
+} t_iemguidummy;
+
+typedef struct _bng
+{
+ t_iemgui x_gui;
+ int x_flashed;
+ int x_flashtime_break;
+ int x_flashtime_hold;
+ t_clock *x_clock_hld;
+ t_clock *x_clock_brk;
+ t_clock *x_clock_lck;
+} t_bng;
+
+typedef struct _hslider
+{
+ t_iemgui x_gui;
+ int x_pos;
+ int x_val;
+ int x_center;
+ int x_thick;
+ int x_lin0_log1;
+ int x_steady;
+ double x_min;
+ double x_max;
+ double x_k;
+} t_hslider;
+
+typedef struct _hdial
+{
+ t_iemgui x_gui;
+ int x_on;
+ int x_on_old;
+ int x_change;
+ int x_number;
+ t_atom x_at[2];
+} t_hdial;
+
+typedef struct _toggle
+{
+ t_iemgui x_gui;
+ float x_on;
+ float x_nonzero;
+} t_toggle;
+
+typedef struct _my_canvas
+{
+ t_iemgui x_gui;
+ t_atom x_at[3];
+ int x_vis_w;
+ int x_vis_h;
+} t_my_canvas;
+
+typedef struct _vslider
+{
+ t_iemgui x_gui;
+ int x_pos;
+ int x_val;
+ int x_lin0_log1;
+ int x_steady;
+ double x_min;
+ double x_max;
+ double x_k;
+} t_vslider;
+
+typedef struct _vu
+{
+ t_iemgui x_gui;
+ int x_led_size;
+ int x_peak;
+ int x_rms;
+ float x_fp;
+ float x_fr;
+ int x_scale;
+ void *x_out_rms;
+ void *x_out_peak;
+} t_vu;
+
+typedef struct _my_numbox
+{
+ t_iemgui x_gui;
+ t_clock *x_clock_reset;
+ t_clock *x_clock_wait;
+ double x_val;
+ double x_min;
+ double x_max;
+ double x_k;
+ int x_lin0_log1;
+ char x_buf[IEMGUI_MAX_NUM_LEN];
+ int x_numwidth;
+ int x_log_height;
+} t_my_numbox;
+
+typedef struct _vdial
+{
+ t_iemgui x_gui;
+ int x_on;
+ int x_on_old;
+ int x_change;
+ int x_number;
+ t_atom x_at[2];
+} t_vdial;
+
+extern int sys_noloadbang;
+extern t_symbol *iemgui_key_sym;
+extern int iemgui_color_hex[];
+extern int iemgui_vu_db2i[];
+extern int iemgui_vu_col[];
+extern char *iemgui_vu_scale_str[];
+
+EXTERN int iemgui_clip_size(int size);
+EXTERN int iemgui_clip_font(int size);
+EXTERN int iemgui_modulo_color(int col);
+EXTERN void iemgui_raute2dollar(t_symbol *s);
+EXTERN void iemgui_dollar2raute(t_symbol *s);
+EXTERN t_symbol *iemgui_unique2dollarzero(t_symbol *s, int unique_num, int and_unique_flag);
+EXTERN t_symbol *iemgui_sym2dollararg(t_symbol *s, int nth_arg, int tail_len);
+EXTERN t_symbol *iemgui_dollarzero2unique(t_symbol *s, int unique_num);
+EXTERN t_symbol *iemgui_dollararg2sym(t_symbol *s, int nth_arg, int tail_len, int pargc, t_atom *pargv);
+EXTERN int iemgui_is_dollarzero(t_symbol *s);
+EXTERN int iemgui_is_dollararg(t_symbol *s, int *tail_len);
+EXTERN void iemgui_fetch_unique(t_iemgui *iemgui);
+EXTERN void iemgui_fetch_parent_args(t_iemgui *iemgui, int *pargc, t_atom **pargv);
+EXTERN void iemgui_verify_snd_ne_rcv(t_iemgui *iemgui);
+EXTERN void iemgui_all_unique2dollarzero(t_iemgui *iemgui, t_symbol **srlsym);
+EXTERN void iemgui_all_sym2dollararg(t_iemgui *iemgui, t_symbol **srlsym);
+EXTERN void iemgui_all_dollarzero2unique(t_iemgui *iemgui, t_symbol **srlsym);
+EXTERN void iemgui_all_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym);
+EXTERN void iemgui_first_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym);
+EXTERN void iemgui_all_col2save(t_iemgui *iemgui, int *bflcol);
+EXTERN void iemgui_all_colfromload(t_iemgui *iemgui, int *bflcol);
+EXTERN int iemgui_compatible_col(int i);
+EXTERN void iemgui_all_dollar2raute(t_symbol **srlsym);
+EXTERN void iemgui_all_raute2dollar(t_symbol **srlsym);
+EXTERN void iemgui_send(void *x, t_iemgui *iemgui, t_symbol *s);
+EXTERN void iemgui_receive(void *x, t_iemgui *iemgui, t_symbol *s);
+EXTERN void iemgui_label(void *x, t_iemgui *iemgui, t_symbol *s);
+EXTERN void iemgui_label_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av);
+EXTERN void iemgui_label_font(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av);
+EXTERN void iemgui_size(void *x, t_iemgui *iemgui);
+EXTERN void iemgui_delta(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av);
+EXTERN void iemgui_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av);
+EXTERN void iemgui_color(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av);
+EXTERN int iemgui_list(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av);
+EXTERN void iemgui_displace(t_gobj *z, t_glist *glist, int dx, int dy);
+EXTERN void iemgui_select(t_gobj *z, t_glist *glist, int selected);
+EXTERN void iemgui_delete(t_gobj *z, t_glist *glist);
+EXTERN void iemgui_vis(t_gobj *z, t_glist *glist, int vis);
+EXTERN void iemgui_save(t_iemgui *iemgui, t_symbol **srl, int *bflcol);
+EXTERN void iemgui_properties(t_iemgui *iemgui, t_symbol **srl);
+EXTERN int iemgui_dialog(t_iemgui *iemgui, t_symbol **srl, int argc, t_atom *argv);
+
+EXTERN t_rtext *rtext_new_without_senditup(t_glist *glist, t_text *who, t_rtext *next);
+EXTERN int canvas_getdollarzero(void);
+EXTERN void canvas_getargs(int *argcp, t_atom **argvp);
+
diff --git a/pd/src/g_array.c b/pd/src/g_array.c
new file mode 100644
index 00000000..2cdb3d8e
--- /dev/null
+++ b/pd/src/g_array.c
@@ -0,0 +1,1358 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h> /* for read/write to files */
+#include "m_pd.h"
+#include "g_canvas.h"
+#include <math.h>
+
+/* see also the "plot" object in g_scalar.c which deals with graphing
+arrays which are fields in scalars. Someday we should unify the
+two, but how? */
+
+ /* aux routine to bash leading '#' to '$' for dialogs in u_main.tk
+ which can't send symbols starting with '$' (because the Pd message
+ interpreter would change them!) */
+
+static t_symbol *sharptodollar(t_symbol *s)
+{
+ if (*s->s_name == '#')
+ {
+ char buf[MAXPDSTRING];
+ strncpy(buf, s->s_name, MAXPDSTRING);
+ buf[MAXPDSTRING-1] = 0;
+ buf[0] = '$';
+ return (gensym(buf));
+ }
+ else return (s);
+}
+
+/* --------- "pure" arrays with scalars for elements. --------------- */
+
+/* Pure arrays have no a priori graphical capabilities.
+They are instantiated by "garrays" below or can be elements of other
+scalars (g_scalar.c); their graphical behavior is defined accordingly. */
+
+t_array *array_new(t_symbol *templatesym, t_gpointer *parent)
+{
+ t_array *x = (t_array *)getbytes(sizeof (*x));
+ t_template *template;
+ t_gpointer *gp;
+ template = template_findbyname(templatesym);
+ x->a_templatesym = templatesym;
+ x->a_n = 1;
+ x->a_elemsize = sizeof(t_word) * template->t_n;
+ x->a_vec = (char *)getbytes(x->a_elemsize);
+ /* note here we blithely copy a gpointer instead of "setting" a
+ new one; this gpointer isn't accounted for and needn't be since
+ we'll be deleted before the thing pointed to gets deleted anyway;
+ see array_free. */
+ x->a_gp = *parent;
+ x->a_stub = gstub_new(0, x);
+ word_init((t_word *)(x->a_vec), template, parent);
+ return (x);
+}
+
+void array_resize(t_array *x, t_template *template, int n)
+{
+ int elemsize, oldn;
+ t_gpointer *gp;
+
+ if (n < 1)
+ n = 1;
+ oldn = x->a_n;
+ elemsize = sizeof(t_word) * template->t_n;
+
+ x->a_vec = (char *)resizebytes(x->a_vec, oldn * elemsize,
+ n * elemsize);
+ x->a_n = n;
+ if (n > oldn)
+ {
+ char *cp = x->a_vec + elemsize * oldn;
+ int i = n - oldn;
+ for (; i--; cp += elemsize)
+ {
+ t_word *wp = (t_word *)cp;
+ word_init(wp, template, &x->a_gp);
+ }
+ }
+}
+
+void array_free(t_array *x)
+{
+ /* we don't unset our gpointer here since it was never "set." */
+ /* gpointer_unset(&x->a_gp); */
+ gstub_cutoff(x->a_stub);
+ freebytes(x->a_vec, x->a_elemsize * x->a_n);
+ freebytes(x, sizeof *x);
+}
+
+/* --------------------- graphical arrays (garrays) ------------------- */
+
+t_class *garray_class;
+static int gcount = 0;
+
+struct _garray
+{
+ t_gobj x_gobj;
+ t_glist *x_glist;
+ t_array x_array; /* actual array; note only 4 fields used as below */
+ t_symbol *x_name;
+ t_symbol *x_realname; /* name with "$" expanded */
+ t_float x_firstx; /* X value of first item */
+ t_float x_xinc; /* X increment */
+ char x_usedindsp; /* true if some DSP routine is using this */
+ char x_saveit; /* true if we should save this with parent */
+};
+
+ /* macros to get into the "array" structure */
+#define x_n x_array.a_n
+#define x_elemsize x_array.a_elemsize
+#define x_vec x_array.a_vec
+#define x_templatesym x_array.a_templatesym
+
+t_garray *graph_array(t_glist *gl, t_symbol *s, t_symbol *templatesym,
+ t_floatarg f, t_floatarg saveit)
+{
+ int n = f, i;
+ int zz, nwords;
+ t_garray *x;
+ t_pd *x2;
+ t_template *template;
+ char *str;
+ if (s == &s_)
+ {
+ char buf[40];
+ sprintf(buf, "array%d", ++gcount);
+ s = gensym(buf);
+ templatesym = &s_float;
+ n = 100;
+ }
+ else if (!strncmp((str = s->s_name), "array", 5)
+ && (zz = atoi(str + 5)) > gcount) gcount = zz;
+ template = template_findbyname(templatesym);
+ if (!template)
+ {
+ error("array: couldn't find template %s", templatesym->s_name);
+ return (0);
+ }
+ nwords = template->t_n;
+ for (i = 0; i < nwords; i++)
+ {
+ /* we can't have array or list elements yet because what scalar
+ can act as their "parent"??? */
+ if (template->t_vec[i].ds_type == DT_ARRAY
+ || template->t_vec[i].ds_type == DT_LIST)
+ {
+ error("array: template %s can't have sublists or arrays",
+ templatesym->s_name);
+ return (0);
+ }
+ }
+ x = (t_garray *)pd_new(garray_class);
+
+ if (n <= 0) n = 100;
+ x->x_n = n;
+ x->x_elemsize = nwords * sizeof(t_word);
+ x->x_vec = getbytes(x->x_n * x->x_elemsize);
+ memset(x->x_vec, 0, x->x_n * x->x_elemsize);
+ /* LATER should check that malloc */
+ x->x_name = s;
+ x->x_realname = canvas_realizedollar(gl, s);
+ pd_bind(&x->x_gobj.g_pd, x->x_realname);
+ x->x_templatesym = templatesym;
+ x->x_firstx = 0;
+ x->x_xinc = 1; /* LATER make methods to set this... */
+ glist_add(gl, &x->x_gobj);
+ x->x_glist = gl;
+ x->x_usedindsp = 0;
+ x->x_saveit = (saveit != 0);
+ if (x2 = pd_findbyclass(gensym("#A"), garray_class))
+ pd_unbind(x2, gensym("#A"));
+
+ pd_bind(&x->x_gobj.g_pd, gensym("#A"));
+
+ return (x);
+}
+
+ /* called from array menu item to create a new one */
+void canvas_menuarray(t_glist *canvas)
+{
+ t_glist *x = (t_glist *)canvas;
+ char cmdbuf[200];
+ sprintf(cmdbuf, "pdtk_array_dialog %%s array%d 100 1 1\n",
+ ++gcount);
+ gfxstub_new(&x->gl_pd, x, cmdbuf);
+}
+
+ /* called from graph_dialog to set properties */
+void garray_properties(t_garray *x)
+{
+ char cmdbuf[200];
+ gfxstub_deleteforkey(x);
+ /* create dialog window. LATER fix this to escape '$'
+ properly; right now we just detect a leading '$' and escape
+ it. There should be a systematic way of doing this. */
+ if (x->x_name->s_name[0] == '$')
+ sprintf(cmdbuf, "pdtk_array_dialog %%s \\%s %d %d 0\n",
+ x->x_name->s_name, x->x_n, x->x_saveit);
+ else sprintf(cmdbuf, "pdtk_array_dialog %%s %s %d %d 0\n",
+ x->x_name->s_name, x->x_n, x->x_saveit);
+ gfxstub_new(&x->x_gobj.g_pd, x, cmdbuf);
+}
+
+ /* this is called back from the dialog window to create a garray.
+ The otherflag requests that we find an existing graph to put it in. */
+void glist_arraydialog(t_glist *parent, t_symbol *name, t_floatarg size,
+ t_floatarg saveit, t_floatarg otherflag)
+{
+ t_glist *gl;
+ t_garray *a;
+ if (size < 1)
+ size = 1;
+ if (otherflag == 0 || (!(gl = glist_findgraph(parent))))
+ gl = glist_addglist(parent, &s_, 0, 1,
+ (size > 1 ? size-1 : size), -1, 0, 0, 0, 0);
+ a = graph_array(gl, sharptodollar(name), &s_float, size, saveit);
+}
+
+ /* this is called from the properties dialog window for an existing array */
+void garray_arraydialog(t_garray *x, t_symbol *name, t_floatarg fsize,
+ t_floatarg saveit, t_floatarg deleteit)
+{
+ if (deleteit != 0)
+ {
+ glist_delete(x->x_glist, &x->x_gobj);
+ }
+ else
+ {
+ int size;
+ t_symbol *argname = sharptodollar(name);
+ if (argname != x->x_name)
+ {
+ x->x_name = argname;
+ pd_unbind(&x->x_gobj.g_pd, x->x_realname);
+ x->x_realname = canvas_realizedollar(x->x_glist, argname);
+ pd_bind(&x->x_gobj.g_pd, x->x_realname);
+ }
+ size = fsize;
+ if (size < 1)
+ size = 1;
+ if (size != x->x_n)
+ garray_resize(x, size);
+ garray_setsaveit(x, (saveit != 0));
+ garray_redraw(x);
+ }
+}
+
+static void garray_free(t_garray *x)
+{
+ t_pd *x2;
+ gfxstub_deleteforkey(x);
+ pd_unbind(&x->x_gobj.g_pd, x->x_realname);
+ /* LATER find a way to get #A unbound earlier (at end of load?) */
+ while (x2 = pd_findbyclass(gensym("#A"), garray_class))
+ pd_unbind(x2, gensym("#A"));
+ freebytes(x->x_vec, x->x_n * x->x_elemsize);
+}
+
+/* ------------- code used by both array and plot widget functions ---- */
+
+ /* routine to get screen coordinates of a point in an array */
+void array_getcoordinate(t_glist *glist,
+ char *elem, int xonset, int yonset, int wonset, int indx,
+ float basex, float basey, float xinc,
+ float *xp, float *yp, float *wp)
+{
+ float xval, yval, ypix, wpix;
+ if (xonset >= 0)
+ xval = *(float *)(elem + xonset);
+ else xval = indx * xinc;
+ if (yonset >= 0)
+ yval = *(float *)(elem + yonset);
+ else yval = 0;
+ ypix = glist_ytopixels(glist, basey + yval);
+ if (wonset >= 0)
+ {
+ /* found "w" field which controls linewidth. */
+ float wval = *(float *)(elem + wonset);
+ wpix = glist_ytopixels(glist, basey + yval + wval) - ypix;
+ if (wpix < 0)
+ wpix = -wpix;
+ }
+ else wpix = 1;
+ *xp = glist_xtopixels(glist, basex + xval);
+ *yp = ypix;
+ *wp = wpix;
+}
+
+static float array_motion_xcumulative;
+static float array_motion_ycumulative;
+static t_symbol *array_motion_xfield;
+static t_symbol *array_motion_yfield;
+static t_glist *array_motion_glist;
+static t_gobj *array_motion_gobj;
+static t_word *array_motion_wp;
+static t_template *array_motion_template;
+static int array_motion_npoints;
+static int array_motion_elemsize;
+static int array_motion_altkey;
+static float array_motion_initx;
+static float array_motion_xperpix;
+static float array_motion_yperpix;
+static int array_motion_lastx;
+static int array_motion_fatten;
+
+ /* LATER protect against the template changing or the scalar disappearing
+ probably by attaching a gpointer here ... */
+
+static void array_motion(void *z, t_floatarg dx, t_floatarg dy)
+{
+ array_motion_xcumulative += dx * array_motion_xperpix;
+ array_motion_ycumulative += dy * array_motion_yperpix;
+ if (*array_motion_xfield->s_name)
+ {
+ /* it's an x, y plot; can drag many points at once */
+ int i;
+ char *charword = (char *)array_motion_wp;
+ for (i = 0; i < array_motion_npoints; i++)
+ {
+ t_word *thisword = (t_word *)(charword + i * array_motion_elemsize);
+ if (*array_motion_xfield->s_name)
+ {
+ float xwas = template_getfloat(array_motion_template,
+ array_motion_xfield, thisword, 1);
+ template_setfloat(array_motion_template,
+ array_motion_xfield, thisword, xwas + dx, 1);
+ }
+ if (*array_motion_yfield->s_name)
+ {
+ float ywas = template_getfloat(array_motion_template,
+ array_motion_yfield, thisword, 1);
+ if (array_motion_fatten)
+ {
+ if (i == 0)
+ {
+ float newy = ywas + dy;
+ if (newy < 0)
+ newy = 0;
+ template_setfloat(array_motion_template,
+ array_motion_yfield, thisword, newy, 1);
+ }
+ }
+ else
+ {
+ template_setfloat(array_motion_template,
+ array_motion_yfield, thisword, ywas + dy, 1);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* a y-only plot. */
+ int thisx = array_motion_initx +
+ array_motion_xcumulative, x2;
+ int increment, i, nchange;
+ char *charword = (char *)array_motion_wp;
+ float newy = array_motion_ycumulative,
+ oldy = template_getfloat(
+ array_motion_template, array_motion_yfield,
+ (t_word *)(charword + array_motion_elemsize * array_motion_lastx), 1);
+ float ydiff = newy - oldy;
+ if (thisx < 0) thisx = 0;
+ else if (thisx >= array_motion_npoints)
+ thisx = array_motion_npoints - 1;
+ increment = (thisx > array_motion_lastx ? -1 : 1);
+ nchange = 1 + increment * (array_motion_lastx - thisx);
+
+ for (i = 0, x2 = thisx; i < nchange; i++, x2 += increment)
+ {
+ template_setfloat(array_motion_template,
+ array_motion_yfield,
+ (t_word *)(charword + array_motion_elemsize * x2),
+ newy, 1);
+ if (nchange > 1)
+ newy -= ydiff * (1./(nchange - 1));
+ }
+ array_motion_lastx = thisx;
+ }
+ glist_redrawitem(array_motion_glist, array_motion_gobj);
+}
+
+int array_doclick(t_array *array, t_glist *glist, t_gobj *gobj,
+ t_symbol *elemtemplatesym,
+ float linewidth, float xloc, float xinc, float yloc,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ t_canvas *elemtemplatecanvas;
+ t_template *elemtemplate;
+ int elemsize, yonset, wonset, xonset, i;
+
+ if (!array_getfields(elemtemplatesym, &elemtemplatecanvas,
+ &elemtemplate, &elemsize, &xonset, &yonset, &wonset))
+ {
+ float best = 100;
+ int incr;
+ /* if it has more than 2000 points, just check 300 of them. */
+ if (array->a_n < 2000)
+ incr = 1;
+ else incr = array->a_n / 300;
+ for (i = 0; i < array->a_n; i += incr)
+ {
+ float pxpix, pypix, pwpix, dx, dy;
+ array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize,
+ xonset, yonset, wonset, i, xloc, yloc, xinc,
+ &pxpix, &pypix, &pwpix);
+ if (pwpix < 4)
+ pwpix = 4;
+ dx = pxpix - xpix;
+ if (dx < 0) dx = -dx;
+ if (dx > 8)
+ continue;
+ dy = pypix - ypix;
+ if (dy < 0) dy = -dy;
+ if (dx + dy < best)
+ best = dx + dy;
+ if (wonset >= 0)
+ {
+ dy = (pypix + pwpix) - ypix;
+ if (dy < 0) dy = -dy;
+ if (dx + dy < best)
+ best = dx + dy;
+ dy = (pypix - pwpix) - ypix;
+ if (dy < 0) dy = -dy;
+ if (dx + dy < best)
+ best = dx + dy;
+ }
+ }
+ if (best > 8)
+ return (0);
+ best += 0.001; /* add truncation error margin */
+ for (i = 0; i < array->a_n; i += incr)
+ {
+ float pxpix, pypix, pwpix, dx, dy, dy2, dy3;
+ array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize,
+ xonset, yonset, wonset, i, xloc, yloc, xinc,
+ &pxpix, &pypix, &pwpix);
+ if (pwpix < 4)
+ pwpix = 4;
+ dx = pxpix - xpix;
+ if (dx < 0) dx = -dx;
+ dy = pypix - ypix;
+ if (dy < 0) dy = -dy;
+ if (wonset >= 0)
+ {
+ dy2 = (pypix + pwpix) - ypix;
+ if (dy2 < 0) dy2 = -dy2;
+ dy3 = (pypix - pwpix) - ypix;
+ if (dy3 < 0) dy3 = -dy3;
+ if (yonset <= 0)
+ dy = 100;
+ }
+ else dy2 = dy3 = 100;
+ if (dx + dy <= best || dx + dy2 <= best || dx + dy3 <= best)
+ {
+ if (dy < dy2 && dy < dy3)
+ array_motion_fatten = 0;
+ else if (dy2 < dy3)
+ array_motion_fatten = -1;
+ else array_motion_fatten = 1;
+ if (doit)
+ {
+ char *elem = (char *)array->a_vec;
+ array_motion_elemsize = elemsize;
+ array_motion_glist = glist;
+ array_motion_gobj = gobj;
+ array_motion_template = elemtemplate;
+ array_motion_xperpix = glist_dpixtodx(glist, 1);
+ array_motion_yperpix = glist_dpixtody(glist, 1);
+ if (alt && xpix < pxpix) /* delete a point */
+ {
+ if (array->a_n <= 1)
+ return (0);
+ memmove((char *)(array->a_vec) + elemsize * i,
+ (char *)(array->a_vec) + elemsize * (i+1),
+ (array->a_n - 1 - i) * elemsize);
+ array_resize(array, elemtemplate, array->a_n - 1);
+ glist_redrawitem(array_motion_glist, array_motion_gobj);
+ return (0);
+ }
+ else if (alt)
+ {
+ /* add a point (after the clicked-on one) */
+ array_resize(array, elemtemplate, array->a_n + 1);
+ elem = (char *)array->a_vec;
+ memmove(elem + elemsize * (i+1),
+ elem + elemsize * i,
+ (array->a_n - i) * elemsize);
+ i++;
+ (array->a_n)++;
+ }
+ if (xonset >= 0)
+ {
+ array_motion_xfield = gensym("x");
+ array_motion_xcumulative =
+ *(float *)((elem + elemsize * i) + xonset);
+ array_motion_wp = (t_word *)(elem + i * elemsize);
+ array_motion_npoints = array->a_n - i;
+ }
+ else
+ {
+ array_motion_xfield = &s_;
+ array_motion_xcumulative = 0;
+ array_motion_wp = (t_word *)elem;
+ array_motion_npoints = array->a_n;
+
+ array_motion_initx = i;
+ array_motion_lastx = i;
+ array_motion_xperpix *= (xinc == 0 ? 1 : 1./xinc);
+ }
+ if (array_motion_fatten)
+ {
+ array_motion_yfield = gensym("w");
+ array_motion_ycumulative =
+ *(float *)((elem + elemsize * i) + wonset);
+ array_motion_xperpix *= array_motion_fatten;
+ }
+ else if (yonset >= 0)
+ {
+ array_motion_yfield = gensym("y");
+ array_motion_ycumulative =
+ *(float *)((elem + elemsize * i) + yonset);
+ }
+ else
+ {
+ array_motion_yfield = &s_;
+ array_motion_ycumulative = 0;
+ }
+ glist_grab(glist, 0, array_motion, 0, xpix, ypix);
+ }
+ if (alt)
+ {
+ if (xpix < pxpix)
+ return (CURSOR_EDITMODE_DISCONNECT);
+ else return (CURSOR_RUNMODE_ADDPOINT);
+ }
+ else return (array_motion_fatten ?
+ CURSOR_RUNMODE_THICKEN : CURSOR_RUNMODE_CLICKME);
+ }
+ }
+ }
+ return (0);
+}
+
+/* -------------------- widget behavior for garray ------------ */
+
+static void garray_getrect(t_gobj *z, t_glist *glist,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_garray *x = (t_garray *)z;
+ float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff;
+ t_canvas *elemtemplatecanvas;
+ t_template *elemtemplate;
+ int elemsize, yonset, wonset, xonset, i;
+
+ if (!array_getfields(x->x_templatesym, &elemtemplatecanvas,
+ &elemtemplate, &elemsize, &xonset, &yonset, &wonset))
+ {
+ int incr;
+ /* if it has more than 2000 points, just check 300 of them. */
+ if (x->x_array.a_n < 2000)
+ incr = 1;
+ else incr = x->x_array.a_n / 300;
+ for (i = 0; i < x->x_array.a_n; i += incr)
+ {
+ float pxpix, pypix, pwpix, dx, dy;
+ array_getcoordinate(glist, (char *)(x->x_array.a_vec) +
+ i * elemsize,
+ xonset, yonset, wonset, i, 0, 0, 1,
+ &pxpix, &pypix, &pwpix);
+ if (pwpix < 2)
+ pwpix = 2;
+ if (pxpix < x1)
+ x1 = pxpix;
+ if (pxpix > x2)
+ x2 = pxpix;
+ if (pypix - pwpix < y1)
+ y1 = pypix - pwpix;
+ if (pypix + pwpix > y2)
+ y2 = pypix + pwpix;
+ }
+ }
+ *xp1 = x1;
+ *yp1 = y1;
+ *xp2 = x2;
+ *yp2 = y2;
+}
+
+static void garray_displace(t_gobj *z, t_glist *glist, int dx, int dy)
+{
+ /* refuse */
+}
+
+static void garray_select(t_gobj *z, t_glist *glist, int state)
+{
+ t_garray *x = (t_garray *)z;
+ /* fill in later */
+}
+
+static void garray_activate(t_gobj *z, t_glist *glist, int state)
+{
+}
+
+static void garray_delete(t_gobj *z, t_glist *glist)
+{
+ /* nothing to do */
+}
+
+static void garray_vis(t_gobj *z, t_glist *glist, int vis)
+{
+ t_garray *x = (t_garray *)z;
+ if (vis)
+ {
+ int i, xonset, yonset, type;
+ t_symbol *arraytype;
+ t_template *template = template_findbyname(x->x_templatesym);
+ if (!template)
+ return;
+ if (!template_find_field(template, gensym("y"), &yonset, &type,
+ &arraytype) || type != DT_FLOAT)
+ {
+ error("%s: needs floating-point 'y' field",
+ x->x_templatesym->s_name);
+ sys_vgui(".x%x.c create text 50 50 -text foo\
+ -tags .x%x.a%x\n",
+ glist_getcanvas(glist), glist_getcanvas(glist), x);
+ }
+ else if (!template_find_field(template, gensym("x"), &xonset, &type,
+ &arraytype) || type != DT_FLOAT)
+ {
+ float firsty, xcum = x->x_firstx;
+ int lastpixel = -1, ndrawn = 0;
+ float yval = 0, xpix;
+ int ixpix = 0;
+ sys_vgui(".x%x.c create line \\\n", glist_getcanvas(glist));
+ for (i = 0; i < x->x_n; i++)
+ {
+ yval = *(float *)(x->x_vec +
+ template->t_n * i * sizeof (t_word) + yonset);
+ xpix = glist_xtopixels(glist, xcum);
+ ixpix = xpix + 0.5;
+ if (ixpix != lastpixel)
+ {
+ sys_vgui("%d %f \\\n", ixpix,
+ glist_ytopixels(glist, yval));
+ ndrawn++;
+ }
+ lastpixel = ixpix;
+ if (ndrawn >= 1000) break;
+ xcum += x->x_xinc;
+ }
+ /* TK will complain if there aren't at least 2 points... */
+ if (ndrawn == 0) sys_vgui("0 0 0 0 \\\n");
+ else if (ndrawn == 1) sys_vgui("%d %f \\\n", ixpix,
+ glist_ytopixels(glist, yval));
+ sys_vgui("-tags .x%x.a%x\n", glist_getcanvas(glist), x);
+ firsty = *(float *)(x->x_vec + yonset);
+ sys_vgui(".x%x.c create text %f %f -text {%s} -anchor e\
+ -font -*-courier-bold--normal--%d-* -tags .x%x.a%x\n",
+ glist_getcanvas(glist),
+ glist_xtopixels(glist, x->x_firstx) - 5.,
+ glist_ytopixels(glist, firsty),
+ x->x_name->s_name, glist_getfont(glist),
+ glist_getcanvas(glist), x);
+ }
+ else
+ {
+ post("x, y arrays not yet supported");
+ }
+ }
+ else
+ {
+ sys_vgui(".x%x.c delete .x%x.a%x\n",
+ glist_getcanvas(glist), glist_getcanvas(glist), x);
+ }
+}
+
+static int garray_click(t_gobj *z, struct _glist *glist,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ t_garray *x = (t_garray *)z;
+ return (array_doclick(&x->x_array, glist, z, x->x_templatesym, 1, 0, 1, 0,
+ xpix, ypix, shift, alt, dbl, doit));
+}
+
+#define ARRAYWRITECHUNKSIZE 1000
+
+static void garray_save(t_gobj *z, t_binbuf *b)
+{
+ t_garray *x = (t_garray *)z;
+ binbuf_addv(b, "sssisi;", gensym("#X"), gensym("array"),
+ x->x_name, x->x_n, x->x_templatesym, x->x_saveit);
+ if (x->x_saveit)
+ {
+ int n = x->x_n, n2 = 0;
+ if (x->x_templatesym != &s_float)
+ {
+ pd_error(x, "sorry, you can only save 'float' arrays now");
+ return;
+ }
+ if (n > 200000)
+ post("warning: I'm saving an array with %d points!\n", n);
+ while (n2 < n)
+ {
+ int chunk = n - n2, i;
+ if (chunk > ARRAYWRITECHUNKSIZE)
+ chunk = ARRAYWRITECHUNKSIZE;
+ binbuf_addv(b, "si", gensym("#A"), n2);
+ for (i = 0; i < chunk; i++)
+ binbuf_addv(b, "f", ((float *)(x->x_vec))[n2+i]);
+ binbuf_addv(b, ";");
+ n2 += chunk;
+ }
+ }
+}
+
+t_widgetbehavior garray_widgetbehavior =
+{
+ garray_getrect,
+ garray_displace,
+ garray_select,
+ garray_activate,
+ garray_delete,
+ garray_vis,
+ garray_click,
+ garray_save,
+ 0
+};
+
+/* ----------------------- public functions -------------------- */
+
+void garray_usedindsp(t_garray *x)
+{
+ x->x_usedindsp = 1;
+}
+
+void garray_redraw(t_garray *x)
+{
+ if (glist_isvisible(x->x_glist))
+ {
+ garray_vis(&x->x_gobj, x->x_glist, 0);
+ garray_vis(&x->x_gobj, x->x_glist, 1);
+ }
+}
+
+ /* This functiopn gets the template of an array; if we can't figure
+ out what template an array's elements belong to we're in grave trouble
+ when it's time to free or resize it. */
+t_template *garray_template(t_garray *x)
+{
+ t_template *template = template_findbyname(x->x_templatesym);
+ if (!template)
+ bug("garray_template");
+ return (template);
+}
+
+int garray_npoints(t_garray *x) /* get the length */
+{
+ return (x->x_n);
+}
+
+char *garray_vec(t_garray *x) /* get the contents */
+{
+ return ((char *)(x->x_vec));
+}
+
+ /* routine that checks if we're just an array of floats and if
+ so returns the goods */
+
+int garray_getfloatarray(t_garray *x, int *size, t_float **vec)
+{
+ t_template *template = garray_template(x);
+ int yonset, type;
+ t_symbol *arraytype;
+ if (!template_find_field(template, gensym("y"), &yonset,
+ &type, &arraytype) || type != DT_FLOAT)
+ error("%s: needs floating-point 'y' field",
+ x->x_templatesym->s_name);
+ else if (template->t_n != 1)
+ error("%s: has more than one field", x->x_templatesym->s_name);
+ else
+ {
+ *size = garray_npoints(x);
+ *vec = (float *)garray_vec(x);
+ return (1);
+ }
+ return (0);
+}
+
+ /* get any floating-point field of any element of an array */
+float garray_get(t_garray *x, t_symbol *s, t_int indx)
+{
+ t_template *template = garray_template(x);
+ int yonset, type;
+ t_symbol *arraytype;
+ if (!template_find_field(template, gensym("y"), &yonset,
+ &type, &arraytype) || type != DT_FLOAT)
+ {
+ error("%s: needs floating-point '%s' field", x->x_templatesym->s_name,
+ s->s_name);
+ return (0);
+ }
+ if (indx < 0) indx = 0;
+ else if (indx >= x->x_n) indx = x->x_n - 1;
+ return (*(float *)((x->x_vec + sizeof(t_word) * indx) + yonset));
+}
+
+ /* set the "saveit" flag */
+void garray_setsaveit(t_garray *x, int saveit)
+{
+ if (x->x_saveit && !saveit)
+ post("warning: array %s: clearing save-in-patch flag",
+ x->x_name->s_name);
+ x->x_saveit = saveit;
+}
+
+/*------------------- Pd messages ------------------------ */
+static void garray_const(t_garray *x, t_floatarg g)
+{
+ t_template *template = garray_template(x);
+ int yonset, type, i;
+ t_symbol *arraytype;
+ if (!template_find_field(template, gensym("y"), &yonset,
+ &type, &arraytype) || type != DT_FLOAT)
+ error("%s: needs floating-point 'y' field",
+ x->x_templatesym->s_name);
+ else for (i = 0; i < x->x_n; i++)
+ *(float *)(((char *)x->x_vec + sizeof(t_word) * i) + yonset) = g;
+ garray_redraw(x);
+}
+
+ /* sum of Fourier components; called from routines below */
+static void garray_dofo(t_garray *x, int npoints, float dcval,
+ int nsin, t_float *vsin, int sineflag)
+{
+ t_template *template = garray_template(x);
+ int yonset, type, i, j;
+ t_symbol *arraytype;
+ double phase, phaseincr, fj;
+ if (npoints == 0)
+ npoints = 512; /* dunno what a good default would be... */
+ if (npoints != (1 << ilog2(npoints)))
+ post("%s: rounnding to %d points", x->x_templatesym->s_name,
+ (npoints = (1<<ilog2(npoints))));
+ garray_resize(x, npoints + 3);
+ phaseincr = 2. * 3.14159 / npoints;
+ if (!template_find_field(template, gensym("y"), &yonset,
+ &type, &arraytype) || type != DT_FLOAT)
+ {
+ error("%s: needs floating-point 'y' field", x->x_templatesym->s_name);
+ return;
+ }
+ for (i = 0, phase = -phaseincr; i < x->x_n; i++, phase += phaseincr )
+ {
+ double sum = dcval;
+ if (sineflag)
+ for (j = 0, fj = phase; j < nsin; j++, fj += phase)
+ sum += vsin[j] * sin(fj);
+ else
+ for (j = 0, fj = 0; j < nsin; j++, fj += phase)
+ sum += vsin[j] * cos(fj);
+ *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) = sum;
+ }
+ garray_redraw(x);
+}
+
+static void garray_sinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_template *template = garray_template(x);
+
+ t_float *svec = (t_float *)t_getbytes(sizeof(t_float) * argc);
+ int npoints, i;
+ if (argc < 2)
+ {
+ error("sinesum: %s: need number of points and partial strengths",
+ x->x_templatesym->s_name);
+ return;
+ }
+
+ npoints = atom_getfloatarg(0, argc, argv);
+ argv++, argc--;
+
+ svec = (t_float *)t_getbytes(sizeof(t_float) * argc);
+ if (!svec) return;
+
+ for (i = 0; i < argc; i++)
+ svec[i] = atom_getfloatarg(i, argc, argv);
+ garray_dofo(x, npoints, 0, argc, svec, 1);
+ t_freebytes(svec, sizeof(t_float) * argc);
+}
+
+static void garray_cosinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_template *template = garray_template(x);
+
+ t_float *svec = (t_float *)t_getbytes(sizeof(t_float) * argc);
+ int npoints, i;
+ if (argc < 2)
+ {
+ error("sinesum: %s: need number of points and partial strengths",
+ x->x_templatesym->s_name);
+ return;
+ }
+
+ npoints = atom_getfloatarg(0, argc, argv);
+ argv++, argc--;
+
+ svec = (t_float *)t_getbytes(sizeof(t_float) * argc);
+ if (!svec) return;
+
+ for (i = 0; i < argc; i++)
+ svec[i] = atom_getfloatarg(i, argc, argv);
+ garray_dofo(x, npoints, 0, argc, svec, 0);
+ t_freebytes(svec, sizeof(t_float) * argc);
+}
+
+static void garray_normalize(t_garray *x, t_float f)
+{
+ t_template *template = garray_template(x);
+ int yonset, type, npoints, i;
+ double maxv, renormer;
+ t_symbol *arraytype;
+
+ if (f <= 0)
+ f = 1;
+
+ if (!template_find_field(template, gensym("y"), &yonset,
+ &type, &arraytype) || type != DT_FLOAT)
+ {
+ error("%s: needs floating-point 'y' field", x->x_templatesym->s_name);
+ return;
+ }
+ for (i = 0, maxv = 0; i < x->x_n; i++)
+ {
+ double v = *(float *)((x->x_vec + sizeof(t_word) * i) + yonset);
+ if (v > maxv)
+ maxv = v;
+ if (-v > maxv)
+ maxv = -v;
+ }
+ if (maxv >= 0)
+ {
+ renormer = f / maxv;
+ for (i = 0; i < x->x_n; i++)
+ {
+ *(float *)((x->x_vec + sizeof(t_word) * i) + yonset)
+ *= renormer;
+ }
+ }
+ garray_redraw(x);
+}
+
+ /* list -- the first value is an index; subsequent values are put in
+ the "y" slot of the array. This generalizes Max's "table", sort of. */
+static void garray_list(t_garray *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_template *template = garray_template(x);
+ int yonset, type, i;
+ t_symbol *arraytype;
+ if (!template_find_field(template, gensym("y"), &yonset,
+ &type, &arraytype) || type != DT_FLOAT)
+ error("%s: needs floating-point 'y' field",
+ x->x_templatesym->s_name);
+ else if (argc < 2) return;
+ else
+ {
+ int firstindex = atom_getfloat(argv);
+ argc--;
+ argv++;
+ /* drop negative x values */
+ if (firstindex < 0)
+ {
+ argc += firstindex;
+ argv -= firstindex;
+ firstindex = 0;
+ if (argc <= 0) return;
+ }
+ if (argc + firstindex > x->x_n)
+ {
+ argc = x->x_n - firstindex;
+ if (argc <= 0) return;
+ }
+ for (i = 0; i < argc; i++)
+ *(float *)((x->x_vec + sizeof(t_word) * (i + firstindex)) + yonset) =
+ atom_getfloat(argv + i);
+ }
+ garray_redraw(x);
+}
+
+ /* forward a "bounds" message to the owning graph */
+static void garray_bounds(t_garray *x, t_floatarg x1, t_floatarg y1,
+ t_floatarg x2, t_floatarg y2)
+{
+ vmess(&x->x_glist->gl_pd, gensym("bounds"), "ffff", x1, y1, x2, y2);
+}
+
+ /* same for "xticks", etc */
+static void garray_xticks(t_garray *x,
+ t_floatarg point, t_floatarg inc, t_floatarg f)
+{
+ vmess(&x->x_glist->gl_pd, gensym("xticks"), "fff", point, inc, f);
+}
+
+static void garray_yticks(t_garray *x,
+ t_floatarg point, t_floatarg inc, t_floatarg f)
+{
+ vmess(&x->x_glist->gl_pd, gensym("yticks"), "fff", point, inc, f);
+}
+
+static void garray_xlabel(t_garray *x, t_symbol *s, int argc, t_atom *argv)
+{
+ typedmess(&x->x_glist->gl_pd, s, argc, argv);
+}
+
+static void garray_ylabel(t_garray *x, t_symbol *s, int argc, t_atom *argv)
+{
+ typedmess(&x->x_glist->gl_pd, s, argc, argv);
+}
+ /* change the name of a garray. */
+static void garray_rename(t_garray *x, t_symbol *s)
+{
+ pd_unbind(&x->x_gobj.g_pd, x->x_realname);
+ pd_bind(&x->x_gobj.g_pd, x->x_realname = x->x_name = s);
+ garray_redraw(x);
+}
+
+static void garray_read(t_garray *x, t_symbol *filename)
+{
+ int nelem = x->x_n, filedesc;
+ FILE *fd;
+ char buf[MAXPDSTRING], *bufptr;
+ t_template *template = garray_template(x);
+ int yonset, type, i;
+ t_symbol *arraytype;
+ if (!template_find_field(template, gensym("y"), &yonset,
+ &type, &arraytype) || type != DT_FLOAT)
+ {
+ error("%s: needs floating-point 'y' field", x->x_templatesym->s_name);
+ return;
+ }
+ if ((filedesc = open_via_path(
+ canvas_getdir(glist_getcanvas(x->x_glist))->s_name,
+ filename->s_name, "", buf, &bufptr, MAXPDSTRING, 0)) < 0
+ || !(fd = fdopen(filedesc, "r")))
+ {
+ error("%s: can't open", filename->s_name);
+ return;
+ }
+ for (i = 0; i < nelem; i++)
+ {
+ if (!fscanf(fd, "%f", (float *)((x->x_vec + sizeof(t_word) * i) +
+ yonset)))
+ {
+ post("%s: read %d elements into table of size %d",
+ filename->s_name, i, nelem);
+ break;
+ }
+ }
+ while (i < nelem)
+ *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) = 0, i++;
+ fclose(fd);
+ garray_redraw(x);
+}
+
+ /* this should be renamed and moved... */
+int garray_ambigendian(void)
+{
+ unsigned short s = 1;
+ unsigned char c = *(char *)(&s);
+ return (c==0);
+}
+
+#define BINREADMODE "rb"
+#define BINWRITEMODE "wb"
+
+static void garray_read16(t_garray *x, t_symbol *filename,
+ t_symbol *endian, t_floatarg fskip)
+{
+ int skip = fskip, filedesc;
+ int i, nelem;
+ float *vec;
+ FILE *fd;
+ char buf[MAXPDSTRING], *bufptr;
+ short s;
+ int cpubig = garray_ambigendian(), swap = 0;
+ char c = endian->s_name[0];
+ if (c == 'b')
+ {
+ if (!cpubig) swap = 1;
+ }
+ else if (c == 'l')
+ {
+ if (cpubig) swap = 1;
+ }
+ else if (c)
+ {
+ error("array_read16: endianness is 'l' (low byte first ala INTEL)");
+ post("... or 'b' (high byte first ala MIPS,DEC,PPC)");
+ }
+ if (!garray_getfloatarray(x, &nelem, &vec))
+ {
+ error("%s: not a float array", x->x_templatesym->s_name);
+ return;
+ }
+ if ((filedesc = open_via_path(
+ canvas_getdir(glist_getcanvas(x->x_glist))->s_name,
+ filename->s_name, "", buf, &bufptr, MAXPDSTRING, 1)) < 0
+ || !(fd = fdopen(filedesc, BINREADMODE)))
+ {
+ error("%s: can't open", filename->s_name);
+ return;
+ }
+ if (skip)
+ {
+ long pos = fseek(fd, (long)skip, SEEK_SET);
+ if (pos < 0)
+ {
+ error("%s: can't seek to byte %d", buf, skip);
+ fclose(fd);
+ return;
+ }
+ }
+
+ for (i = 0; i < nelem; i++)
+ {
+ if (fread(&s, sizeof(s), 1, fd) < 1)
+ {
+ post("%s: read %d elements into table of size %d",
+ filename->s_name, i, nelem);
+ break;
+ }
+ if (swap) s = ((s & 0xff) << 8) | ((s & 0xff00) >> 8);
+ vec[i] = s * (1./32768.);
+ }
+ while (i < nelem) vec[i++] = 0;
+ fclose(fd);
+ garray_redraw(x);
+}
+
+static void garray_write(t_garray *x, t_symbol *filename)
+{
+ FILE *fd;
+ char buf[MAXPDSTRING];
+ t_template *template = garray_template(x);
+ int yonset, type, i;
+ t_symbol *arraytype;
+ if (!template_find_field(template, gensym("y"), &yonset,
+ &type, &arraytype) || type != DT_FLOAT)
+ {
+ error("%s: needs floating-point 'y' field", x->x_templatesym->s_name);
+ return;
+ }
+ canvas_makefilename(glist_getcanvas(x->x_glist), filename->s_name,
+ buf, MAXPDSTRING);
+ sys_bashfilename(buf, buf);
+ if (!(fd = fopen(buf, "w")))
+ {
+ error("%s: can't create", buf);
+ return;
+ }
+ for (i = 0; i < x->x_n; i++)
+ {
+ if (fprintf(fd, "%g\n",
+ *(float *)((x->x_vec + sizeof(t_word) * i) + yonset)) < 1)
+ {
+ post("%s: write error", filename->s_name);
+ break;
+ }
+ }
+ fclose(fd);
+}
+
+static unsigned char waveheader[] = {
+0x52, 0x49, 0x46, 0x46,
+0x00, 0x00, 0x00, 0x00,
+0x57, 0x41, 0x56, 0x45,
+0x66, 0x6d, 0x74, 0x20,
+
+0x10, 0x00, 0x00, 0x00,
+0x01, 0x00, 0x01, 0x00,
+0x44, 0xac, 0x00, 0x00,
+0x88, 0x58, 0x01, 0x00,
+
+0x02, 0x00, 0x10, 0x00,
+0x64, 0x61, 0x74, 0x61,
+0x00, 0x00, 0x00, 0x00,
+};
+
+ /* wave format only so far */
+static void garray_write16(t_garray *x, t_symbol *filename, t_symbol *format)
+{
+ t_template *template = garray_template(x);
+ int yonset, type, i;
+ t_symbol *arraytype;
+ FILE *fd;
+ int aiff = (format == gensym("aiff"));
+ char filenamebuf[MAXPDSTRING], buf2[MAXPDSTRING];
+ int swap = garray_ambigendian(); /* wave is only little endian */
+ int intbuf;
+ strncpy(filenamebuf, filename->s_name, MAXPDSTRING-10);
+ filenamebuf[MAXPDSTRING-10] = 0;
+ if (sizeof(int) != 4) post("write16: only works on 32-bit machines");
+ if (aiff)
+ {
+ if (strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff"))
+ strcat(filenamebuf, ".aiff");
+ }
+ else
+ {
+ if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav"))
+ strcat(filenamebuf, ".wav");
+ }
+ if (!template_find_field(template, gensym("y"), &yonset,
+ &type, &arraytype) || type != DT_FLOAT)
+ {
+ error("%s: needs floating-point 'y' field", x->x_templatesym->s_name);
+ return;
+ }
+ canvas_makefilename(glist_getcanvas(x->x_glist), filenamebuf,
+ buf2, MAXPDSTRING);
+ sys_bashfilename(buf2, buf2);
+ if (!(fd = fopen(buf2, BINWRITEMODE)))
+ {
+ error("%s: can't create", buf2);
+ return;
+ }
+ intbuf = 2 * x->x_n + 36;
+ if (swap)
+ {
+ unsigned char *foo = (unsigned char *)&intbuf, xxx;
+ xxx = foo[0]; foo[0] = foo[3]; foo[3] = xxx;
+ xxx = foo[1]; foo[1] = foo[2]; foo[2] = xxx;
+ }
+ memcpy((void *)(waveheader + 4), (void *)(&intbuf), 4);
+ intbuf = 2 * x->x_n;
+ if (swap)
+ {
+ unsigned char *foo = (unsigned char *)&intbuf, xxx;
+ xxx = foo[0]; foo[0] = foo[3]; foo[3] = xxx;
+ xxx = foo[1]; foo[1] = foo[2]; foo[2] = xxx;
+ }
+ memcpy((void *)(waveheader + 40), (void *)(&intbuf), 4);
+ if (fwrite(waveheader, sizeof(waveheader), 1, fd) < 1)
+ {
+ post("%s: write error", buf2);
+ goto closeit;
+ }
+ for (i = 0; i < x->x_n; i++)
+ {
+ float f = 32767. * *(float *)((x->x_vec + sizeof(t_word) * i) + yonset);
+ short sh;
+ if (f < -32768) f = -32768;
+ else if (f > 32767) f = 32767;
+ sh = f;
+ if (swap)
+ {
+ unsigned char *foo = (unsigned char *)&sh, xxx;
+ xxx = foo[0]; foo[0] = foo[1]; foo[1] = xxx;
+ }
+ if (fwrite(&sh, sizeof(sh), 1, fd) < 1)
+ {
+ post("%s: write error", buf2);
+ goto closeit;
+ }
+ }
+closeit:
+ fclose(fd);
+}
+
+void garray_resize(t_garray *x, t_floatarg f)
+{
+ int was = x->x_n, elemsize;
+ t_glist *gl;
+ int dspwas;
+ int n = f;
+ char *nvec;
+
+ if (n < 1) n = 1;
+ elemsize = template_findbyname(x->x_templatesym)->t_n * sizeof(t_word);
+ nvec = t_resizebytes(x->x_vec, was * elemsize, n * elemsize);
+ if (!nvec)
+ {
+ pd_error(x, "array resize failed: out of memory");
+ return;
+ }
+ x->x_vec = nvec;
+ /* LATER should check t_resizebytes result */
+ if (n > was)
+ memset(x->x_vec + was*elemsize,
+ 0, (n - was) * elemsize);
+ x->x_n = n;
+
+ /* if this is the only array in the graph,
+ reset the graph's coordinates */
+ gl = x->x_glist;
+ if (gl->gl_list == &x->x_gobj && !x->x_gobj.g_next)
+ {
+ vmess(&gl->gl_pd, gensym("bounds"), "ffff",
+ 0., gl->gl_y1, (double)(n > 1 ? n-1 : 1), gl->gl_y2);
+ /* close any dialogs that might have the wrong info now... */
+ gfxstub_deleteforkey(gl);
+ }
+ else garray_redraw(x);
+ if (x->x_usedindsp) canvas_update_dsp();
+}
+
+static void garray_print(t_garray *x)
+{
+ post("garray %s: template %s, length %d",
+ x->x_name->s_name, x->x_templatesym->s_name, x->x_n);
+}
+
+void g_array_setup(void)
+{
+ garray_class = class_new(gensym("array"), 0, (t_method)garray_free,
+ sizeof(t_garray), CLASS_GOBJ, 0);
+ class_setwidget(garray_class, &garray_widgetbehavior);
+ class_addmethod(garray_class, (t_method)garray_const, gensym("const"),
+ A_DEFFLOAT, A_NULL);
+ class_addlist(garray_class, garray_list);
+ class_addmethod(garray_class, (t_method)garray_bounds, gensym("bounds"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(garray_class, (t_method)garray_xticks, gensym("xticks"),
+ A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(garray_class, (t_method)garray_xlabel, gensym("xlabel"),
+ A_GIMME, 0);
+ class_addmethod(garray_class, (t_method)garray_yticks, gensym("yticks"),
+ A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(garray_class, (t_method)garray_ylabel, gensym("ylabel"),
+ A_GIMME, 0);
+ class_addmethod(garray_class, (t_method)garray_rename, gensym("rename"),
+ A_SYMBOL, 0);
+ class_addmethod(garray_class, (t_method)garray_read, gensym("read"),
+ A_SYMBOL, A_NULL);
+ class_addmethod(garray_class, (t_method)garray_read16, gensym("read16"),
+ A_SYMBOL, A_DEFFLOAT, A_DEFSYM, A_NULL);
+ class_addmethod(garray_class, (t_method)garray_write, gensym("write"),
+ A_SYMBOL, A_NULL);
+ class_addmethod(garray_class, (t_method)garray_write16, gensym("write16"),
+ A_SYMBOL, A_DEFSYM, A_NULL);
+ class_addmethod(garray_class, (t_method)garray_resize, gensym("resize"),
+ A_FLOAT, A_NULL);
+ class_addmethod(garray_class, (t_method)garray_print, gensym("print"),
+ A_NULL);
+ class_addmethod(garray_class, (t_method)garray_sinesum, gensym("sinesum"),
+ A_GIMME, 0);
+ class_addmethod(garray_class, (t_method)garray_cosinesum,
+ gensym("cosinesum"), A_GIMME, 0);
+ class_addmethod(garray_class, (t_method)garray_normalize,
+ gensym("normalize"), A_DEFFLOAT, 0);
+ class_addmethod(garray_class, (t_method)garray_arraydialog,
+ gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+}
+
+
diff --git a/pd/src/g_bang.c b/pd/src/g_bang.c
new file mode 100644
index 00000000..cb92c685
--- /dev/null
+++ b/pd/src/g_bang.c
@@ -0,0 +1,600 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */
+/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include "t_tk.h"
+#include "g_all_guis.h"
+#include <math.h>
+
+#ifdef NT
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+
+/* --------------- bng gui-bang ------------------------- */
+
+t_widgetbehavior bng_widgetbehavior;
+static t_class *bng_class;
+
+/* widget helper functions */
+
+
+void bng_draw_update(t_bng *x, t_glist *glist)
+{
+ if(glist_isvisible(glist))
+ {
+ sys_vgui(".x%x.c itemconfigure %xBUT -fill #%6.6x\n", glist_getcanvas(glist), x,
+ x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol);
+ }
+}
+
+void bng_draw_new(t_bng *x, t_glist *glist)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n",
+ canvas, xpos, ypos,
+ xpos + x->x_gui.x_w, ypos + x->x_gui.x_h,
+ x->x_gui.x_bcol, x);
+ sys_vgui(".x%x.c create oval %d %d %d %d -fill #%6.6x -tags %xBUT\n",
+ canvas, xpos+1, ypos+1,
+ xpos + x->x_gui.x_w-1, ypos + x->x_gui.x_h-1,
+ x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol, x);
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n",
+ canvas, xpos+x->x_gui.x_ldx,
+ ypos+x->x_gui.x_ldy,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"",
+ x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas, xpos,
+ ypos + x->x_gui.x_h-1, xpos + IOWIDTH,
+ ypos + x->x_gui.x_h, x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas, xpos, ypos,
+ xpos + IOWIDTH, ypos+1, x, 0);
+}
+
+void bng_draw_move(t_bng *x, t_glist *glist)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n",
+ canvas, x, xpos, ypos,
+ xpos + x->x_gui.x_w, ypos + x->x_gui.x_h);
+ sys_vgui(".x%x.c coords %xBUT %d %d %d %d\n",
+ canvas, x, xpos+1,ypos+1,
+ xpos + x->x_gui.x_w-1, ypos + x->x_gui.x_h-1);
+ sys_vgui(".x%x.c itemconfigure %xBUT -fill #%6.6x\n", canvas, x,
+ x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol);
+ sys_vgui(".x%x.c coords %xLABEL %d %d\n",
+ canvas, x, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n",
+ canvas, x, 0, xpos,
+ ypos + x->x_gui.x_h-1, xpos + IOWIDTH,
+ ypos + x->x_gui.x_h);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n",
+ canvas, x, 0, xpos, ypos,
+ xpos + IOWIDTH, ypos+1);
+}
+
+void bng_draw_erase(t_bng* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c delete %xBASE\n", canvas, x);
+ sys_vgui(".x%x.c delete %xBUT\n", canvas, x);
+ sys_vgui(".x%x.c delete %xLABEL\n", canvas, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+void bng_draw_config(t_bng* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n",
+ canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"");
+ sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, x, x->x_gui.x_bcol);
+ sys_vgui(".x%x.c itemconfigure %xBUT -fill #%6.6x\n", canvas, x,
+ x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol);
+}
+
+void bng_draw_io(t_bng* x, t_glist* glist, int old_snd_rcv_flags)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas, xpos,
+ ypos + x->x_gui.x_h-1, xpos + IOWIDTH,
+ ypos + x->x_gui.x_h, x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas, xpos, ypos,
+ xpos + IOWIDTH, ypos+1, x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+void bng_draw_select(t_bng* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if(x->x_gui.x_fsf.x_selected)
+ {
+ pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ sys_vgui(".x%x.c itemconfigure %xBUT -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ }
+ else
+ {
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL);
+ sys_vgui(".x%x.c itemconfigure %xBUT -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL);
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol);
+ }
+}
+
+void bng_draw(t_bng *x, t_glist *glist, int mode)
+{
+ if(mode == IEM_GUI_DRAW_MODE_UPDATE)
+ bng_draw_update(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_MOVE)
+ bng_draw_move(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_NEW)
+ bng_draw_new(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_SELECT)
+ bng_draw_select(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_ERASE)
+ bng_draw_erase(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_CONFIG)
+ bng_draw_config(x, glist);
+ else if(mode >= IEM_GUI_DRAW_MODE_IO)
+ bng_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO);
+}
+
+/* ------------------------ bng widgetbehaviour----------------------------- */
+
+static void bng_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_bng *x = (t_bng *)z;
+
+ *xp1 = text_xpix(&x->x_gui.x_obj, glist);
+ *yp1 = text_ypix(&x->x_gui.x_obj, glist);
+ *xp2 = *xp1 + x->x_gui.x_w;
+ *yp2 = *yp1 + x->x_gui.x_h;
+}
+
+static void bng_save(t_gobj *z, t_binbuf *b)
+{
+ t_bng *x = (t_bng *)z;
+ int bflcol[3], *ip1, *ip2;
+ t_symbol *srl[3];
+
+ iemgui_save(&x->x_gui, srl, bflcol);
+ ip1 = (int *)(&x->x_gui.x_isa);
+ ip2 = (int *)(&x->x_gui.x_fsf);
+ binbuf_addv(b, "ssiisiiiisssiiiiiii", gensym("#X"),gensym("obj"),
+ (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix,
+ gensym("bng"), x->x_gui.x_w,
+ x->x_flashtime_hold, x->x_flashtime_break,
+ (*ip1)&IEM_INIT_ARGS_ALL,
+ srl[0], srl[1], srl[2],
+ x->x_gui.x_ldx, x->x_gui.x_ldy,
+ (*ip2)&IEM_FSTYLE_FLAGS_ALL, x->x_gui.x_fontsize,
+ bflcol[0], bflcol[1], bflcol[2]);
+ binbuf_addv(b, ";");
+}
+
+void bng_check_minmax(t_bng *x, int ftbreak, int fthold)
+{
+ if(ftbreak > fthold)
+ {
+ int h;
+
+ h = ftbreak;
+ ftbreak = fthold;
+ fthold = h;
+ }
+ if(ftbreak < IEM_BNG_MINBREAKFLASHTIME)
+ ftbreak = IEM_BNG_MINBREAKFLASHTIME;
+ if(fthold < IEM_BNG_MINHOLDFLASHTIME)
+ fthold = IEM_BNG_MINHOLDFLASHTIME;
+ x->x_flashtime_break = ftbreak;
+ x->x_flashtime_hold = fthold;
+}
+
+static void bng_properties(t_gobj *z, t_glist *owner)
+{
+ t_bng *x = (t_bng *)z;
+ char buf[800];
+ t_symbol *srl[3];
+
+ iemgui_properties(&x->x_gui, srl);
+ sprintf(buf, "pdtk_iemgui_dialog %%s BANG \
+ ----------dimensions(pix):----------- %d %d size: 0 0 empty \
+ --------flash-time(ms)(ms):--------- %d intrrpt: %d hold: %d \
+ %d empty empty %d %d empty %d \
+ %s %s \
+ %s %d %d \
+ %d %d \
+ %d %d %d\n",
+ x->x_gui.x_w, IEM_GUI_MINSIZE,
+ x->x_flashtime_break, x->x_flashtime_hold, 2,/*min_max_schedule+clip*/
+ -1, x->x_gui.x_isa.x_loadinit, -1, -1,/*no linlog, no multi*/
+ srl[0]->s_name, srl[1]->s_name,
+ srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy,
+ x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize,
+ 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol);
+ gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf);
+}
+
+static void bng_set(t_bng *x)
+{
+ if(x->x_flashed)
+ {
+ x->x_flashed = 0;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ clock_delay(x->x_clock_brk, x->x_flashtime_break);
+ x->x_flashed = 1;
+ }
+ else
+ {
+ x->x_flashed = 1;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ }
+ clock_delay(x->x_clock_hld, x->x_flashtime_hold);
+}
+
+static void bng_bout1(t_bng *x)/*wird nur mehr gesendet, wenn snd != rcv*/
+{
+ if(!x->x_gui.x_fsf.x_put_in2out)
+ {
+ x->x_gui.x_isa.x_locked = 1;
+ clock_delay(x->x_clock_lck, 2);
+ }
+ outlet_bang(x->x_gui.x_obj.ob_outlet);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing && x->x_gui.x_fsf.x_put_in2out)
+ pd_bang(x->x_gui.x_snd->s_thing);
+}
+
+static void bng_bout2(t_bng *x)/*wird immer gesendet, wenn moeglich*/
+{
+ if(!x->x_gui.x_fsf.x_put_in2out)
+ {
+ x->x_gui.x_isa.x_locked = 1;
+ clock_delay(x->x_clock_lck, 2);
+ }
+ outlet_bang(x->x_gui.x_obj.ob_outlet);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_bang(x->x_gui.x_snd->s_thing);
+}
+
+static void bng_bang(t_bng *x)/*wird nur mehr gesendet, wenn snd != rcv*/
+{
+ if(!x->x_gui.x_isa.x_locked)
+ {
+ bng_set(x);
+ bng_bout1(x);
+ }
+}
+
+static void bng_bang2(t_bng *x)/*wird immer gesendet, wenn moeglich*/
+{
+ if(!x->x_gui.x_isa.x_locked)
+ {
+ bng_set(x);
+ bng_bout2(x);
+ }
+}
+
+static void bng_dialog(t_bng *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *srl[3];
+ int a = (int)atom_getintarg(0, argc, argv);
+ int fthold = (int)atom_getintarg(2, argc, argv);
+ int ftbreak = (int)atom_getintarg(3, argc, argv);
+ int sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);
+
+ x->x_gui.x_w = iemgui_clip_size(a);
+ x->x_gui.x_h = x->x_gui.x_w;
+ bng_check_minmax(x, ftbreak, fthold);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+}
+
+static void bng_click(t_bng *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
+{
+ bng_set(x);
+ bng_bout2(x);
+}
+
+static int bng_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ if(doit)
+ bng_click((t_bng *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt);
+ return (1);
+}
+
+static void bng_float(t_bng *x, t_floatarg f)
+{bng_bang2(x);}
+
+static void bng_symbol(t_bng *x, t_symbol *s)
+{bng_bang2(x);}
+
+static void bng_pointer(t_bng *x, t_gpointer *gp)
+{bng_bang2(x);}
+
+static void bng_list(t_bng *x, t_symbol *s, int ac, t_atom *av)
+{
+ int l=iemgui_list((void *)x, &x->x_gui, s, ac, av);
+
+ if(l < 0)
+ {
+ bng_bang2(x);
+ }
+ else if(l > 0)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+ }
+}
+
+static void bng_anything(t_bng *x, t_symbol *s, int argc, t_atom *argv)
+{bng_bang2(x);}
+
+static void bng_loadbang(t_bng *x)
+{
+ if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit)
+ {
+ bng_set(x);
+ bng_bout2(x);
+ }
+}
+
+static void bng_size(t_bng *x, t_symbol *s, int ac, t_atom *av)
+{
+ x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av));
+ x->x_gui.x_h = x->x_gui.x_w;
+ iemgui_size((void *)x, &x->x_gui);
+}
+
+static void bng_delta(t_bng *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}
+
+static void bng_pos(t_bng *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void bng_flashtime(t_bng *x, t_symbol *s, int ac, t_atom *av)
+{
+ bng_check_minmax(x, (int)atom_getintarg(0, ac, av),
+ (int)atom_getintarg(1, ac, av));
+}
+
+static void bng_color(t_bng *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_color((void *)x, &x->x_gui, s, ac, av);}
+
+static void bng_send(t_bng *x, t_symbol *s)
+{iemgui_send(x, &x->x_gui, s);}
+
+static void bng_receive(t_bng *x, t_symbol *s)
+{iemgui_receive(x, &x->x_gui, s);}
+
+static void bng_label(t_bng *x, t_symbol *s)
+{iemgui_label((void *)x, &x->x_gui, s);}
+
+static void bng_label_pos(t_bng *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void bng_label_font(t_bng *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);}
+
+static void bng_init(t_bng *x, t_floatarg f)
+{
+ x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1;
+}
+
+static void bng_tick_hld(t_bng *x)
+{
+ x->x_flashed = 0;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+}
+
+static void bng_tick_brk(t_bng *x)
+{
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+}
+
+static void bng_tick_lck(t_bng *x)
+{
+ x->x_gui.x_isa.x_locked = 0;
+}
+
+static void *bng_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_bng *x = (t_bng *)pd_new(bng_class);
+ int bflcol[]={-262144, -1, -1};
+ t_symbol *srl[3];
+ int a=IEM_GUI_DEFAULTSIZE;
+ int ldx=0, ldy=-6;
+ int fs=8, iinit=0, ifstyle=0;
+ int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME;
+ t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit);
+ t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle);
+ char str[144];
+
+ srl[0] = gensym("empty");
+ srl[1] = gensym("empty");
+ srl[2] = gensym("empty");
+
+ if((argc == 14)&&IS_A_FLOAT(argv,0)
+ &&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2)
+ &&IS_A_FLOAT(argv,3)
+ &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4))
+ &&(IS_A_SYMBOL(argv,5)||IS_A_FLOAT(argv,5))
+ &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6))
+ &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8)
+ &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,11)
+ &&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13))
+ {
+
+ a = (int)atom_getintarg(0, argc, argv);
+ fthold = (int)atom_getintarg(1, argc, argv);
+ ftbreak = (int)atom_getintarg(2, argc, argv);
+ iinit = (int)(atom_getintarg(3, argc, argv));
+ if(IS_A_SYMBOL(argv,4))
+ srl[0] = atom_getsymbolarg(4, argc, argv);
+ else if(IS_A_FLOAT(argv,4))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(4, argc, argv));
+ srl[0] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,5))
+ srl[1] = atom_getsymbolarg(5, argc, argv);
+ else if(IS_A_FLOAT(argv,5))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(5, argc, argv));
+ srl[1] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,6))
+ srl[2] = atom_getsymbolarg(6, argc, argv);
+ else if(IS_A_FLOAT(argv,6))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(6, argc, argv));
+ srl[2] = gensym(str);
+ }
+ ldx = (int)atom_getintarg(7, argc, argv);
+ ldy = (int)atom_getintarg(8, argc, argv);
+ ifstyle = (int)(atom_getintarg(9, argc, argv));
+ fs = (int)atom_getintarg(10, argc, argv);
+ bflcol[0] = (int)atom_getintarg(11, argc, argv);
+ bflcol[1] = (int)atom_getintarg(12, argc, argv);
+ bflcol[2] = (int)atom_getintarg(13, argc, argv);
+ }
+
+ x->x_gui.x_draw = (t_iemfunptr)bng_draw;
+ iinit &= IEM_INIT_ARGS_ALL;
+ ifstyle &= IEM_FSTYLE_FLAGS_ALL;
+
+ fstyle->x_snd_able = 1;
+ fstyle->x_rcv_able = 1;
+ x->x_flashed = 0;
+ x->x_gui.x_glist = (t_glist *)canvas_getcurrent();
+ x->x_gui.x_isa = *init;
+ if(!strcmp(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0;
+ if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0;
+ x->x_gui.x_unique_num = 0;
+ if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica");
+ else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times");
+ else { fstyle->x_font_style = 0;
+ strcpy(x->x_gui.x_font, "courier"); }
+ x->x_gui.x_fsf = *fstyle;
+ iemgui_first_dollararg2sym(&x->x_gui, srl);
+
+ if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]);
+ x->x_gui.x_snd = srl[0];
+ x->x_gui.x_rcv = srl[1];
+ x->x_gui.x_lab = srl[2];
+ x->x_gui.x_ldx = ldx;
+ x->x_gui.x_ldy = ldy;
+
+ if(fs < 4)
+ fs = 4;
+ x->x_gui.x_fontsize = fs;
+ x->x_gui.x_w = iemgui_clip_size(a);
+ x->x_gui.x_h = x->x_gui.x_w;
+ bng_check_minmax(x, ftbreak, fthold);
+ iemgui_all_colfromload(&x->x_gui, bflcol);
+ x->x_gui.x_isa.x_locked = 0;
+ iemgui_verify_snd_ne_rcv(&x->x_gui);
+ x->x_clock_hld = clock_new(x, (t_method)bng_tick_hld);
+ x->x_clock_brk = clock_new(x, (t_method)bng_tick_brk);
+ x->x_clock_lck = clock_new(x, (t_method)bng_tick_lck);
+ outlet_new(&x->x_gui.x_obj, &s_bang);
+ return (x);
+}
+
+static void bng_ff(t_bng *x)
+{
+ if(x->x_gui.x_fsf.x_selected)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ if(x->x_gui.x_fsf.x_rcv_able)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);
+ clock_free(x->x_clock_lck);
+ clock_free(x->x_clock_brk);
+ clock_free(x->x_clock_hld);
+ gfxstub_deleteforkey(x);
+}
+
+void g_bang_setup(void)
+{
+ bng_class = class_new(gensym("bng"), (t_newmethod)bng_new,
+ (t_method)bng_ff, sizeof(t_bng), 0, A_GIMME, 0);
+ class_addbang(bng_class, bng_bang);
+ class_addfloat(bng_class, bng_float);
+ class_addsymbol(bng_class, bng_symbol);
+ class_addpointer(bng_class, bng_pointer);
+ class_addlist(bng_class, bng_list);
+ class_addanything(bng_class, bng_anything);
+ class_addmethod(bng_class, (t_method)bng_click, gensym("click"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(bng_class, (t_method)bng_dialog, gensym("dialog"),
+ A_GIMME, 0);
+ class_addmethod(bng_class, (t_method)bng_loadbang, gensym("loadbang"), 0);
+ class_addmethod(bng_class, (t_method)bng_size, gensym("size"), A_GIMME, 0);
+ class_addmethod(bng_class, (t_method)bng_delta, gensym("delta"), A_GIMME, 0);
+ class_addmethod(bng_class, (t_method)bng_pos, gensym("pos"), A_GIMME, 0);
+ class_addmethod(bng_class, (t_method)bng_flashtime, gensym("flashtime"), A_GIMME, 0);
+ class_addmethod(bng_class, (t_method)bng_color, gensym("color"), A_GIMME, 0);
+ class_addmethod(bng_class, (t_method)bng_send, gensym("send"), A_DEFSYM, 0);
+ class_addmethod(bng_class, (t_method)bng_receive, gensym("receive"), A_DEFSYM, 0);
+ class_addmethod(bng_class, (t_method)bng_label, gensym("label"), A_DEFSYM, 0);
+ class_addmethod(bng_class, (t_method)bng_label_pos, gensym("label_pos"), A_GIMME, 0);
+ class_addmethod(bng_class, (t_method)bng_label_font, gensym("label_font"), A_GIMME, 0);
+ class_addmethod(bng_class, (t_method)bng_init, gensym("init"), A_FLOAT, 0);
+ if(!iemgui_key_sym)
+ iemgui_key_sym = gensym("#keyname");
+ bng_widgetbehavior.w_getrectfn = bng_getrect;
+ bng_widgetbehavior.w_displacefn = iemgui_displace;
+ bng_widgetbehavior.w_selectfn = iemgui_select;
+ bng_widgetbehavior.w_activatefn = NULL;
+ bng_widgetbehavior.w_deletefn = iemgui_delete;
+ bng_widgetbehavior.w_visfn = iemgui_vis;
+ bng_widgetbehavior.w_clickfn = bng_newclick;
+ bng_widgetbehavior.w_propertiesfn = bng_properties;
+ bng_widgetbehavior.w_savefn = bng_save;
+ class_setwidget(bng_class, &bng_widgetbehavior);
+ class_sethelpsymbol(bng_class, gensym("bng"));
+}
diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c
new file mode 100644
index 00000000..c63b2c0b
--- /dev/null
+++ b/pd/src/g_canvas.c
@@ -0,0 +1,1482 @@
+/* Copyright (c) 1997-2001 Miller Puckette and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* this file defines the "glist" class, also known as "canvas" (the two used
+to be different but are now unified except for some fossilized names.) */
+
+/* changes by Thomas Musil IEM KUG Graz Austria 2001 */
+
+/* improvement: line-delete-protection, look for "protect" */
+/* bug-fix: canvas_menuclose(): by Krzysztof Czaja */
+/* bug-fix: table_new(): I reversed the y-bounds */
+
+/* IOhannes :
+ * changed the canvas_restore, so that it might accept $args as well
+ * (like "pd $0_test")
+ * so you can make multiple & distinguishable templates
+ * 1511:forum::für::umläute:2001
+ * changes marked with IOhannes
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include <string.h>
+#include "g_all_guis.h"
+
+struct _canvasenvironment
+{
+ t_symbol *ce_dir; /* directory patch lives in */
+ int ce_argc; /* number of "$" arguments */
+ t_atom *ce_argv; /* array of "$" arguments */
+ int ce_dollarzero; /* value of "$0" */
+};
+
+#define GLIST_DEFCANVASWIDTH 450
+#define GLIST_DEFCANVASHEIGHT 300
+
+#ifdef MACOSX
+#define GLIST_DEFCANVASYLOC 20
+#else
+#define GLIST_DEFCANVASYLOC 0
+#endif
+
+/* ---------------------- variables --------------------------- */
+
+extern t_pd *newest;
+t_class *canvas_class;
+static int canvas_dspstate; /* whether DSP is on or off */
+t_canvas *canvas_editing; /* last canvas to start text edting */
+t_canvas *canvas_whichfind; /* last canvas we did a find in */
+t_canvas *canvas_list; /* list of all root canvases */
+
+/* ------------------ forward function declarations --------------- */
+static void canvas_start_dsp(void);
+static void canvas_stop_dsp(void);
+static void canvas_drawlines(t_canvas *x);
+static void canvas_setbounds(t_canvas *x, int x1, int y1, int x2, int y2);
+static void canvas_reflecttitle(t_canvas *x);
+static void canvas_addtolist(t_canvas *x);
+static void canvas_takeofflist(t_canvas *x);
+static void canvas_pop(t_canvas *x, t_floatarg fvis);
+void canvas_create_editor(t_glist *x, int createit);
+
+/* --------- functions to handle the canvas environment ----------- */
+
+static t_symbol *canvas_newfilename = &s_;
+static t_symbol *canvas_newdirectory = &s_;
+static int canvas_newargc;
+static t_atom *canvas_newargv;
+
+static void glist_doupdatewindowlist(t_glist *gl, char *sbuf)
+{
+ t_gobj *g;
+ if (!gl->gl_owner)
+ {
+ /* this is a canvas; if we have a window, put on "windows" list */
+ t_canvas *canvas = (t_canvas *)gl;
+ if (canvas->gl_havewindow)
+ {
+ if (strlen(sbuf) + strlen(gl->gl_name->s_name) + 100 <= 1024)
+ {
+ char tbuf[1024];
+ sprintf(tbuf, "{%s .x%x} ", gl->gl_name->s_name, (t_int)canvas);
+ strcat(sbuf, tbuf);
+ }
+ }
+ }
+ for (g = gl->gl_list; g; g = g->g_next)
+ {
+ if (pd_class(&g->g_pd) == canvas_class)
+ glist_doupdatewindowlist((t_glist *)g, sbuf);
+ }
+ return;
+}
+
+ /* maintain the list of visible toplevels for the GUI's "windows" menu */
+void canvas_updatewindowlist( void)
+{
+ t_canvas *x;
+ char sbuf[1024];
+ strcpy(sbuf, "set menu_windowlist {");
+ /* find all root canvases */
+ for (x = canvas_list; x; x = x->gl_next)
+ glist_doupdatewindowlist(x, sbuf);
+ strcat(sbuf, "}\n");
+ sys_gui(sbuf);
+}
+
+ /* add a glist the list of "root" canvases (toplevels without parents.) */
+static void canvas_addtolist(t_canvas *x)
+{
+ x->gl_next = canvas_list;
+ canvas_list = x;
+}
+
+static void canvas_takeofflist(t_canvas *x)
+{
+ /* take it off the window list */
+ if (x == canvas_list) canvas_list = x->gl_next;
+ else
+ {
+ t_canvas *z;
+ for (z = canvas_list; z->gl_next != x; z = z->gl_next)
+ ;
+ z->gl_next = x->gl_next;
+ }
+}
+
+
+void canvas_setargs(int argc, t_atom *argv)
+{
+ /* if there's an old one lying around free it here. This
+ happens if an abstraction is loaded but never gets as far
+ as calling canvas_new(). */
+ if (canvas_newargv)
+ freebytes(canvas_newargv, canvas_newargc * sizeof(t_atom));
+ canvas_newargc = argc;
+ canvas_newargv = copybytes(argv, argc * sizeof(t_atom));
+}
+
+void glob_setfilename(void *dummy, t_symbol *filesym, t_symbol *dirsym)
+{
+ canvas_newfilename = filesym;
+ canvas_newdirectory = dirsym;
+}
+
+t_canvas *canvas_getcurrent(void)
+{
+ return ((t_canvas *)pd_findbyclass(&s__X, canvas_class));
+}
+
+void canvas_setcurrent(t_canvas *x)
+{
+ pd_pushsym(&x->gl_pd);
+}
+
+void canvas_unsetcurrent(t_canvas *x)
+{
+ pd_popsym(&x->gl_pd);
+}
+
+t_canvasenvironment *canvas_getenv(t_canvas *x)
+{
+ if (!x) bug("canvas_getenv");
+ while (!x->gl_env)
+ if (!(x = x->gl_owner))
+ bug("t_canvasenvironment", x);
+ return (x->gl_env);
+}
+
+int canvas_getdollarzero( void)
+{
+ t_canvas *x = canvas_getcurrent();
+ t_canvasenvironment *env = (x ? canvas_getenv(x) : 0);
+ if (env)
+ return (env->ce_dollarzero);
+ else return (0);
+}
+
+void canvas_getargs(int *argcp, t_atom **argvp)
+{
+ t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());
+ *argcp = e->ce_argc;
+ *argvp = e->ce_argv;
+}
+
+t_symbol *realizedollsym(t_symbol *s, int ac, t_atom *av, int tonew);
+
+t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s)
+{
+ t_symbol *ret;
+ char *name = s->s_name;
+ if (*name == '$' && name[1] >= '0' && name[1] <= '9')
+ {
+ t_canvasenvironment *env = canvas_getenv(x);
+ canvas_setcurrent(x);
+ ret = realizedollsym(gensym(name+1), env->ce_argc, env->ce_argv, 1);
+ canvas_unsetcurrent(x);
+ }
+ else ret = s;
+ return (ret);
+}
+
+t_symbol *canvas_getcurrentdir(void)
+{
+ t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());
+ return (e->ce_dir);
+}
+
+t_symbol *canvas_getdir(t_canvas *x)
+{
+ t_canvasenvironment *e = canvas_getenv(x);
+ return (e->ce_dir);
+}
+
+void canvas_makefilename(t_canvas *x, char *file, char *result, int resultsize)
+{
+ char *dir = canvas_getenv(x)->ce_dir->s_name;
+ if (file[0] == '/' || (file[0] && file[1] == ':') || !*dir)
+ {
+ strncpy(result, file, resultsize);
+ result[resultsize-1] = 0;
+ }
+ else
+ {
+ int nleft;
+ strncpy(result, dir, resultsize);
+ result[resultsize-1] = 0;
+ nleft = resultsize - strlen(result) - 1;
+ if (nleft <= 0) return;
+ strcat(result, "/");
+ strncat(result, file, nleft);
+ result[resultsize-1] = 0;
+ }
+}
+
+void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir)
+{
+ if (strcmp(x->gl_name->s_name, "Pd"))
+ pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name));
+ x->gl_name = s;
+ if (strcmp(x->gl_name->s_name, "Pd"))
+ pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name));
+ if (glist_isvisible(x))
+ canvas_reflecttitle(x);
+ if (dir && dir != &s_)
+ {
+ t_canvasenvironment *e = canvas_getenv(x);
+ e->ce_dir = dir;
+ }
+}
+
+/* --------------- traversing the set of lines in a canvas ----------- */
+
+void linetraverser_start(t_linetraverser *t, t_canvas *x)
+{
+ t->tr_ob = 0;
+ t->tr_x = x;
+ t->tr_nextoc = 0;
+ t->tr_nextoutno = t->tr_nout = 0;
+}
+
+t_outconnect *linetraverser_next(t_linetraverser *t)
+{
+ t_outconnect *rval = t->tr_nextoc;
+ int outno;
+ while (!rval)
+ {
+ outno = t->tr_nextoutno;
+ while (outno == t->tr_nout)
+ {
+ t_gobj *y;
+ t_object *ob = 0;
+ if (!t->tr_ob) y = t->tr_x->gl_list;
+ else y = t->tr_ob->ob_g.g_next;
+ for (; y; y = y->g_next)
+ if (ob = pd_checkobject(&y->g_pd)) break;
+ if (!ob) return (0);
+ t->tr_ob = ob;
+ t->tr_nout = obj_noutlets(ob);
+ outno = 0;
+ if (glist_isvisible(t->tr_x))
+ gobj_getrect(y, t->tr_x,
+ &t->tr_x11, &t->tr_y11, &t->tr_x12, &t->tr_y12);
+ else t->tr_x11 = t->tr_y11 = t->tr_x12 = t->tr_y12 = 0;
+ }
+ t->tr_nextoutno = outno + 1;
+ rval = obj_starttraverseoutlet(t->tr_ob, &t->tr_outlet, outno);
+ t->tr_outno = outno;
+ }
+ t->tr_nextoc = obj_nexttraverseoutlet(rval, &t->tr_ob2,
+ &t->tr_inlet, &t->tr_inno);
+ t->tr_nin = obj_ninlets(t->tr_ob2);
+ if (!t->tr_nin) bug("drawline");
+ if (glist_isvisible(t->tr_x))
+ {
+ int inplus = (t->tr_nin == 1 ? 1 : t->tr_nin - 1);
+ int outplus = (t->tr_nout == 1 ? 1 : t->tr_nout - 1);
+ gobj_getrect(&t->tr_ob2->ob_g, t->tr_x,
+ &t->tr_x21, &t->tr_y21, &t->tr_x22, &t->tr_y22);
+ t->tr_lx1 = t->tr_x11 +
+ ((t->tr_x12 - t->tr_x11 - IOWIDTH) * t->tr_outno) /
+ outplus + IOMIDDLE;
+ t->tr_ly1 = t->tr_y12;
+ t->tr_lx2 = t->tr_x21 +
+ ((t->tr_x22 - t->tr_x21 - IOWIDTH) * t->tr_inno)/inplus +
+ IOMIDDLE;
+ t->tr_ly2 = t->tr_y21;
+ }
+ else
+ {
+ t->tr_x21 = t->tr_y21 = t->tr_x22 = t->tr_y22 = 0;
+ t->tr_lx1 = t->tr_ly1 = t->tr_lx2 = t->tr_ly2 = 0;
+ }
+
+ return (rval);
+}
+
+void linetraverser_skipobject(t_linetraverser *t)
+{
+ t->tr_nextoc = 0;
+ t->tr_nextoutno = t->tr_nout;
+}
+
+/* -------------------- the canvas object -------------------------- */
+int glist_valid = 10000;
+
+void glist_init(t_glist *x)
+{
+ /* zero out everyone except "pd" field */
+ memset(((char *)x) + sizeof(x->gl_pd), 0, sizeof(*x) - sizeof(x->gl_pd));
+ x->gl_stub = gstub_new(x, 0);
+ x->gl_valid = ++glist_valid;
+ x->gl_xlabel = (t_symbol **)t_getbytes(0);
+ x->gl_ylabel = (t_symbol **)t_getbytes(0);
+}
+
+ /* make a new glist. It will either be a "root" canvas or else
+ its parent will be a "text" object in another window... we don't
+ know which yet. */
+t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)
+{
+ t_canvas *x = (t_canvas *)pd_new(canvas_class);
+ t_canvas *owner = canvas_getcurrent();
+ t_symbol *s = &s_;
+ int vis = 0, width = GLIST_DEFCANVASWIDTH, height = GLIST_DEFCANVASHEIGHT;
+ int xloc = 0, yloc = GLIST_DEFCANVASYLOC;
+ int font = (owner ? owner->gl_font : sys_defaultfont);
+ glist_init(x);
+ x->gl_obj.te_type = T_OBJECT;
+ if (!owner)
+ canvas_addtolist(x);
+ /* post("canvas %x, owner %x", x, owner); */
+
+ if (argc == 5) /* toplevel: x, y, w, h, font */
+ {
+ xloc = atom_getintarg(0, argc, argv);
+ yloc = atom_getintarg(1, argc, argv);
+ width = atom_getintarg(2, argc, argv);
+ height = atom_getintarg(3, argc, argv);
+ font = atom_getintarg(4, argc, argv);
+ }
+ else if (argc == 6) /* subwindow: x, y, w, h, name, vis */
+ {
+ xloc = atom_getintarg(0, argc, argv);
+ yloc = atom_getintarg(1, argc, argv);
+ width = atom_getintarg(2, argc, argv);
+ height = atom_getintarg(3, argc, argv);
+ s = atom_getsymbolarg(4, argc, argv);
+ vis = atom_getintarg(5, argc, argv);
+ }
+ /* (otherwise assume we're being created from the menu.) */
+
+ if (canvas_newdirectory->s_name[0])
+ {
+ static int dollarzero = 1000;
+ t_canvasenvironment *env = x->gl_env =
+ (t_canvasenvironment *)getbytes(sizeof(*x->gl_env));
+ env->ce_dir = canvas_newdirectory;
+ env->ce_argc = canvas_newargc;
+ env->ce_argv = canvas_newargv;
+ env->ce_dollarzero = dollarzero++;
+ canvas_newdirectory = &s_;
+ canvas_newargc = 0;
+ canvas_newargv = 0;
+ }
+ else x->gl_env = 0;
+
+ x->gl_x1 = 0;
+ x->gl_y1 = 0;
+ x->gl_x2 = 1;
+ x->gl_y2 = 1;
+ canvas_setbounds(x, xloc, yloc, xloc + width, yloc + height);
+ x->gl_owner = owner;
+ x->gl_name = (*s->s_name ? s :
+ (canvas_newfilename ? canvas_newfilename : gensym("Pd")));
+ if (strcmp(x->gl_name->s_name, "Pd"))
+ pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name));
+ x->gl_loading = 1;
+ x->gl_willvis = vis;
+ x->gl_edit = !strncmp(x->gl_name->s_name, "Untitled", 8);
+ x->gl_font = sys_nearestfontsize(font);
+ pd_pushsym(&x->gl_pd);
+ return(x);
+}
+
+void canvas_setgraph(t_glist *x, int flag);
+
+static void canvas_coords(t_glist *x, t_symbol *s, int argc, t_atom *argv)
+{
+ x->gl_x1 = atom_getfloatarg(0, argc, argv);
+ x->gl_y1 = atom_getfloatarg(1, argc, argv);
+ x->gl_x2 = atom_getfloatarg(2, argc, argv);
+ x->gl_y2 = atom_getfloatarg(3, argc, argv);
+ x->gl_pixwidth = atom_getintarg(4, argc, argv);
+ x->gl_pixheight = atom_getintarg(5, argc, argv);
+ canvas_setgraph(x, atom_getintarg(6, argc, argv));
+}
+
+ /* make a new glist and add it to this glist. It will appear as
+ a "graph", not a text object. */
+t_glist *glist_addglist(t_glist *g, t_symbol *sym,
+ float x1, float y1, float x2, float y2,
+ float px1, float py1, float px2, float py2)
+{
+ static int gcount = 0;
+ int zz;
+ int menu = 0;
+ char *str;
+ t_glist *x = (t_glist *)pd_new(canvas_class);
+ glist_init(x);
+ x->gl_obj.te_type = T_OBJECT;
+ if (!*sym->s_name)
+ {
+ char buf[40];
+ sprintf(buf, "graph%d", ++gcount);
+ sym = gensym(buf);
+ menu = 1;
+ }
+ else if (!strncmp((str = sym->s_name), "graph", 5)
+ && (zz = atoi(str + 5)) > gcount)
+ gcount = zz;
+ /* in 0.34 and earlier, the pixel rectangle and the y bounds were
+ reversed; this would behave the same, except that the dialog window
+ would be confusing. The "correct" way is to have "py1" be the value
+ that is higher on the screen. */
+ if (py2 < py1)
+ {
+ float zz;
+ zz = y2;
+ y2 = y1;
+ y1 = zz;
+ zz = py2;
+ py2 = py1;
+ py1 = zz;
+ }
+ if (x1 == x2 || y1 == y2)
+ x1 = 0, x2 = 100, y1 = 1, y2 = -1;
+ if (px1 >= px2 || py1 >= py2)
+ px1 = 100, py1 = 20, px2 = 100 + GLIST_DEFGRAPHWIDTH,
+ py2 = 20 + GLIST_DEFGRAPHHEIGHT;
+ x->gl_name = sym;
+ x->gl_x1 = x1;
+ x->gl_x2 = x2;
+ x->gl_y1 = y1;
+ x->gl_y2 = y2;
+ x->gl_obj.te_xpix = px1;
+ x->gl_obj.te_ypix = py1;
+ x->gl_pixwidth = px2 - px1;
+ x->gl_pixheight = py2 - py1;
+ x->gl_font = (canvas_getcurrent() ?
+ canvas_getcurrent()->gl_font : sys_defaultfont);
+ x->gl_screenx1 = x->gl_screeny1 = 0;
+ x->gl_screenx2 = 450;
+ x->gl_screeny2 = 300;
+ if (strcmp(x->gl_name->s_name, "Pd"))
+ pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name));
+ x->gl_owner = g;
+ x->gl_stretch = 1;
+ x->gl_isgraph = 1;
+ x->gl_obj.te_binbuf = binbuf_new();
+ binbuf_addv(x->gl_obj.te_binbuf, "s", gensym("graph"));
+ if (!menu)
+ pd_pushsym(&x->gl_pd);
+ glist_add(g, &x->gl_gobj);
+ if (glist_isvisible(g))
+ canvas_create_editor(x, 1);
+ return (x);
+}
+
+ /* call glist_addglist from a Pd message */
+void glist_glist(t_glist *g, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *sym = atom_getsymbolarg(0, argc, argv);
+ float x1 = atom_getfloatarg(1, argc, argv);
+ float y1 = atom_getfloatarg(2, argc, argv);
+ float x2 = atom_getfloatarg(3, argc, argv);
+ float y2 = atom_getfloatarg(4, argc, argv);
+ float px1 = atom_getfloatarg(5, argc, argv);
+ float py1 = atom_getfloatarg(6, argc, argv);
+ float px2 = atom_getfloatarg(7, argc, argv);
+ float py2 = atom_getfloatarg(8, argc, argv);
+ glist_addglist(g, sym, x1, y1, x2, y2, px1, py1, px2, py2);
+}
+
+ /* return true if the glist should appear as a graph on parent;
+ otherwise it appears as a text box. */
+int glist_isgraph(t_glist *x)
+{
+ return (x->gl_isgraph);
+}
+
+ /* This is sent from the GUI to inform a toplevel that its window has been
+ moved or resized. */
+static void canvas_setbounds(t_canvas *x, int x1, int y1, int x2, int y2)
+{
+ x->gl_screenx1 = x1;
+ x->gl_screeny1 = y1;
+ x->gl_screenx2 = x2;
+ x->gl_screeny2 = y2;
+ if (!glist_isgraph(x) && (x->gl_y2 < x->gl_y1))
+ {
+ /* if it's flipped so that y grows upward,
+ fix so that zero is bottom edge and redraw. This is
+ only appropriate if we're a regular "text" object on the
+ parent. */
+ float diff = x->gl_y1 - x->gl_y2;
+ x->gl_y1 = x->gl_screeny2 * diff;
+ x->gl_y2 = x->gl_y1 - diff;
+ canvas_redraw(x);
+ }
+}
+
+t_symbol *canvas_makebindsym(t_symbol *s)
+{
+ char buf[MAXPDSTRING];
+ strcpy(buf, "pd-");
+ strcat(buf, s->s_name);
+ return (gensym(buf));
+}
+
+void canvas_reflecttitle(t_canvas *x)
+{
+ char namebuf[MAXPDSTRING];
+ t_canvasenvironment *env = canvas_getenv(x);
+ if (env->ce_argc)
+ {
+ int i;
+ strcpy(namebuf, " (");
+ for (i = 0; i < env->ce_argc; i++)
+ {
+ if (strlen(namebuf) > MAXPDSTRING/2 - 5)
+ break;
+ if (i != 0)
+ strcat(namebuf, " ");
+ atom_string(&env->ce_argv[i], namebuf + strlen(namebuf),
+ MAXPDSTRING/2);
+ }
+ strcat(namebuf, ")");
+ }
+ else namebuf[0] = 0;
+ sys_vgui("wm title .x%x {%s%c%s - %s}\n",
+ x, x->gl_name->s_name, (x->gl_dirty? '*' : ' '), namebuf,
+ canvas_getdir(x)->s_name);
+}
+
+void canvas_dirty(t_canvas *x, t_int n)
+{
+ t_canvas *x2 = canvas_getrootfor(x);
+ if ((unsigned)n != x2->gl_dirty)
+ {
+ x2->gl_dirty = n;
+ canvas_reflecttitle(x2);
+ }
+}
+
+extern t_gobj *canvas_selectme; /* HACK */
+
+ /* the window becomes "mapped" (visible and not miniaturized) or
+ "unmapped" (either miniaturized or just plain gone.) This should be
+ called from the GUI after the fact to "notify" us that we're mapped. */
+void canvas_map(t_canvas *x, t_floatarg f)
+{
+ int flag = (f != 0);
+ t_gobj *y;
+ if (flag)
+ {
+ if (!glist_isvisible(x))
+ {
+ t_selection *sel;
+ if (!x->gl_havewindow)
+ {
+ bug("canvas_map");
+ canvas_vis(x, 1);
+ }
+ for (y = x->gl_list; y; y = y->g_next)
+ gobj_vis(y, x, 1);
+ for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
+ gobj_select(sel->sel_what, x, 1);
+ x->gl_mapped = 1;
+ if (canvas_selectme)
+ {
+ glist_noselect(x);
+ glist_select(x, canvas_selectme);
+ canvas_selectme = 0;
+ }
+ canvas_drawlines(x);
+ }
+ }
+ else
+ {
+ if (glist_isvisible(x))
+ {
+ for (y = x->gl_list; y; y = y->g_next)
+ gobj_vis(y, x, 0);
+ x->gl_mapped = 0;
+ }
+ }
+}
+
+void canvas_redraw(t_canvas *x)
+{
+ if (glist_isvisible(x))
+ {
+ canvas_map(x, 0);
+ canvas_map(x, 1);
+ }
+}
+
+/* ---- editors -- perhaps this and "vis" should go to g_editor.c ------- */
+
+static t_editor *editor_new(t_glist *owner)
+{
+ char buf[40];
+ t_editor *x = (t_editor *)getbytes(sizeof(*x));
+ x->e_connectbuf = binbuf_new();
+ x->e_deleted = binbuf_new();
+ x->e_glist = owner;
+ sprintf(buf, ".x%x", (t_int)owner);
+ x->e_guiconnect = guiconnect_new(&owner->gl_pd, gensym(buf));
+ return (x);
+}
+
+static void editor_free(t_editor *x, t_glist *y)
+{
+ glist_noselect(y);
+ guiconnect_notarget(x->e_guiconnect, 1000);
+ binbuf_free(x->e_connectbuf);
+ binbuf_free(x->e_deleted);
+ freebytes((void *)x, sizeof(*x));
+}
+
+ /* recursively create or destroy all editors of a glist and its
+ sub-glists, as long as they aren't toplevels. */
+void canvas_create_editor(t_glist *x, int createit)
+{
+ t_gobj *y;
+ if (createit)
+ {
+ if (x->gl_editor)
+ bug("canvas_create_editor");
+ else x->gl_editor = editor_new(x);
+ }
+ else
+ {
+ if (!x->gl_editor)
+ bug("canvas_create_editor");
+ else editor_free(x->gl_editor, x);
+ x->gl_editor = 0;
+ }
+ for (y = x->gl_list; y; y = y->g_next)
+ if (pd_class(&y->g_pd) == canvas_class &&
+ ((t_canvas *)y)->gl_isgraph)
+ canvas_create_editor((t_canvas *)y, createit);
+}
+
+ /* we call this when we want the window to become visible, mapped, and
+ in front of all windows; or with "f" zero, when we want to get rid of
+ the window. */
+void canvas_vis(t_canvas *x, t_floatarg f)
+{
+ char buf[30];
+ int flag = (f != 0);
+ if (flag)
+ {
+ /* test if we're already visible and toplevel */
+ if (glist_isvisible(x) && !x->gl_isgraph)
+ { /* just put us in front */
+#ifdef NT
+ canvas_vis(x, 0);
+ canvas_vis(x, 1);
+#else
+ sys_vgui("raise .x%x\n", x);
+ sys_vgui("focus .x%x.c\n", x);
+ sys_vgui("wm deiconify .x%x\n", x);
+#endif
+ }
+ else
+ {
+ canvas_create_editor(x, 1);
+ sys_vgui("pdtk_canvas_new .x%x %d %d +%d+%d\n", x,
+ (int)(x->gl_screenx2 - x->gl_screenx1),
+ (int)(x->gl_screeny2 - x->gl_screeny1),
+ (int)(x->gl_screenx1), (int)(x->gl_screeny1)
+ );
+ canvas_reflecttitle(x);
+ /* simulate a mouse up so u_main will calculate scrollbars...
+ ugly! */
+ sys_vgui("pdtk_canvas_mouseup .x%x.c 0 0 0\n", x);
+ x->gl_havewindow = 1;
+ canvas_updatewindowlist();
+ }
+ }
+ else /* make invisible */
+ {
+ int i;
+ t_canvas *x2;
+ if (!x->gl_havewindow)
+ {
+ /* bug workaround -- a graph in a visible patch gets "invised"
+ when the patch is closed, and must lost the editor here. It's
+ probably not the natural place to do this. Other cases like
+ subpatches fall here too but don'd need the editor freed, so
+ we check if it exists. */
+ if (x->gl_editor)
+ canvas_create_editor(x, 0);
+ return;
+ }
+ glist_noselect(x);
+ if (glist_isvisible(x))
+ canvas_map(x, 0);
+ canvas_create_editor(x, 0);
+ sys_vgui("destroy .x%x\n", x);
+ for (i = 1, x2 = x; x2; x2 = x2->gl_next, i++)
+ ;
+ sys_vgui(".mbar.find.menu delete %d\n", i);
+ /* if we're a graph on our parent, and if the parent exists
+ and is visible, show ourselves on parent. */
+ if (glist_isgraph(x) && x->gl_owner)
+ {
+ t_glist *gl2 = x->gl_owner;
+ canvas_create_editor(x, 1);
+ if (glist_isvisible(gl2))
+ gobj_vis(&x->gl_gobj, gl2, 0);
+ x->gl_havewindow = 0;
+ if (glist_isvisible(gl2))
+ gobj_vis(&x->gl_gobj, gl2, 1);
+ }
+ else x->gl_havewindow = 0;
+ canvas_updatewindowlist();
+ }
+}
+
+ /* we call this on a non-toplevel glist to "open" it into its
+ own window. */
+void glist_menu_open(t_glist *x)
+{
+ if (glist_isvisible(x) && !glist_istoplevel(x))
+ {
+ t_glist *gl2 = x->gl_owner;
+ if (!gl2)
+ bug("canvas_vis"); /* shouldn't happen but don't get too upset. */
+ else
+ {
+ /* erase ourself in parent window */
+ gobj_vis(&x->gl_gobj, gl2, 0);
+ /* get rid of our editor (and subeditors) */
+ canvas_create_editor(x, 0);
+ x->gl_havewindow = 1;
+ /* redraw ourself in parent window (blanked out this time) */
+ gobj_vis(&x->gl_gobj, gl2, 1);
+ }
+ }
+ canvas_vis(x, 1);
+}
+
+int glist_isvisible(t_glist *x)
+{
+ return ((!x->gl_loading) && glist_getcanvas(x)->gl_mapped);
+}
+
+int glist_istoplevel(t_glist *x)
+{
+ /* we consider a graph "toplevel" if it has its own window
+ or if it appears as a box in its parent window so that we
+ don't draw the actual contents there. */
+ return (x->gl_havewindow || !x->gl_isgraph);
+}
+
+int glist_getfont(t_glist *x)
+{
+ return (glist_getcanvas(x)->gl_font);
+}
+
+void canvas_free(t_canvas *x)
+{
+ t_gobj *y;
+ int dspstate = canvas_suspend_dsp();
+
+ if (canvas_editing == x)
+ canvas_editing = 0;
+ if (canvas_whichfind == x)
+ canvas_whichfind = 0;
+ glist_noselect(x);
+ while (y = x->gl_list)
+ glist_delete(x, y);
+ canvas_vis(x, 0);
+
+ if (strcmp(x->gl_name->s_name, "Pd"))
+ pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name));
+ if (x->gl_env)
+ {
+ freebytes(x->gl_env->ce_argv, x->gl_env->ce_argc * sizeof(t_atom));
+ freebytes(x->gl_env, sizeof(*x->gl_env));
+ }
+ canvas_resume_dsp(dspstate);
+ glist_cleanup(x);
+ gfxstub_deleteforkey(x); /* probably unnecessary */
+ if (!x->gl_owner)
+ canvas_takeofflist(x);
+}
+
+/* ----------------- lines ---------- */
+
+static void canvas_drawlines(t_canvas *x)
+{
+ t_linetraverser t;
+ t_outconnect *oc;
+ {
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ sys_vgui(".x%x.c create line %d %d %d %d -tags l%x\n",
+ glist_getcanvas(x),
+ t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2, oc);
+ }
+}
+
+void canvas_fixlinesfor(t_canvas *x, t_text *text)
+{
+ t_linetraverser t;
+ t_outconnect *oc;
+
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ {
+ if (t.tr_ob == text || t.tr_ob2 == text)
+ {
+ sys_vgui(".x%x.c coords l%x %d %d %d %d\n",
+ glist_getcanvas(x), oc,
+ t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2);
+ }
+ }
+}
+
+ /* kill all lines for the object */
+void canvas_deletelinesfor(t_canvas *x, t_text *text)
+{
+ t_linetraverser t;
+ t_outconnect *oc;
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ {
+ if (t.tr_ob == text || t.tr_ob2 == text)
+ {
+ if (x->gl_editor)
+ {
+ sys_vgui(".x%x.c delete l%x\n",
+ glist_getcanvas(x), oc);
+ }
+ obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
+ }
+ }
+}
+
+ /* kill all lines for one inlet or outlet */
+void canvas_deletelinesforio(t_canvas *x, t_text *text,
+ t_inlet *inp, t_outlet *outp)
+{
+ t_linetraverser t;
+ t_outconnect *oc;
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ {
+ if ((t.tr_ob == text && t.tr_outlet == outp) ||
+ (t.tr_ob2 == text && t.tr_inlet == inp))
+ {
+ if (x->gl_editor)
+ {
+ sys_vgui(".x%x.c delete l%x\n",
+ glist_getcanvas(x), oc);
+ }
+ obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
+ }
+ }
+}
+
+static void canvas_pop(t_canvas *x, t_floatarg fvis)
+{
+ if (fvis != 0)
+ canvas_vis(x, 1);
+ pd_popsym(&x->gl_pd);
+ canvas_resortinlets(x);
+ canvas_resortoutlets(x);
+ x->gl_loading = 0;
+}
+
+void canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv);
+
+
+void canvas_restore(t_canvas *x, t_symbol *s, int argc, t_atom *argv)
+{ /* IOhannes */
+ t_pd *z;
+ /* this should be unnecessary, but sometimes the canvas's name gets
+ out of sync with the owning box's argument; this fixes that */
+ if (argc > 3)
+ {
+ t_atom *ap=argv+3;
+ if (ap->a_type == A_SYMBOL)
+ {
+ char *buf=ap->a_w.w_symbol->s_name, *bufp;
+ if (*buf == '$' && buf[1] >= '0' && buf[1] <= '9')
+ {
+ for (bufp = buf+2; *bufp; bufp++)
+ if (*bufp < '0' || *bufp > '9')
+ {
+ SETDOLLSYM(ap, gensym(buf+1));
+ goto didit;
+ }
+ SETDOLLAR(ap, atoi(buf+1));
+ didit: ;
+ }
+ }
+
+ if (ap->a_type == A_DOLLSYM)
+ {
+ t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());
+ canvas_rename(x, realizedollsym(ap->a_w.w_symbol,
+ e->ce_argc, e->ce_argv, 1), 0);
+ }
+ else if (ap->a_type == A_SYMBOL)
+ canvas_rename(x, argv[3].a_w.w_symbol, 0);
+ }
+ canvas_pop(x, x->gl_willvis);
+
+ if (!(z = gensym("#X")->s_thing)) error("canvas_restore: out of context");
+ else if (*z != canvas_class) error("canvas_restore: wasn't a canvas");
+ else
+ {
+ t_canvas *x2 = (t_canvas *)z;
+ x->gl_owner = x2;
+ canvas_objfor(x2, &x->gl_obj, argc, argv);
+ }
+}
+
+static void canvas_loadbangabstractions(t_canvas *x)
+{
+ t_gobj *y;
+ t_symbol *s = gensym("loadbang");
+ for (y = x->gl_list; y; y = y->g_next)
+ if (pd_class(&y->g_pd) == canvas_class)
+ {
+ if (canvas_isabstraction((t_canvas *)y))
+ canvas_loadbang((t_canvas *)y);
+ else
+ canvas_loadbangabstractions((t_canvas *)y);
+ }
+}
+
+void canvas_loadbangsubpatches(t_canvas *x)
+{
+ t_gobj *y;
+ t_symbol *s = gensym("loadbang");
+ for (y = x->gl_list; y; y = y->g_next)
+ if (pd_class(&y->g_pd) == canvas_class)
+ {
+ if (!canvas_isabstraction((t_canvas *)y))
+ canvas_loadbangsubpatches((t_canvas *)y);
+ }
+ for (y = x->gl_list; y; y = y->g_next)
+ if ((pd_class(&y->g_pd) != canvas_class) &&
+ zgetfn(&y->g_pd, s))
+ pd_vmess(&y->g_pd, s, "");
+}
+
+void canvas_loadbang(t_canvas *x)
+{
+ t_gobj *y;
+ canvas_loadbangabstractions(x);
+ canvas_loadbangsubpatches(x);
+}
+
+ /* When you ask a canvas its size the result is 2 pixels more than what
+ you gave it to open it; perhaps there's a 1-pixel border all around it
+ or something. Anyway, we just add the 2 pixels back here: */
+
+#ifdef NT
+#define HORIZBORDER 2
+#define VERTBORDER 2
+#else
+#define HORIZBORDER 2
+#define VERTBORDER 2
+#endif
+
+static void canvas_relocate(t_canvas *x, t_symbol *canvasgeom,
+ t_symbol *topgeom)
+{
+ int cxpix, cypix, cw, ch, txpix, typix, tw, th;
+ if (sscanf(canvasgeom->s_name, "%dx%d+%d+%d", &cw, &ch, &cxpix, &cypix)
+ < 4 ||
+ sscanf(topgeom->s_name, "%dx%d+%d+%d", &tw, &th, &txpix, &typix) < 4)
+ bug("canvas_relocate");
+ /* for some reason this is initially called with cw=ch=1 so
+ we just suppress that here. */
+ if (cw > 5 && ch > 5)
+ canvas_setbounds(x, txpix, typix,
+ txpix + cw - HORIZBORDER, typix + ch - VERTBORDER);
+}
+
+void canvas_popabstraction(t_canvas *x)
+{
+ newest = &x->gl_pd;
+ pd_popsym(&x->gl_pd);
+ x->gl_loading = 0;
+ canvas_resortinlets(x);
+ canvas_resortoutlets(x);
+}
+
+void canvas_logerror(t_object *y)
+{
+#ifdef LATER
+ canvas_vis(x, 1);
+ if (!glist_isselected(x, &y->ob_g))
+ glist_select(x, &y->ob_g);
+#endif
+}
+
+/* -------------------------- subcanvases ---------------------- */
+
+static void *subcanvas_new(t_symbol *s)
+{
+ t_atom a[6];
+ t_canvas *x, *z = canvas_getcurrent();
+ if (!*s->s_name) s = gensym("/SUBPATCH/");
+ SETFLOAT(a, 0);
+ SETFLOAT(a+1, GLIST_DEFCANVASYLOC);
+ SETFLOAT(a+2, GLIST_DEFCANVASWIDTH);
+ SETFLOAT(a+3, GLIST_DEFCANVASHEIGHT);
+ SETSYMBOL(a+4, s);
+ SETFLOAT(a+5, 1);
+ x = canvas_new(0, 0, 6, a);
+ x->gl_owner = z;
+ canvas_pop(x, 1);
+ return (x);
+}
+
+static void canvas_click(t_canvas *x,
+ t_floatarg xpos, t_floatarg ypos,
+ t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
+{
+ canvas_vis(x, 1);
+}
+
+
+ /* find out from subcanvas contents how much to fatten the box */
+void canvas_fattensub(t_canvas *x,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_gobj *y;
+ *xp2 += 50; /* fake for now */
+ *yp2 += 50;
+}
+
+static void canvas_rename_method(t_canvas *x, t_symbol *s, int ac, t_atom *av)
+{
+ if (ac && av->a_type == A_SYMBOL)
+ canvas_rename(x, av->a_w.w_symbol, 0);
+ else canvas_rename(x, gensym("Pd"), 0);
+}
+
+/* ------------------ table ---------------------------*/
+
+static int tabcount = 0;
+
+static void *table_new(t_symbol *s, t_floatarg f)
+{
+ t_atom a[9];
+ t_glist *gl;
+ t_canvas *x, *z = canvas_getcurrent();
+ if (s == &s_)
+ {
+ char tabname[255];
+ t_symbol *t = gensym("table");
+ sprintf(tabname, "%s%d", t->s_name, tabcount++);
+ s = gensym(tabname);
+ }
+ if (f <= 1)
+ f = 100;
+ SETFLOAT(a, 0);
+ SETFLOAT(a+1, GLIST_DEFCANVASYLOC);
+ SETFLOAT(a+2, 600);
+ SETFLOAT(a+3, 400);
+ SETSYMBOL(a+4, s);
+ SETFLOAT(a+5, 0);
+ x = canvas_new(0, 0, 6, a);
+
+ x->gl_owner = z;
+
+ /* create a graph for the table */
+ gl = glist_addglist((t_glist*)x, &s_, 0, -1, (f > 1 ? f-1 : 1), 1,
+ 50, 350, 550, 50);
+
+ graph_array(gl, s, &s_float, f, 0);
+
+ canvas_pop(x, 0);
+
+ return (x);
+}
+
+ /* return true if the "canvas" object is an abstraction (so we don't
+ save its contents, fogr example.) */
+int canvas_isabstraction(t_canvas *x)
+{
+ return (x->gl_env != 0);
+}
+
+ /* return true if the "canvas" object is a "table". */
+int canvas_istable(t_canvas *x)
+{
+ t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0);
+ int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0);
+ int istable = (argc && argv[0].a_type == A_SYMBOL &&
+ argv[0].a_w.w_symbol == gensym("table"));
+ return (istable);
+}
+
+ /* return true if the "canvas" object should be treated as a text
+ object. This is true for abstractions but also for "table"s... */
+int canvas_showtext(t_canvas *x)
+{
+ t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0);
+ int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0);
+ int isarray = (argc && argv[0].a_type == A_SYMBOL &&
+ argv[0].a_w.w_symbol == gensym("graph"));
+ return (!isarray);
+}
+
+static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp);
+static void canvas_dsp(t_canvas *x, t_signal **sp)
+{
+ canvas_dodsp(x, 0, sp);
+}
+
+ /* get the document containing this canvas */
+t_canvas *canvas_getrootfor(t_canvas *x)
+{
+ if ((!x->gl_owner) || canvas_isabstraction(x))
+ return (x);
+ else return (canvas_getrootfor(x->gl_owner));
+}
+
+/* ------------------------- DSP chain handling ------------------------- */
+
+EXTERN_STRUCT _dspcontext;
+#define t_dspcontext struct _dspcontext
+
+void ugen_start(void);
+void ugen_stop(void);
+
+t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp,
+ int ninlets, int noutlets);
+void ugen_add(t_dspcontext *dc, t_object *x);
+void ugen_connect(t_dspcontext *dc, t_object *x1, int outno,
+ t_object *x2, int inno);
+void ugen_done_graph(t_dspcontext *dc);
+
+int obj_issignaloutlet(t_object *x, int outno);
+int obj_nsiginlets(t_object *x);
+int obj_nsigoutlets(t_object *x);
+
+ /* schedule one canvas for DSP. This is called below for all "root"
+ canvases, but is also called from the "dsp" method for sub-
+ canvases, which are treated almost like any other tilde object. */
+
+static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp)
+{
+ t_linetraverser t;
+ t_outconnect *oc;
+ t_gobj *y;
+ t_object *ob;
+ t_symbol *dspsym = gensym("dsp");
+ t_dspcontext *dc;
+
+ /* create a new "DSP graph" object to use in sorting this canvas.
+ If we aren't toplevel, there are already other dspcontexts around. */
+
+ dc = ugen_start_graph(toplevel, sp,
+ obj_nsiginlets(&x->gl_obj),
+ obj_nsigoutlets(&x->gl_obj));
+
+ /* find all the "dsp" boxes and add them to the graph */
+
+ for (y = x->gl_list; y; y = y->g_next)
+ if ((ob = pd_checkobject(&y->g_pd)) && zgetfn(&y->g_pd, dspsym))
+ ugen_add(dc, ob);
+
+ /* ... and all dsp interconnections */
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ if (obj_issignaloutlet(t.tr_ob, t.tr_outno))
+ ugen_connect(dc, t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
+
+ /* finally, sort them and add them to the DSP chain */
+ ugen_done_graph(dc);
+}
+
+ /* this routine starts DSP for all root canvases. */
+static void canvas_start_dsp(void)
+{
+ t_canvas *x;
+ if (canvas_dspstate) ugen_stop();
+ else sys_gui("pdtk_pd_dsp ON\n");
+ ugen_start();
+
+ for (x = canvas_list; x; x = x->gl_next)
+ canvas_dodsp(x, 1, 0);
+
+ canvas_dspstate = 1;
+}
+
+static void canvas_stop_dsp(void)
+{
+ if (canvas_dspstate)
+ {
+ ugen_stop();
+ sys_gui("pdtk_pd_dsp OFF\n");
+ canvas_dspstate = 0;
+ }
+}
+
+ /* DSP can be suspended before, and resumed after, operations which
+ might affect the DSP chain. For example, we suspend before loading and
+ resume afterward, so that DSP doesn't get resorted for every DSP object
+ int the patch. */
+
+int canvas_suspend_dsp(void)
+{
+ int rval = canvas_dspstate;
+ if (rval) canvas_stop_dsp();
+ return (rval);
+}
+
+void canvas_resume_dsp(int oldstate)
+{
+ if (oldstate) canvas_start_dsp();
+}
+
+ /* this is equivalent to suspending and resuming in one step. */
+void canvas_update_dsp(void)
+{
+ if (canvas_dspstate) canvas_start_dsp();
+}
+
+void glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv)
+{
+ int newstate;
+ if (argc)
+ {
+ newstate = atom_getintarg(0, argc, argv);
+ if (newstate && !canvas_dspstate)
+ canvas_start_dsp();
+ else if (!newstate && canvas_dspstate)
+ canvas_stop_dsp();
+ }
+ else post("dsp state %d", canvas_dspstate);
+}
+
+ /* LATER replace this with a queueing scheme */
+void glist_redrawitem(t_glist *owner, t_gobj *gobj)
+{
+ if (glist_isvisible(owner))
+ {
+ gobj_vis(gobj, owner, 0);
+ gobj_vis(gobj, owner, 1);
+ }
+}
+
+ /* redraw all "scalars" (do this if a drawing command is changed.)
+ LATER we'll use the "template" information to select which ones we
+ redraw. */
+static void glist_redrawall(t_glist *gl)
+{
+ t_gobj *g;
+ int vis = glist_isvisible(gl);
+ for (g = gl->gl_list; g; g = g->g_next)
+ {
+ t_class *cl;
+ if (vis && g->g_pd == scalar_class)
+ glist_redrawitem(gl, g);
+ else if (g->g_pd == canvas_class)
+ glist_redrawall((t_glist *)g);
+ }
+}
+
+ /* public interface for above */
+void canvas_redrawallfortemplate(t_canvas *template)
+{
+ t_canvas *x;
+ if (!template->gl_imatemplate) return;
+ /* find all root canvases */
+ for (x = canvas_list; x; x = x->gl_next)
+ glist_redrawall(x);
+}
+
+ /* Same as above but just zap them. Call this if a template
+ is changed by adding or removing a field. LATER we'll just
+ modify all the items appropriately. */
+static void glist_zapall(t_glist *gl)
+{
+ t_gobj *g;
+ for (g = gl->gl_list; g; g = g->g_next)
+ {
+ t_class *cl;
+ if (g->g_pd == canvas_class)
+ glist_zapall((t_glist *)g);
+ }
+ /* do we have any scalars? */
+ for (g = gl->gl_list; g; g = g->g_next)
+ {
+ if (g->g_pd == scalar_class)
+ break;
+ }
+ if (!g) return;
+ /* delete all the scalars. This is inefficient if for some reason
+ you've mixed scalars with other items in a single glist. */
+ while (1)
+ {
+ for (g = gl->gl_list; g; g = g->g_next)
+ {
+ if (g->g_pd == scalar_class)
+ {
+ glist_delete(gl, g);
+ break;
+ }
+ }
+ if (!g) break;
+ }
+}
+
+ /* public interface for above */
+void canvas_zapallfortemplate(t_canvas *template)
+{
+ t_canvas *x;
+ if (!template->gl_imatemplate) return;
+ /* find all root canvases */
+ for (x = canvas_list; x; x = x->gl_next)
+ glist_zapall(x);
+}
+
+ /* warn a canvas that some datum has used it as a template. If a
+ canvas has no data associated with it (at load time, for instance)
+ we don't have to search through the world for instances as it changes. */
+void canvas_setusedastemplate(t_canvas *x)
+{
+ x->gl_imatemplate = 1;
+}
+
+/* ------------------------------- setup routine ------------------------ */
+
+ /* why are some of these "glist" and others "canvas"? */
+extern void glist_text(t_glist *x, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv);
+extern void glist_scalar(t_glist *canvas, t_symbol *s, int argc, t_atom *argv);
+
+void g_graph_setup(void);
+void g_editor_setup(void);
+void g_readwrite_setup(void);
+
+void g_canvas_setup(void)
+{
+ /* we prevent the user from typing "canvas" in an object box
+ by sending 0 for a creator function. */
+ canvas_class = class_new(gensym("canvas"), 0,
+ (t_method)canvas_free, sizeof(t_canvas), CLASS_NOINLET, 0);
+ /* here is the real creator function, invoked in patch files
+ by sending the "canvas" message to #N, which is bound
+ to pd_camvasmaker. */
+ class_addmethod(pd_canvasmaker, (t_method)canvas_new, gensym("canvas"),
+ A_GIMME, 0);
+ class_addmethod(canvas_class, (t_method)canvas_restore,
+ gensym("restore"), A_GIMME, 0);
+ class_addmethod(canvas_class, (t_method)canvas_coords,
+ gensym("coords"), A_GIMME, 0);
+
+/* -------------------------- objects ----------------------------- */
+ class_addmethod(canvas_class, (t_method)canvas_obj,
+ gensym("obj"), A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_msg,
+ gensym("msg"), A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_floatatom,
+ gensym("floatatom"), A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_symbolatom,
+ gensym("symbolatom"), A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)glist_text,
+ gensym("text"), A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)glist_glist, gensym("graph"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)glist_scalar,
+ gensym("scalar"), A_GIMME, A_NULL);
+
+ /* -------------- Thomas Musil's GUI objects ------------ */
+ class_addmethod(canvas_class, (t_method)canvas_bng, gensym("bng"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_toggle, gensym("toggle"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_vslider, gensym("vslider"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_hslider, gensym("hslider"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_hdial, gensym("hdial"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_vdial, gensym("vdial"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_vumeter, gensym("vumeter"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_mycnv, gensym("mycnv"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_numbox, gensym("numbox"),
+ A_GIMME, A_NULL);
+
+/* ------------------------ gui stuff --------------------------- */
+ class_addmethod(canvas_class, (t_method)canvas_pop, gensym("pop"),
+ A_DEFFLOAT, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_loadbang,
+ gensym("loadbang"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_relocate,
+ gensym("relocate"), A_SYMBOL, A_SYMBOL, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_vis,
+ gensym("vis"), A_FLOAT, A_NULL);
+ class_addmethod(canvas_class, (t_method)glist_menu_open,
+ gensym("menu-open"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_map,
+ gensym("map"), A_FLOAT, A_NULL);
+
+/* ---------------------- list handling ------------------------ */
+ class_addmethod(canvas_class, (t_method)glist_clear, gensym("clear"),
+ A_NULL);
+
+/* ----- subcanvases, which you get by typing "pd" in a box ---- */
+ class_addcreator((t_newmethod)subcanvas_new, gensym("pd"), A_DEFSYMBOL, 0);
+ class_addcreator((t_newmethod)subcanvas_new, gensym("page"), A_DEFSYMBOL, 0);
+
+ class_addmethod(canvas_class, (t_method)canvas_click,
+ gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(canvas_class, (t_method)canvas_dsp, gensym("dsp"), 0);
+ class_addmethod(canvas_class, (t_method)canvas_rename_method,
+ gensym("rename"), A_GIMME, 0);
+
+/*---------------------------- tables -- GG ------------------- */
+
+ class_addcreator((t_newmethod)table_new, gensym("table"),
+ A_DEFSYM, A_DEFFLOAT, 0);
+
+/* -------------- setups from other files for canvas_class ---------------- */
+ g_graph_setup();
+ g_editor_setup();
+ g_readwrite_setup();
+
+}
diff --git a/pd/src/g_canvas.h b/pd/src/g_canvas.h
new file mode 100644
index 00000000..fb567ea5
--- /dev/null
+++ b/pd/src/g_canvas.h
@@ -0,0 +1,564 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* this file defines the structure for "glists" and related structures and
+functions. "Glists" and "canvases" and "graphs" used to be different
+structures until being unified in version 0.35.
+
+A glist occupies its own window if the "gl_havewindow" flag is set. Its
+appearance on its "parent" or "owner" (if it has one) is as a graph if
+"gl_isgraph" is set, and otherwise as a text box.
+
+A glist is "root" if it has no owner, i.e., a document window. In this
+case "gl_havewindow" is always set.
+
+We maintain a list of root windows, so that we can traverse the whole
+collection of everything in a Pd process.
+
+If a glist has a window it may still not be "mapped." Miniaturized
+windows aren't mapped, for example, but a window is also not mapped
+immediately upon creation. In either case gl_havewindow is true but
+gl_mapped is false.
+
+Closing a non-root window makes it invisible; closing a root destroys it.
+
+A glist that's just a text object on its parent is always "toplevel." An
+embedded glist can switch back and forth to appear as a toplevel by double-
+clicking on it. Single-clicking a text box makes the toplevel become visible
+and raises the window it's in.
+
+If a glist shows up as a graph on its parent, the graph is blanked while the
+glist has its own window, even if miniaturized.
+
+*/
+
+/* --------------------- geometry ---------------------------- */
+#define IOWIDTH 7 /* width of an inlet/outlet in pixels */
+#define IOMIDDLE ((IOWIDTH-1)/2)
+#define GLIST_DEFGRAPHWIDTH 200
+#define GLIST_DEFGRAPHHEIGHT 140
+/* ----------------------- data ------------------------------- */
+
+typedef struct _updateheader
+{
+ struct _updateheader *upd_next;
+ unsigned int upd_array:1; /* true if array, false if glist */
+ unsigned int upd_queued:1; /* true if we're queued */
+} t_updateheader;
+
+ /* types to support glists grabbing mouse motion or keys from parent */
+typedef void (*t_glistmotionfn)(void *z, t_floatarg dx, t_floatarg dy);
+typedef void (*t_glistkeyfn)(void *z, t_floatarg key);
+
+EXTERN_STRUCT _rtext;
+#define t_rtext struct _rtext
+
+EXTERN_STRUCT _gtemplate;
+#define t_gtemplate struct _gtemplate
+
+EXTERN_STRUCT _guiconnect;
+#define t_guiconnect struct _guiconnect
+
+EXTERN_STRUCT _tscalar;
+#define t_tscalar struct _tscalar
+
+EXTERN_STRUCT _canvasenvironment;
+#define t_canvasenvironment struct _canvasenvironment
+
+EXTERN_STRUCT _linetraverser;
+#define t_linetraverser struct _linetraverser
+
+typedef struct _selection
+{
+ t_gobj *sel_what;
+ struct _selection *sel_next;
+} t_selection;
+
+ /* this structure is instantiated whenever a glist becomes visible. */
+typedef struct _editor
+{
+ t_updateheader e_upd; /* update header structure */
+ t_selection *e_updlist; /* list of objects to update */
+ t_rtext *e_rtext; /* text responder linked list */
+ t_selection *e_selection; /* head of the selection list */
+ t_rtext *e_textedfor; /* the rtext if any that we are editing */
+ t_gobj *e_grab; /* object being "dragged" */
+ t_glistmotionfn e_motionfn; /* ... motion callback */
+ t_glistkeyfn e_keyfn; /* ... keypress callback */
+ t_binbuf *e_connectbuf; /* connections to deleted objects */
+ t_binbuf *e_deleted; /* last stuff we deleted */
+ t_guiconnect *e_guiconnect; /* GUI connection for filtering messages */
+ struct _glist *e_glist; /* glist which owns this */
+ int e_xwas; /* xpos on last mousedown or motion event */
+ int e_ywas; /* ypos, similarly */
+ unsigned int e_onmotion: 3; /* action to take on motion */
+ unsigned int e_lastmoved: 1; /* one if mouse has moved since click */
+ unsigned int e_textdirty: 1; /* one if e_textedfor has changed */
+} t_editor;
+
+#define MA_NONE 0 /* e_onmotion: do nothing on mouse motion */
+#define MA_MOVE 1 /* drag the selection around */
+#define MA_CONNECT 2 /* make a connection */
+#define MA_REGION 3 /* selection region */
+#define MA_PASSOUT 4 /* send on to e_grab */
+#define MA_DRAGTEXT 5 /* drag in text editor to alter selection */
+
+/* editor structure for "garrays". We don't bother to delete and regenerate
+this structure when the "garray" becomes invisible or visible, although we
+could do so if the structure gets big (like the "editor" above.) */
+
+typedef struct _arrayvis
+{
+ t_updateheader av_upd; /* update header structure */
+ t_garray *av_garray; /* owning structure */
+} t_arrayvis;
+
+/* the t_tick structure describes where to draw x and y "ticks" for a glist */
+
+typedef struct _tick /* where to put ticks on x or y axes */
+{
+ float k_point; /* one point to draw a big tick at */
+ float k_inc; /* x or y increment per little tick */
+ int k_lperb; /* little ticks per big; 0 if no ticks to draw */
+} t_tick;
+
+/* the t_glist structure, which describes a list of elements that live on an
+area of a window.
+
+*/
+
+struct _glist
+{
+ t_object gl_obj; /* header in case we're a glist */
+ t_gobj *gl_list; /* the actual data */
+ struct _gstub *gl_stub; /* safe pointer handler */
+ int gl_valid; /* incremented when pointers might be stale */
+ struct _glist *gl_owner; /* parent glist, supercanvas, or 0 if none */
+ int gl_pixwidth; /* width in pixels (on parent, if a graph) */
+ int gl_pixheight;
+ float gl_x1; /* bounding rectangle in our own coordinates */
+ float gl_y1;
+ float gl_x2;
+ float gl_y2;
+ int gl_screenx1; /* screen coordinates when toplevel */
+ int gl_screeny1;
+ int gl_screenx2;
+ int gl_screeny2;
+ t_tick gl_xtick; /* ticks marking X values */
+ int gl_nxlabels; /* number of X coordinate labels */
+ t_symbol **gl_xlabel; /* ... an array to hold them */
+ float gl_xlabely; /* ... and their Y coordinates */
+ t_tick gl_ytick; /* same as above for Y ticks and labels */
+ int gl_nylabels;
+ t_symbol **gl_ylabel;
+ float gl_ylabelx;
+ t_editor *gl_editor; /* editor structure when visible */
+ t_symbol *gl_name; /* symbol bound here */
+ int gl_font; /* nominal font size in points, e.g., 10 */
+ struct _glist *gl_next; /* link in list of toplevels */
+ t_canvasenvironment *gl_env; /* root canvases and abstractions only */
+ unsigned int gl_havewindow:1; /* true if we own a window */
+ unsigned int gl_mapped:1; /* true if, moreover, it's "mapped" */
+ unsigned int gl_dirty:1; /* (root canvas only:) patch has changed */
+ unsigned int gl_loading:1; /* am now loading from file */
+ unsigned int gl_willvis:1; /* make me visible after loading */
+ unsigned int gl_edit:1; /* edit mode */
+ unsigned int gl_imatemplate:1; /* someone needs me as template */
+ unsigned int gl_isdeleting:1; /* we're inside glist_delete -- hack! */
+ unsigned int gl_protect:1; /* don't delete connections on click */
+ unsigned int gl_stretch:1; /* stretch contents on resize */
+ unsigned int gl_isgraph:1; /* show as graph on parent */
+};
+
+#define gl_gobj gl_obj.te_g
+#define gl_pd gl_gobj.g_pd
+
+/* a data structure to describe a field in a pure datum */
+
+#define DT_FLOAT 0
+#define DT_SYMBOL 1
+#define DT_LIST 2
+#define DT_ARRAY 3
+
+typedef struct _dataslot
+{
+ int ds_type;
+ t_symbol *ds_name;
+ t_symbol *ds_arraytemplate; /* filled in for arrays only */
+} t_dataslot;
+
+typedef struct _template
+{
+ t_pd t_pd; /* header */
+ struct _gtemplate *t_list; /* list of "struct"/gtemplate objects */
+ t_symbol *t_sym; /* name */
+ int t_n; /* number of dataslots (fields) */
+ t_dataslot *t_vec; /* array of dataslots */
+} t_template;
+
+struct _array
+{
+ int a_n; /* number of elements */
+ int a_elemsize; /* size in bytes; LATER get this from template */
+ char *a_vec; /* array of elements */
+ t_symbol *a_templatesym; /* template for elements */
+ int a_valid; /* protection against stale pointers into array */
+ t_gpointer a_gp; /* pointer to scalar or array element we're in */
+ t_gstub *a_stub;
+};
+
+ /* structure for traversing all the connections in a glist */
+struct _linetraverser
+{
+ t_canvas *tr_x;
+ t_object *tr_ob;
+ int tr_nout;
+ int tr_outno;
+ t_object *tr_ob2;
+ t_outlet *tr_outlet;
+ t_inlet *tr_inlet;
+ int tr_nin;
+ int tr_inno;
+ int tr_x11, tr_y11, tr_x12, tr_y12;
+ int tr_x21, tr_y21, tr_x22, tr_y22;
+ int tr_lx1, tr_ly1, tr_lx2, tr_ly2;
+ t_outconnect *tr_nextoc;
+ int tr_nextoutno;
+};
+
+/* function types used to define graphical behavior for gobjs, a bit like X
+widgets. We don't use Pd methods because Pd's typechecking can't specify the
+types of pointer arguments. Also it's more convenient this way, since
+every "patchable" object can just get the "text" behaviors. */
+
+ /* Call this to get a gobj's bounding rectangle in pixels */
+typedef void (*t_getrectfn)(t_gobj *x, struct _glist *glist,
+ int *x1, int *y1, int *x2, int *y2);
+ /* and this to displace a gobj: */
+typedef void (*t_displacefn)(t_gobj *x, struct _glist *glist, int dx, int dy);
+ /* change color to show selection: */
+typedef void (*t_selectfn)(t_gobj *x, struct _glist *glist, int state);
+ /* change appearance to show activation/deactivation: */
+typedef void (*t_activatefn)(t_gobj *x, struct _glist *glist, int state);
+ /* warn a gobj it's about to be deleted */
+typedef void (*t_deletefn)(t_gobj *x, struct _glist *glist);
+ /* making visible or invisible */
+typedef void (*t_visfn)(t_gobj *x, struct _glist *glist, int flag);
+ /* field a mouse click (when not in "edit" mode) */
+typedef int (*t_clickfn)(t_gobj *x, struct _glist *glist,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit);
+ /* save to a binbuf */
+typedef void (*t_savefn)(t_gobj *x, t_binbuf *b);
+ /* open properties dialog */
+typedef void (*t_propertiesfn)(t_gobj *x, struct _glist *glist);
+ /* ... and later, resizing; getting/setting font or color... */
+
+struct _widgetbehavior
+{
+ t_getrectfn w_getrectfn;
+ t_displacefn w_displacefn;
+ t_selectfn w_selectfn;
+ t_activatefn w_activatefn;
+ t_deletefn w_deletefn;
+ t_visfn w_visfn;
+ t_clickfn w_clickfn;
+ t_savefn w_savefn;
+ t_propertiesfn w_propertiesfn;
+};
+
+/* -------- behaviors for scalars defined by objects in template --------- */
+/* these are set by "drawing commands" in g_template.c which add appearance to
+scalars, which live in some other window. If the scalar is just included
+in a canvas the "parent" is a misnomer. There is also a text scalar object
+which really does draw the scalar on the parent window; see g_scalar.c. */
+
+/* note how the click function wants the whole scalar, not the "data", so
+doesn't work on array elements... LATER reconsider this */
+
+ /* bounding rectangle: */
+typedef void (*t_parentgetrectfn)(t_gobj *x, struct _glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int *x1, int *y1, int *x2, int *y2);
+ /* displace it */
+typedef void (*t_parentdisplacefn)(t_gobj *x, struct _glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int dx, int dy);
+ /* change color to show selection */
+typedef void (*t_parentselectfn)(t_gobj *x, struct _glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int state);
+ /* change appearance to show activation/deactivation: */
+typedef void (*t_parentactivatefn)(t_gobj *x, struct _glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int state);
+ /* making visible or invisible */
+typedef void (*t_parentvisfn)(t_gobj *x, struct _glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int flag);
+ /* field a mouse click */
+typedef int (*t_parentclickfn)(t_gobj *x, struct _glist *glist,
+ t_scalar *sc, t_template *template, float basex, float basey,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit);
+
+struct _parentwidgetbehavior
+{
+ t_parentgetrectfn w_parentgetrectfn;
+ t_parentdisplacefn w_parentdisplacefn;
+ t_parentselectfn w_parentselectfn;
+ t_parentactivatefn w_parentactivatefn;
+ t_parentvisfn w_parentvisfn;
+ t_parentclickfn w_parentclickfn;
+};
+
+ /* cursor definitions; used as return value for t_parentclickfn */
+#define CURSOR_RUNMODE_NOTHING 0
+#define CURSOR_RUNMODE_CLICKME 1
+#define CURSOR_RUNMODE_THICKEN 2
+#define CURSOR_RUNMODE_ADDPOINT 3
+#define CURSOR_EDITMODE_NOTHING 4
+#define CURSOR_EDITMODE_CONNECT 5
+#define CURSOR_EDITMODE_DISCONNECT 6
+EXTERN void canvas_setcursor(t_glist *x, unsigned int cursornum);
+
+extern t_canvas *canvas_editing; /* last canvas to start text edting */
+extern t_canvas *canvas_whichfind; /* last canvas we did a find in */
+extern t_canvas *canvas_list; /* list of all root canvases */
+extern t_class *vinlet_class, *voutlet_class;
+extern int glist_valid; /* incremented when pointers might be stale */
+
+/* ------------------- functions on any gobj ----------------------------- */
+EXTERN void gobj_getrect(t_gobj *x, t_glist *owner, int *x1, int *y1,
+ int *x2, int *y2);
+EXTERN void gobj_displace(t_gobj *x, t_glist *owner, int dx, int dy);
+EXTERN void gobj_select(t_gobj *x, t_glist *owner, int state);
+EXTERN void gobj_activate(t_gobj *x, t_glist *owner, int state);
+EXTERN void gobj_delete(t_gobj *x, t_glist *owner);
+EXTERN void gobj_vis(t_gobj *x, t_glist *glist, int flag);
+EXTERN int gobj_click(t_gobj *x, struct _glist *glist,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit);
+EXTERN void gobj_save(t_gobj *x, t_binbuf *b);
+EXTERN void gobj_properties(t_gobj *x, struct _glist *glist);
+
+/* -------------------- functions on glists --------------------- */
+EXTERN t_glist *glist_new( void);
+EXTERN void glist_init(t_glist *x);
+EXTERN void glist_add(t_glist *x, t_gobj *g);
+EXTERN void glist_cleanup(t_glist *x);
+EXTERN void glist_free(t_glist *x);
+
+EXTERN void glist_clear(t_glist *x);
+EXTERN t_canvas *glist_getcanvas(t_glist *x);
+EXTERN int glist_isselected(t_glist *x, t_gobj *y);
+EXTERN void glist_select(t_glist *x, t_gobj *y);
+EXTERN void glist_deselect(t_glist *x, t_gobj *y);
+EXTERN void glist_noselect(t_glist *x);
+EXTERN void glist_selectall(t_glist *x);
+EXTERN void glist_delete(t_glist *x, t_gobj *y);
+EXTERN void glist_retext(t_glist *x, t_text *y);
+EXTERN void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn,
+ t_glistkeyfn keyfn, int xpos, int ypos);
+EXTERN int glist_isvisible(t_glist *x);
+EXTERN int glist_istoplevel(t_glist *x);
+EXTERN t_glist *glist_findgraph(t_glist *x);
+EXTERN int glist_getfont(t_glist *x);
+EXTERN void glist_sort(t_glist *canvas);
+EXTERN void glist_read(t_glist *x, t_symbol *filename, t_symbol *format);
+EXTERN void glist_mergefile(t_glist *x, t_symbol *filename, t_symbol *format);
+
+EXTERN float glist_pixelstox(t_glist *x, float xpix);
+EXTERN float glist_pixelstoy(t_glist *x, float ypix);
+EXTERN float glist_xtopixels(t_glist *x, float xval);
+EXTERN float glist_ytopixels(t_glist *x, float yval);
+EXTERN float glist_dpixtodx(t_glist *x, float dxpix);
+EXTERN float glist_dpixtody(t_glist *x, float dypix);
+
+EXTERN void glist_redrawitem(t_glist *owner, t_gobj *gobj);
+EXTERN void glist_getnextxy(t_glist *gl, int *xval, int *yval);
+EXTERN void glist_glist(t_glist *g, t_symbol *s, int argc, t_atom *argv);
+EXTERN t_glist *glist_addglist(t_glist *g, t_symbol *sym,
+ float x1, float y1, float x2, float y2,
+ float px1, float py1, float px2, float py2);
+EXTERN void glist_arraydialog(t_glist *parent, t_symbol *name,
+ t_floatarg size, t_floatarg saveit, t_floatarg newgraph);
+EXTERN t_binbuf *glist_writetobinbuf(t_glist *x, int wholething);
+EXTERN int glist_isgraph(t_glist *x);
+EXTERN void glist_redraw(t_glist *x);
+EXTERN void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime,
+ char *tag, int x1, int y1, int x2, int y2);
+EXTERN void glist_eraseiofor(t_glist *glist, t_object *ob, char *tag);
+EXTERN void canvas_create_editor(t_glist *x, int createit);
+
+/* -------------------- functions on texts ------------------------- */
+EXTERN void text_setto(t_text *x, t_glist *glist, char *buf, int bufsize);
+EXTERN void text_drawborder(t_text *x, t_glist *glist, char *tag,
+ int width, int height, int firsttime);
+EXTERN void text_eraseborder(t_text *x, t_glist *glist, char *tag);
+EXTERN int text_xcoord(t_text *x, t_glist *glist);
+EXTERN int text_ycoord(t_text *x, t_glist *glist);
+EXTERN int text_xpix(t_text *x, t_glist *glist);
+EXTERN int text_ypix(t_text *x, t_glist *glist);
+EXTERN int text_shouldvis(t_text *x, t_glist *glist);
+
+/* -------------------- functions on rtexts ------------------------- */
+#define RTEXT_DOWN 1
+#define RTEXT_DRAG 2
+#define RTEXT_DBL 3
+#define RTEXT_SHIFT 4
+
+EXTERN t_rtext *rtext_new(t_glist *glist, t_text *who, t_rtext *next,
+ int sendipup);
+EXTERN t_rtext *rtext_remove(t_rtext *first, t_rtext *x);
+EXTERN t_rtext *glist_findrtext(t_glist *gl, t_text *who);
+EXTERN int rtext_height(t_rtext *x);
+EXTERN void rtext_displace(t_rtext *x, int dx, int dy);
+EXTERN void rtext_select(t_rtext *x, int state);
+EXTERN void rtext_activate(t_rtext *x, int state);
+EXTERN void rtext_free(t_rtext *x);
+EXTERN void rtext_key(t_rtext *x, int n, t_symbol *s);
+EXTERN void rtext_mouse(t_rtext *x, int xval, int yval, int flag);
+EXTERN void rtext_retext(t_rtext *x);
+EXTERN int rtext_width(t_rtext *x);
+EXTERN int rtext_height(t_rtext *x);
+EXTERN char *rtext_gettag(t_rtext *x);
+EXTERN void rtext_gettext(t_rtext *x, char **buf, int *bufsize);
+
+/* -------------------- functions on canvases ------------------------ */
+EXTERN t_class *canvas_class;
+
+EXTERN t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv);
+EXTERN t_symbol *canvas_makebindsym(t_symbol *s);
+EXTERN void canvas_vistext(t_canvas *x, t_text *y);
+EXTERN void canvas_fixlinesfor(t_canvas *x, t_text *text);
+EXTERN void canvas_deletelinesfor(t_canvas *x, t_text *text);
+EXTERN void canvas_stowconnections(t_canvas *x);
+EXTERN void canvas_restoreconnections(t_canvas *x);
+EXTERN void canvas_redraw(t_canvas *x);
+
+EXTERN t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *sym);
+EXTERN void canvas_rminlet(t_canvas *x, t_inlet *ip);
+EXTERN t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *sym);
+EXTERN void canvas_rmoutlet(t_canvas *x, t_outlet *op);
+EXTERN void canvas_redrawallfortemplate(t_canvas *template);
+EXTERN void canvas_zapallfortemplate(t_canvas *template);
+EXTERN void canvas_setusedastemplate(t_canvas *x);
+EXTERN t_canvas *canvas_getcurrent(void);
+EXTERN void canvas_setcurrent(t_canvas *x);
+EXTERN void canvas_unsetcurrent(t_canvas *x);
+EXTERN t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s);
+EXTERN t_canvas *canvas_getrootfor(t_canvas *x);
+EXTERN void canvas_dirty(t_canvas *x, t_int n);
+EXTERN int canvas_getfont(t_canvas *x);
+typedef int (*t_canvasapply)(t_canvas *x, t_int x1, t_int x2, t_int x3);
+
+EXTERN t_int *canvas_recurapply(t_canvas *x, t_canvasapply *fn,
+ t_int x1, t_int x2, t_int x3);
+
+EXTERN void canvas_resortinlets(t_canvas *x);
+EXTERN void canvas_resortoutlets(t_canvas *x);
+EXTERN void canvas_free(t_canvas *x);
+EXTERN void canvas_updatewindowlist( void);
+EXTERN void canvas_editmode(t_canvas *x, t_floatarg yesplease);
+EXTERN int canvas_isabstraction(t_canvas *x);
+EXTERN int canvas_istable(t_canvas *x);
+EXTERN int canvas_showtext(t_canvas *x);
+EXTERN void canvas_vis(t_canvas *x, t_floatarg f);
+EXTERN t_canvasenvironment *canvas_getenv(t_canvas *x);
+EXTERN void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir);
+EXTERN void canvas_loadbang(t_canvas *x);
+EXTERN int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos,
+ int *x1p, int *y1p, int *x2p, int *y2p);
+EXTERN int canvas_setdeleting(t_canvas *x, int flag);
+
+/* ---- functions on canvasses as objects --------------------- */
+
+EXTERN void canvas_fattenforscalars(t_canvas *x,
+ int *x1, int *y1, int *x2, int *y2);
+EXTERN void canvas_visforscalars(t_canvas *x, t_glist *glist, int vis);
+EXTERN int canvas_clicksub(t_canvas *x, int xpix, int ypix, int shift,
+ int alt, int dbl, int doit);
+EXTERN t_glist *canvas_getglistonsuper(void);
+
+EXTERN void linetraverser_start(t_linetraverser *t, t_canvas *x);
+EXTERN t_outconnect *linetraverser_next(t_linetraverser *t);
+EXTERN void linetraverser_skipobject(t_linetraverser *t);
+
+/* --------------------- functions on tscalars --------------------- */
+
+EXTERN void tscalar_getrect(t_tscalar *x, t_glist *owner,
+ int *xp1, int *yp1, int *xp2, int *yp2);
+EXTERN void tscalar_vis(t_tscalar *x, t_glist *owner, int flag);
+EXTERN int tscalar_click(t_tscalar *x, int xpix, int ypix, int shift,
+ int alt, int dbl, int doit);
+
+/* --------- functions on garrays (graphical arrays) -------------------- */
+
+EXTERN t_template *garray_template(t_garray *x);
+
+/* -------------------- arrays --------------------- */
+EXTERN t_garray *graph_array(t_glist *gl, t_symbol *s, t_symbol *template,
+ t_floatarg f, t_floatarg saveit);
+EXTERN t_array *array_new(t_symbol *templatesym, t_gpointer *parent);
+EXTERN void array_resize(t_array *x, t_template *template, int n);
+EXTERN void array_free(t_array *x);
+
+/* --------------------- gpointers and stubs ---------------- */
+EXTERN t_gstub *gstub_new(t_glist *gl, t_array *a);
+EXTERN void gstub_cutoff(t_gstub *gs);
+EXTERN void gpointer_setglist(t_gpointer *gp, t_glist *glist, t_scalar *x);
+
+/* --------------------- scalars ------------------------- */
+EXTERN void word_init(t_word *wp, t_template *template, t_gpointer *gp);
+EXTERN void word_restore(t_word *wp, t_template *template,
+ int argc, t_atom *argv);
+EXTERN t_scalar *scalar_new(t_glist *owner,
+ t_symbol *templatesym);
+EXTERN void scalar_getbasexy(t_scalar *x, float *basex, float *basey);
+
+/* ------helper routines for "garrays" and "plots" -------------- */
+EXTERN int array_doclick(t_array *array, t_glist *glist, t_gobj *gobj,
+ t_symbol *elemtemplatesym,
+ float linewidth, float xloc, float xinc, float yloc,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit);
+
+EXTERN void array_getcoordinate(t_glist *glist,
+ char *elem, int xonset, int yonset, int wonset, int indx,
+ float basex, float basey, float xinc,
+ float *xp, float *yp, float *wp);
+
+EXTERN int array_getfields(t_symbol *elemtemplatesym,
+ t_canvas **elemtemplatecanvasp,
+ t_template **elemtemplatep, int *elemsizep,
+ int *xonsetp, int *yonsetp, int *wonsetp);
+
+/* --------------------- templates ------------------------- */
+EXTERN t_template *template_new(t_symbol *sym, int argc, t_atom *argv);
+EXTERN void template_free(t_template *x);
+EXTERN int template_match(t_template *x1, t_template *x2);
+EXTERN int template_find_field(t_template *x, t_symbol *name, int *p_onset,
+ int *p_type, t_symbol **p_arraytype);
+EXTERN t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp,
+ int loud);
+EXTERN void template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp,
+ t_float f, int loud);
+EXTERN t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname,
+ t_word *wp, int loud);
+EXTERN void template_setsymbol(t_template *x, t_symbol *fieldname,
+ t_word *wp, t_symbol *s, int loud);
+
+EXTERN t_template *gtemplate_get(t_gtemplate *x);
+EXTERN t_template *template_findbyname(t_symbol *s);
+EXTERN t_canvas *template_findcanvas(t_template *template);
+
+EXTERN t_float template_getfloat(t_template *x, t_symbol *fieldname,
+ t_word *wp, int loud);
+EXTERN void template_setfloat(t_template *x, t_symbol *fieldname,
+ t_word *wp, t_float f, int loud);
+EXTERN t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname,
+ t_word *wp, int loud);
+EXTERN void template_setsymbol(t_template *x, t_symbol *fieldname,
+ t_word *wp, t_symbol *s, int loud);
+
+/* ----------------------- guiconnects, g_guiconnect.c --------- */
+EXTERN t_guiconnect *guiconnect_new(t_pd *who, t_symbol *sym);
+EXTERN void guiconnect_notarget(t_guiconnect *x, double timedelay);
diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c
new file mode 100644
index 00000000..4e6783ff
--- /dev/null
+++ b/pd/src/g_editor.c
@@ -0,0 +1,1658 @@
+/* Copyright (c) 1997-2001 Miller Puckette and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include <string.h>
+
+void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename,
+ int selectem);
+
+/* ------------------ forward function declarations --------------- */
+static void canvas_doclear(t_canvas *x);
+static void glist_setlastxy(t_glist *gl, int xval, int yval);
+static void glist_donewloadbangs(t_glist *x);
+
+/* ---------------- generic widget behavior ------------------------- */
+
+void gobj_getrect(t_gobj *x, t_glist *glist, int *x1, int *y1,
+ int *x2, int *y2)
+{
+ if (x->g_pd->c_wb && x->g_pd->c_wb->w_getrectfn)
+ (*x->g_pd->c_wb->w_getrectfn)(x, glist, x1, y1, x2, y2);
+}
+
+void gobj_displace(t_gobj *x, t_glist *glist, int dx, int dy)
+{
+ if (x->g_pd->c_wb && x->g_pd->c_wb->w_displacefn)
+ (*x->g_pd->c_wb->w_displacefn)(x, glist, dx, dy);
+}
+
+void gobj_select(t_gobj *x, t_glist *glist, int state)
+{
+ if (x->g_pd->c_wb && x->g_pd->c_wb->w_selectfn)
+ (*x->g_pd->c_wb->w_selectfn)(x, glist, state);
+}
+
+void gobj_activate(t_gobj *x, t_glist *glist, int state)
+{
+ if (x->g_pd->c_wb && x->g_pd->c_wb->w_activatefn)
+ (*x->g_pd->c_wb->w_activatefn)(x, glist, state);
+}
+
+void gobj_delete(t_gobj *x, t_glist *glist)
+{
+ if (x->g_pd->c_wb && x->g_pd->c_wb->w_deletefn)
+ (*x->g_pd->c_wb->w_deletefn)(x, glist);
+}
+
+void gobj_vis(t_gobj *x, struct _glist *glist, int flag)
+{
+ if (x->g_pd->c_wb && x->g_pd->c_wb->w_visfn)
+ (*x->g_pd->c_wb->w_visfn)(x, glist, flag);
+}
+
+int gobj_click(t_gobj *x, struct _glist *glist,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ if (x->g_pd->c_wb && x->g_pd->c_wb->w_clickfn)
+ return ((*x->g_pd->c_wb->w_clickfn)(x,
+ glist, xpix, ypix, shift, alt, dbl, doit));
+ else return (0);
+}
+
+void gobj_save(t_gobj *x, t_binbuf *b)
+{
+ if (x->g_pd->c_wb && x->g_pd->c_wb->w_savefn)
+ (*x->g_pd->c_wb->w_savefn)(x, b);
+}
+
+void gobj_properties(t_gobj *x, struct _glist *glist)
+{
+ if (x->g_pd->c_wb && x->g_pd->c_wb->w_propertiesfn)
+ (*x->g_pd->c_wb->w_propertiesfn)(x, glist);
+}
+
+/* ------------------------ managing the selection ----------------- */
+
+int glist_isselected(t_glist *x, t_gobj *y)
+{
+ if (x->gl_editor)
+ {
+ t_selection *sel;
+ for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
+ if (sel->sel_what == y) return (1);
+ }
+ return (0);
+}
+
+ /* call this for unselected objects only */
+void glist_select(t_glist *x, t_gobj *y)
+{
+ if (x->gl_editor)
+ {
+ t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
+ /* LATER #ifdef out the following check */
+ if (glist_isselected(x, y)) bug("glist_select");
+ sel->sel_next = x->gl_editor->e_selection;
+ sel->sel_what = y;
+ x->gl_editor->e_selection = sel;
+ gobj_select(y, x, 1);
+ }
+}
+
+ /* call this for selected objects only */
+void glist_deselect(t_glist *x, t_gobj *y)
+{
+ int fixdsp = 0;
+ static int reenter = 0;
+ if (reenter) return;
+ reenter = 1;
+ if (x->gl_editor)
+ {
+ t_selection *sel, *sel2;
+ t_rtext *z = 0;
+ if (!glist_isselected(x, y)) bug("glist_deselect");
+ if (x->gl_editor->e_textedfor)
+ {
+ t_rtext *fuddy = glist_findrtext(x, (t_text *)y);
+ if (x->gl_editor->e_textedfor == fuddy)
+ {
+ if (x->gl_editor->e_textdirty)
+ {
+ z = fuddy;
+ canvas_stowconnections(glist_getcanvas(x));
+ }
+ gobj_activate(y, x, 0);
+ }
+ if (zgetfn(&y->g_pd, gensym("dsp")))
+ fixdsp = 1;
+ }
+ if ((sel = x->gl_editor->e_selection)->sel_what == y)
+ {
+ x->gl_editor->e_selection = x->gl_editor->e_selection->sel_next;
+ gobj_select(sel->sel_what, x, 0);
+ freebytes(sel, sizeof(*sel));
+ }
+ else
+ {
+ for (sel = x->gl_editor->e_selection; sel2 = sel->sel_next;
+ sel = sel2)
+ {
+ if (sel2->sel_what == y)
+ {
+ sel->sel_next = sel2->sel_next;
+ gobj_select(sel2->sel_what, x, 0);
+ freebytes(sel2, sizeof(*sel2));
+ break;
+ }
+ }
+ }
+ if (z)
+ {
+ char *buf;
+ int bufsize;
+
+ rtext_gettext(z, &buf, &bufsize);
+ text_setto((t_text *)y, x, buf, bufsize);
+ canvas_fixlinesfor(glist_getcanvas(x), (t_text *)y);
+ x->gl_editor->e_textedfor = 0;
+ }
+ if (fixdsp)
+ canvas_update_dsp();
+ }
+ reenter = 0;
+}
+
+void glist_noselect(t_glist *x)
+{
+ if (x->gl_editor) while (x->gl_editor->e_selection)
+ glist_deselect(x, x->gl_editor->e_selection->sel_what);
+}
+
+void glist_selectall(t_glist *x)
+{
+ if (x->gl_editor)
+ {
+ glist_noselect(x);
+ if (x->gl_list)
+ {
+ t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
+ t_gobj *y = x->gl_list;
+ x->gl_editor->e_selection = sel;
+ sel->sel_what = y;
+ gobj_select(y, x, 1);
+ while (y = y->g_next)
+ {
+ t_selection *sel2 = (t_selection *)getbytes(sizeof(*sel2));
+ sel->sel_next = sel2;
+ sel = sel2;
+ sel->sel_what = y;
+ gobj_select(y, x, 1);
+ }
+ sel->sel_next = 0;
+ }
+ }
+}
+
+
+/* ------------------------ event handling ------------------------ */
+
+#define CURSOR_RUNMODE_NOTHING 0
+#define CURSOR_RUNMODE_CLICKME 1
+#define CURSOR_RUNMODE_THICKEN 2
+#define CURSOR_RUNMODE_ADDPOINT 3
+#define CURSOR_EDITMODE_NOTHING 4
+#define CURSOR_EDITMODE_CONNECT 5
+#define CURSOR_EDITMODE_DISCONNECT 6
+
+static char *cursorlist[] = {
+#ifdef NT
+ "right_ptr", /* CURSOR_RUNMODE_NOTHING */
+#else
+ "left_ptr", /* CURSOR_RUNMODE_NOTHING */
+#endif
+ "arrow", /* CURSOR_RUNMODE_CLICKME */
+ "sb_v_double_arrow", /* CURSOR_RUNMODE_THICKEN */
+ "plus", /* CURSOR_RUNMODE_ADDPOINT */
+ "hand2", /* CURSOR_EDITMODE_NOTHING */
+ "circle", /* CURSOR_EDITMODE_CONNECT */
+ "X_cursor" /* CURSOR_EDITMODE_DISCONNECT */
+};
+
+void canvas_setcursor(t_canvas *x, unsigned int cursornum)
+{
+ static t_canvas *xwas;
+ static unsigned int cursorwas;
+ if (cursornum >= sizeof(cursorlist)/sizeof *cursorlist)
+ {
+ bug("canvas_setcursor");
+ return;
+ }
+ if (xwas != x || cursorwas != cursornum)
+ {
+ sys_vgui(".x%x configure -cursor %s\n", x, cursorlist[cursornum]);
+ xwas = x;
+ cursorwas = cursornum;
+ }
+}
+
+ /* check if a point lies in a gobj. */
+int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos,
+ int *x1p, int *y1p, int *x2p, int *y2p)
+{
+ int x1, y1, x2, y2;
+ t_text *ob;
+ if ((ob = pd_checkobject(&y->g_pd)) &&
+ !text_shouldvis(ob, x))
+ return (0);
+ gobj_getrect(y, x, &x1, &y1, &x2, &y2);
+ if (xpos >= x1 && xpos <= x2 && ypos >= y1 && ypos <= y2)
+ {
+ *x1p = x1;
+ *y1p = y1;
+ *x2p = x2;
+ *y2p = y2;
+ return (1);
+ }
+ else return (0);
+}
+
+ /* find the last gobj, if any, containing the point. */
+static t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,
+ int *x1p, int *y1p, int *x2p, int *y2p)
+{
+ t_gobj *y, *rval = 0;
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ if (canvas_hitbox(x, y, xpos, ypos, x1p, y1p, x2p, y2p))
+ rval = y;
+ }
+ return (rval);
+}
+
+ /* right-clicking on a canvas object pops up a menu. */
+static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y)
+{
+ int canprop, canopen;
+ canprop = (!y || (y && y->g_pd->c_wb && y->g_pd->c_wb->w_propertiesfn));
+ canopen = (y && zgetfn(&y->g_pd, gensym("menu-open")));
+ sys_vgui("pdtk_canvas_popup .x%x %d %d %d %d\n",
+ x, xpos, ypos, canprop, canopen);
+}
+
+ /* tell GUI to create a properties dialog on the canvas. We tell
+ the user the negative of the "pixel" y scale to make it appear to grow
+ naturally upward, whereas pixels grow downward. */
+static void canvas_properties(t_glist *x)
+{
+ char graphbuf[200];
+ sprintf(graphbuf, "pdtk_canvas_dialog %%s %g %g %g %g \n",
+ glist_dpixtodx(x, 1), -glist_dpixtody(x, 1),
+ (float)glist_isgraph(x), (float)x->gl_stretch);
+ gfxstub_new(&x->gl_pd, x, graphbuf);
+}
+
+
+void canvas_setgraph(t_glist *x, int flag)
+{
+ if (!flag && glist_isgraph(x))
+ {
+ if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
+ gobj_vis(&x->gl_gobj, x->gl_owner, 0);
+ x->gl_isgraph = 0;
+ if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
+ {
+ gobj_vis(&x->gl_gobj, x->gl_owner, 1);
+ canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
+ }
+ }
+ else if (flag && !glist_isgraph(x))
+ {
+ if (x->gl_pixwidth <= 0)
+ x->gl_pixwidth = GLIST_DEFGRAPHWIDTH;
+
+ if (x->gl_pixheight <= 0)
+ x->gl_pixheight = GLIST_DEFGRAPHHEIGHT;
+
+ if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
+ gobj_vis(&x->gl_gobj, x->gl_owner, 0);
+ x->gl_isgraph = 1;
+ /* if (x->gl_owner && glist_isvisible(x->gl_owner))
+ canvas_vis(x, 1); */
+ if (x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
+ canvas_create_editor(x, 1);
+ if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
+ {
+ gobj_vis(&x->gl_gobj, x->gl_owner, 1);
+ canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
+ }
+ }
+}
+
+ /* called from the gui when "OK" is selected on the canvas properties
+ dialog. Again we negate "y" scale. */
+static void canvas_donecanvasdialog(t_glist *x, t_floatarg xperpix,
+ t_floatarg yperpix, t_floatarg fgraphme)
+{
+ int graphme = (fgraphme != 0), redraw = 0;
+ yperpix = -yperpix;
+ if (xperpix == 0)
+ xperpix = 1;
+ if (yperpix == 0)
+ yperpix = 1;
+ canvas_setgraph(x, graphme);
+ if (!x->gl_isgraph && (xperpix != glist_dpixtodx(x, 1)))
+ {
+ if (xperpix > 0)
+ {
+ x->gl_x1 = 0;
+ x->gl_x2 = xperpix;
+ }
+ else
+ {
+ x->gl_x1 = -xperpix * (x->gl_screenx2 - x->gl_screenx1);
+ x->gl_x2 = x->gl_x1 + xperpix;
+ }
+ redraw = 1;
+ }
+ if (!x->gl_isgraph && (yperpix != glist_dpixtody(x, 1)))
+ {
+ if (yperpix > 0)
+ {
+ x->gl_y1 = 0;
+ x->gl_y2 = yperpix;
+ }
+ else
+ {
+ x->gl_y1 = -yperpix * (x->gl_screeny2 - x->gl_screeny1);
+ x->gl_y2 = x->gl_y1 + yperpix;
+ }
+ redraw = 1;
+ }
+ if (redraw)
+ canvas_redraw(x);
+}
+
+ /* called from the gui when a popup menu comes back with "properties,"
+ "open," or "help." */
+static void canvas_done_popup(t_canvas *x, float which, float xpos, float ypos)
+{
+ char pathbuf[MAXPDSTRING], namebuf[MAXPDSTRING];
+ t_gobj *y;
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ int x1, y1, x2, y2;
+ if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2))
+ {
+ if (which == 0) /* properties */
+ {
+ if (!y->g_pd->c_wb || !y->g_pd->c_wb->w_propertiesfn)
+ continue;
+ gobj_properties(y, x);
+ return;
+ }
+ else if (which == 1) /* open */
+ {
+ if (!zgetfn(&y->g_pd, gensym("menu-open")))
+ continue;
+ vmess(&y->g_pd, gensym("menu-open"), "");
+ return;
+ }
+ else /* help */
+ {
+ char *s = class_gethelpname(pd_class(&y->g_pd));
+ strcpy(pathbuf, sys_libdir->s_name);
+ strcat(pathbuf, "/doc/5.reference");
+ strcpy(namebuf, s);
+ if (strcmp(namebuf + strlen(namebuf) - 3, ".pd"))
+ strcat(namebuf, ".pd");
+ glob_evalfile(0, gensym(namebuf), gensym(pathbuf));
+ return;
+ }
+ }
+ }
+ if (which == 0)
+ canvas_properties(x);
+ else if (which == 2)
+ {
+ strcpy(pathbuf, sys_libdir->s_name);
+ strcat(pathbuf, "/doc/5.reference/0.INTRO.txt");
+ sys_vgui("menu_opentext %s\n", pathbuf);
+ }
+}
+
+#define NOMOD 0
+#define SHIFTMOD 1
+#define CTRLMOD 2
+#define ALTMOD 4
+#define RIGHTCLICK 8
+
+/* on one-button-mouse machines, you can use double click to
+ mean right click (which gets the popup menu.) Do this for Mac. */
+#ifdef MACOSX
+#define SIMULATERIGHTCLICK
+#endif
+
+#ifdef SIMULATERIGHTCLICK
+static double canvas_upclicktime;
+static int canvas_upx, canvas_upy;
+#define DCLICKINTERVAL 0.25
+#endif
+
+ /* mouse click */
+void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
+ int mod, int doit)
+{
+ t_gobj *y;
+ int shiftmod, runmode, altmod, rightclick, protectmod;
+ int x1, y1, x2, y2, clickreturned = 0;
+
+ if (!x->gl_editor)
+ {
+ bug("editor");
+ return;
+ }
+
+ shiftmod = (mod & SHIFTMOD);
+ runmode = ((mod & CTRLMOD) || (!x->gl_edit));
+ altmod = (mod & ALTMOD);
+ rightclick = (mod & RIGHTCLICK);
+ protectmod = x->gl_protect;
+
+#ifdef SIMULATERIGHTCLICK
+ if (doit && !runmode && xpos == canvas_upx && ypos == canvas_upy &&
+ sys_getrealtime() - canvas_upclicktime < DCLICKINTERVAL)
+ rightclick = 1;
+#endif
+
+ x->gl_editor->e_lastmoved = 0;
+ if (doit)
+ {
+ x->gl_editor->e_grab = 0;
+ x->gl_editor->e_onmotion = MA_NONE;
+ }
+ /* post("click %d %d %d %d", xpos, ypos, which, mod); */
+
+ if (x->gl_editor->e_onmotion != MA_NONE)
+ return;
+
+ x->gl_editor->e_xwas = xpos;
+ x->gl_editor->e_ywas = ypos;
+
+ if (runmode && !rightclick)
+ {
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ /* check if the object wants to be clicked */
+ if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
+ && (clickreturned = gobj_click(y, x, xpos, ypos,
+ shiftmod, altmod, 0, doit)))
+ break;
+ }
+ if (!doit)
+ {
+ if (y)
+ canvas_setcursor(x, clickreturned);
+ else canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
+ }
+ return;
+ }
+ /* if not a runmode left click, fall here. */
+ if (y = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2))
+ {
+ t_object *ob = pd_checkobject(&y->g_pd);
+ /* check you're in the rectangle */
+ ob = pd_checkobject(&y->g_pd);
+ if (rightclick)
+ canvas_rightclick(x, xpos, ypos, y);
+ else if (shiftmod)
+ {
+ if (doit)
+ {
+ t_rtext *rt;
+ if (ob && (rt = x->gl_editor->e_textedfor) &&
+ rt == glist_findrtext(x, ob))
+ {
+ rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_SHIFT);
+ x->gl_editor->e_onmotion = MA_DRAGTEXT;
+ x->gl_editor->e_xwas = x1;
+ x->gl_editor->e_ywas = y1;
+ }
+ else
+ {
+ if (glist_isselected(x, y))
+ glist_deselect(x, y);
+ else glist_select(x, y);
+ }
+ }
+ }
+ else
+ {
+ /* look for an outlet */
+ int noutlet;
+ if (ob && (noutlet = obj_noutlets(ob)) && ypos >= y2-4)
+ {
+ int width = x2 - x1;
+ int nout1 = (noutlet > 1 ? noutlet - 1 : 1);
+ int closest = ((xpos-x1) * (nout1) + width/2)/width;
+ int hotspot = x1 +
+ (width - IOWIDTH) * closest / (nout1);
+ if (closest < noutlet &&
+ xpos >= (hotspot-1) && xpos <= hotspot + (IOWIDTH+1))
+ {
+ if (doit)
+ {
+ x->gl_editor->e_onmotion = MA_CONNECT;
+ x->gl_editor->e_xwas = xpos;
+ x->gl_editor->e_ywas = ypos;
+ sys_vgui(
+ ".x%x.c create line %d %d %d %d -tags x\n",
+ x, xpos, ypos, xpos, ypos);
+ }
+ else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
+ }
+ else if (doit)
+ goto nooutletafterall;
+ }
+ /* not in an outlet; select and move */
+ else if (doit)
+ {
+ t_rtext *rt;
+ /* check if the box is being text edited */
+ nooutletafterall:
+ if (ob && (rt = x->gl_editor->e_textedfor) &&
+ rt == glist_findrtext(x, ob))
+ {
+ rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_DOWN);
+ x->gl_editor->e_onmotion = MA_DRAGTEXT;
+ x->gl_editor->e_xwas = x1;
+ x->gl_editor->e_ywas = y1;
+ }
+ else
+ {
+ /* otherwise select and drag to displace */
+ if (!glist_isselected(x, y))
+ {
+ glist_noselect(x);
+ glist_select(x, y);
+ }
+ x->gl_editor->e_onmotion = MA_MOVE;
+ }
+ }
+ else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
+ }
+ return;
+ }
+ /* if right click doesn't hit any boxes, call rightclick
+ routine anyway */
+ if (rightclick)
+ canvas_rightclick(x, xpos, ypos, 0);
+
+ /* if not an editing action, and if we didn't hit a
+ box, set cursor and return */
+ if (runmode || rightclick)
+ {
+ canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
+ return;
+ }
+ /* having failed to find a box, we try lines now. */
+ if (!runmode && !altmod && !shiftmod && !protectmod)
+ {
+ t_linetraverser t;
+ t_outconnect *oc;
+ float fx = xpos, fy = ypos;
+ linetraverser_start(&t, glist_getcanvas(x));
+ while (oc = linetraverser_next(&t))
+ {
+ float lx1 = t.tr_lx1, ly1 = t.tr_ly1,
+ lx2 = t.tr_lx2, ly2 = t.tr_ly2;
+ float area = (lx2 - lx1) * (fy - ly1) -
+ (ly2 - ly1) * (fx - lx1);
+ float dsquare = (lx2-lx1) * (lx2-lx1) + (ly2-ly1) * (ly2-ly1);
+ if (area * area >= 50 * dsquare) continue;
+ if ((lx2-lx1) * (fx-lx1) + (ly2-ly1) * (fy-ly1) < 0) continue;
+ if ((lx2-lx1) * (lx2-fx) + (ly2-ly1) * (ly2-fy) < 0) continue;
+ if (doit)
+ {
+ sys_vgui(".x%x.c delete l%x\n",
+ glist_getcanvas(x), oc);
+ obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
+ }
+ else canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT);
+ return;
+ }
+ }
+ canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
+ if (doit)
+ {
+ if (!shiftmod) glist_noselect(x);
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags x\n",
+ x, xpos, ypos, xpos, ypos);
+ x->gl_editor->e_onmotion = MA_REGION;
+ }
+}
+
+void canvas_mousedown(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
+ t_floatarg which, t_floatarg mod)
+{
+ canvas_doclick(x, xpos, ypos, which, mod, 1);
+}
+
+static int canvas_isconnected (t_canvas *x, t_text *ob1, int n1,
+ t_text *ob2, int n2)
+{
+ t_linetraverser t;
+ t_outconnect *oc;
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ if (t.tr_ob == ob1 && t.tr_outno == n1 &&
+ t.tr_ob2 == ob2 && t.tr_inno == n2)
+ return (1);
+ return (0);
+}
+
+void canvas_doconnect(t_canvas *x, int xpos, int ypos, int which, int doit)
+{
+ int x11, y11, x12, y12;
+ t_gobj *y1;
+ int x21, y21, x22, y22;
+ t_gobj *y2;
+ int xwas = x->gl_editor->e_xwas,
+ ywas = x->gl_editor->e_ywas;
+ if (doit) sys_vgui(".x%x.c delete x\n", x);
+ else sys_vgui(".x%x.c coords x %d %d %d %d\n",
+ x, x->gl_editor->e_xwas,
+ x->gl_editor->e_ywas, xpos, ypos);
+
+ if ((y1 = canvas_findhitbox(x, xwas, ywas, &x11, &y11, &x12, &y12))
+ && (y2 = canvas_findhitbox(x, xpos, ypos, &x21, &y21, &x22, &y22)))
+ {
+ t_object *ob1 = pd_checkobject(&y1->g_pd);
+ t_object *ob2 = pd_checkobject(&y2->g_pd);
+ int noutlet1, ninlet2;
+ if (ob1 && ob2 && ob1 != ob2 &&
+ (noutlet1 = obj_noutlets(ob1))
+ && (ninlet2 = obj_ninlets(ob2)))
+ {
+ int width1 = x12 - x11, closest1, hotspot1;
+ int width2 = x22 - x21, closest2, hotspot2;
+ int lx1, lx2, ly1, ly2;
+ t_outconnect *oc;
+
+ if (noutlet1 > 1)
+ {
+ closest1 = ((xwas-x11) * (noutlet1-1) + width1/2)/width1;
+ hotspot1 = x11 +
+ (width1 - IOWIDTH) * closest1 / (noutlet1-1);
+ }
+ else closest1 = 0, hotspot1 = x11;
+
+ if (ninlet2 > 1)
+ {
+ closest2 = ((xpos-x21) * (ninlet2-1) + width2/2)/width2;
+ hotspot2 = x21 +
+ (width2 - IOWIDTH) * closest2 / (ninlet2-1);
+ }
+ else closest2 = 0, hotspot2 = x21;
+
+ if (closest1 >= noutlet1)
+ closest1 = noutlet1 - 1;
+ if (closest2 >= ninlet2)
+ closest2 = ninlet2 - 1;
+
+ if (canvas_isconnected (x, ob1, closest1, ob2, closest2))
+ {
+ canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
+ return;
+ }
+ if (doit)
+ {
+ oc = obj_connect(ob1, closest1, ob2, closest2);
+ lx1 = x11 + (noutlet1 > 1 ?
+ ((x12-x11-IOWIDTH) * closest1)/(noutlet1-1) : 0)
+ + IOMIDDLE;
+ ly1 = y12;
+ lx2 = x21 + (ninlet2 > 1 ?
+ ((x22-x21-IOWIDTH) * closest2)/(ninlet2-1) : 0)
+ + IOMIDDLE;
+ ly2 = y21;
+ sys_vgui(".x%x.c create line %d %d %d %d -tags l%x\n",
+ glist_getcanvas(x),
+ lx1, ly1, lx2, ly2, oc);
+ }
+ else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
+ return;
+ }
+ }
+ canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
+}
+
+void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit)
+{
+ if (doit)
+ {
+ t_gobj *y;
+ int lox, loy, hix, hiy;
+ if (x->gl_editor->e_xwas < xpos)
+ lox = x->gl_editor->e_xwas, hix = xpos;
+ else hix = x->gl_editor->e_xwas, lox = xpos;
+ if (x->gl_editor->e_ywas < ypos)
+ loy = x->gl_editor->e_ywas, hiy = ypos;
+ else hiy = x->gl_editor->e_ywas, loy = ypos;
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ int x1, y1, x2, y2;
+ gobj_getrect(y, x, &x1, &y1, &x2, &y2);
+ if (hix >= x1 && lox <= x2 && hiy >= y1 && loy <= y2
+ && !glist_isselected(x, y))
+ glist_select(x, y);
+ }
+ sys_vgui(".x%x.c delete x\n", x);
+ x->gl_editor->e_onmotion = 0;
+ }
+ else sys_vgui(".x%x.c coords x %d %d %d %d\n",
+ x, x->gl_editor->e_xwas,
+ x->gl_editor->e_ywas, xpos, ypos);
+}
+
+void canvas_mouseup(t_canvas *x,
+ t_floatarg fxpos, t_floatarg fypos, t_floatarg fwhich)
+{
+ int xpos = fxpos, ypos = fypos, which = fwhich;
+ /* post("mouseup %d %d %d", xpos, ypos, which); */
+ if (!x->gl_editor)
+ {
+ bug("editor");
+ return;
+ }
+#ifdef SIMULATERIGHTCLICK
+ canvas_upclicktime = sys_getrealtime();
+ canvas_upx = xpos;
+ canvas_upy = ypos;
+#endif
+
+ if (x->gl_editor->e_onmotion == MA_CONNECT)
+ canvas_doconnect(x, xpos, ypos, which, 1);
+ else if (x->gl_editor->e_onmotion == MA_REGION)
+ canvas_doregion(x, xpos, ypos, 1);
+ else if (x->gl_editor->e_onmotion == MA_MOVE)
+ {
+ /* after motion, if there's only one item selected, activate it */
+ if (x->gl_editor->e_selection &&
+ !(x->gl_editor->e_selection->sel_next))
+ gobj_activate(x->gl_editor->e_selection->sel_what,
+ x, 1);
+ }
+ else if (x->gl_editor->e_onmotion == MA_PASSOUT)
+ x->gl_editor->e_onmotion = 0;
+ x->gl_editor->e_onmotion = MA_NONE;
+}
+
+ /* this routine is called whenever a key is pressed or released. "x"
+ may be zero if there's no current canvas. The first argument is true or
+ fals for down/up; the second one is either a symbolic key name (e.g.,
+ "Right" or an Ascii key number. */
+void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av)
+{
+ static t_symbol *keynumsym, *keyupsym, *keynamesym;
+ float keynum, fflag;
+ if (ac < 2)
+ return;
+ fflag = (av[0].a_type == A_FLOAT ? av[0].a_w.w_float : 0);
+ keynum = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : 0);
+ if (keynum == '\\' || keynum == '{' || keynum == '}')
+ {
+ post("%c: dropped", (int)keynum);
+ return;
+ }
+ if (keynum == '\r') keynum = '\n';
+ /* post("key %c", keynum); */
+ if (av[1].a_type == A_SYMBOL &&
+ !strcmp(av[1].a_w.w_symbol->s_name, "Return"))
+ keynum = '\n';
+ if (!keynumsym)
+ {
+ keynumsym = gensym("#key");
+ keyupsym = gensym("#keyup");
+ keynamesym = gensym("#keyname");
+ }
+ if (keynumsym->s_thing && (fflag != 0))
+ pd_float(keynumsym->s_thing, keynum);
+ if (keyupsym->s_thing && (fflag == 0))
+ pd_float(keyupsym->s_thing, keynum);
+ if (keynamesym->s_thing)
+ {
+ t_atom at[2];
+ at[0] = av[0];
+ if (av[1].a_type == A_SYMBOL)
+ at[1] = av[1];
+ else
+ {
+ char buf[3];
+ sprintf(buf, "%c", (int)(av[1].a_w.w_float));
+ SETSYMBOL(at+1, gensym(buf));
+ }
+ pd_list(keynamesym->s_thing, 0, 2, at);
+ }
+ if (x && (fflag != 0))
+ {
+ if (!x->gl_editor)
+ {
+ bug("editor");
+ return;
+ }
+ /* if an object has "grabbed" keys just send them on */
+ if (x->gl_editor->e_grab && (keynum != 0)
+ && x->gl_editor->e_keyfn)
+ (* x->gl_editor->e_keyfn)
+ (x->gl_editor->e_grab, keynum);
+ /* if a text editor is open send it on */
+ else if (x->gl_editor->e_textedfor)
+ {
+ rtext_key(x->gl_editor->e_textedfor,
+ (int)keynum,
+ (av[1].a_type == A_SYMBOL ? av[1].a_w.w_symbol : &s_));
+ if (x->gl_editor->e_textdirty)
+ canvas_dirty(x, 1);
+ }
+ /* otherwise check for backspace or clear and do so */
+ else if (keynum == 8 || keynum == 127)
+ canvas_doclear(x);
+ }
+}
+
+void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
+ t_floatarg fmod)
+{
+ /* post("motion %d %d", xpos, ypos); */
+ int mod = fmod;
+ if (!x->gl_editor)
+ {
+ bug("editor");
+ return;
+ }
+ glist_setlastxy(x, xpos, ypos);
+ if (x->gl_editor->e_onmotion == MA_MOVE)
+ {
+ t_selection *y;
+ int resortin = 0, resortout = 0;
+ for (y = x->gl_editor->e_selection; y; y = y->sel_next)
+ {
+ t_class *cl = pd_class(&y->sel_what->g_pd);
+ gobj_displace(y->sel_what, x,
+ xpos - x->gl_editor->e_xwas,
+ ypos - x->gl_editor->e_ywas);
+ if (cl == vinlet_class) resortin = 1;
+ else if (cl == voutlet_class) resortout = 1;
+ }
+ x->gl_editor->e_xwas = xpos;
+ x->gl_editor->e_ywas = ypos;
+ if (resortin) canvas_resortinlets(x);
+ if (resortout) canvas_resortoutlets(x);
+ canvas_dirty(x, 1);
+ }
+ else if (x->gl_editor->e_onmotion == MA_REGION)
+ canvas_doregion(x, xpos, ypos, 0);
+ else if (x->gl_editor->e_onmotion == MA_CONNECT)
+ canvas_doconnect(x, xpos, ypos, 0, 0);
+ else if (x->gl_editor->e_onmotion == MA_PASSOUT)
+ {
+ (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd,
+ xpos - x->gl_editor->e_xwas,
+ ypos - x->gl_editor->e_ywas);
+ x->gl_editor->e_xwas = xpos;
+ x->gl_editor->e_ywas = ypos;
+ }
+ else if (x->gl_editor->e_onmotion == MA_DRAGTEXT)
+ {
+ t_rtext *rt = x->gl_editor->e_textedfor;
+ if (rt)
+ rtext_mouse(rt, xpos - x->gl_editor->e_xwas,
+ ypos - x->gl_editor->e_ywas, RTEXT_DRAG);
+ }
+ else canvas_doclick(x, xpos, ypos, 0, mod, 0);
+
+ x->gl_editor->e_lastmoved = 1;
+}
+
+void canvas_startmotion(t_canvas *x)
+{
+ int xval, yval;
+ if (!x->gl_editor) return;
+ glist_getnextxy(x, &xval, &yval);
+ if (xval == 0 && yval == 0) return;
+ x->gl_editor->e_onmotion = MA_MOVE;
+ x->gl_editor->e_xwas = xval;
+ x->gl_editor->e_ywas = yval;
+}
+
+/* ----------------------------- window stuff ----------------------- */
+
+void canvas_print(t_canvas *x, t_symbol *s)
+{
+ if (*s->s_name) sys_vgui(".x%x.c postscript -file %s\n", x, s->s_name);
+ else sys_vgui(".x%x.c postscript -file x.ps\n", x);
+}
+
+
+#if 0 /* LATER fix this to re-load abstractions when the patch changes.
+ The best way to do this is probably to rewrite "stowconnections"
+ so that it can operate either on the selection as now or on
+ another replacement criterion. Could do the same trick for
+ externs if we wish. */
+ /* recursively check for abstractions to reload as result of a save. */
+static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir)
+{
+ t_gobj *g;
+ for (g = gl->gl_list; g; g = g->g_next)
+ {
+ if (pd_class(&g->g_pd) == canvas_class &&
+ canvas_isabstraction(&g->g_pd) &&
+ ((t_canvas *)g)->gl_name == name &&
+ ((t_canvas *)g)->gl_env->ce_dir == dir)
+ {
+ .....
+ glist_noselect(gl);
+ canvas_vis(glist_getcanvas(gl), 1);
+ canvas_editmode(glist_getcanvas(gl), 1.);
+ glist_select(gl, g);
+ return (1);
+ }
+ else if (pd_class(&g->g_pd) == graph_class)
+ glist_doreload((t_glist *)g, name, dir);
+ else if (pd_class(&g->g_pd) == canvas_class)
+ glist_doreload(&((t_canvas *)g),
+ name, dir);
+ }
+ }
+ return (0);
+}
+
+static void canvas_reload(t_symbol *name, t_symbol *dir)
+{
+ t_canvas *x;
+ /* find all root canvases */
+ for (x = canvas_list; x; x = x->gl_next)
+ glist_doreload(x, name, dir);
+}
+
+#endif /* 0 -- also uncomment call to canvas_reload below */
+
+void canvas_menuclose(t_canvas *x, t_floatarg force)
+{
+ if (x->gl_owner)
+ canvas_vis(x, 0);
+ else if ((force != 0) || (!x->gl_dirty))
+ pd_free(&x->gl_pd);
+ else sys_vgui("pdtk_check {This window has been modified. Close anyway?}\
+ {.x%x menuclose 1;\n}\n", x);
+}
+
+ /* put up a dialog which may call canvas_font back to do the work */
+static void canvas_menufont(t_canvas *x)
+{
+ char buf[80];
+ t_canvas *x2 = canvas_getrootfor(x);
+ gfxstub_deleteforkey(x2);
+ sprintf(buf, "pdtk_canvas_dofont %%s %d\n", x2->gl_font);
+ gfxstub_new(&x2->gl_pd, &x2->gl_pd, buf);
+}
+
+static int canvas_find_index1, canvas_find_index2;
+static t_binbuf *canvas_findbuf;
+int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf);
+t_gobj *canvas_selectme; /* HACK */
+
+ /* find an atom or string of atoms */
+static int canvas_dofind(t_canvas *x, int *myindex1p)
+{
+ t_gobj *y;
+ int myindex1 = *myindex1p, myindex2;
+ if (myindex1 >= canvas_find_index1)
+ {
+ for (y = x->gl_list, myindex2 = 0; y;
+ y = y->g_next, myindex2++)
+ {
+ t_object *ob = 0;
+ if (ob = pd_checkobject(&y->g_pd))
+ {
+ if (binbuf_match(ob->ob_binbuf, canvas_findbuf))
+ {
+ if (myindex1 > canvas_find_index1 ||
+ myindex1 == canvas_find_index1 &&
+ myindex2 > canvas_find_index2)
+ {
+ canvas_find_index1 = myindex1;
+ canvas_find_index2 = myindex2;
+ glist_noselect(x);
+ if (glist_isvisible(x))
+ {
+ canvas_vis(x, 1);
+ canvas_editmode(x, 1.);
+ glist_select(x, y);
+ }
+ else
+ {
+ /* LATER fix so we can select it right here.
+ ERight now, HACK it so that canvas_map selects it.
+ We can't select earlier because the rtexts aren't
+ created in time. Should create the rtexts in
+ canvas_vis() but we don't so that yet. */
+
+ canvas_selectme = y;
+ canvas_vis(x, 1);
+ }
+ return (1);
+ }
+ }
+ }
+ }
+ }
+ for (y = x->gl_list, myindex2 = 0; y; y = y->g_next, myindex2++)
+ {
+ if (pd_class(&y->g_pd) == canvas_class)
+ {
+ (*myindex1p)++;
+ if (canvas_dofind((t_canvas *)y, myindex1p))
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static void canvas_find(t_canvas *x, t_symbol *s, int ac, t_atom *av)
+{
+ int myindex1 = 0, i;
+ for (i = 0; i < ac; i++)
+ {
+ if (av[i].a_type == A_SYMBOL)
+ {
+ if (!strcmp(av[i].a_w.w_symbol->s_name, "_semi_"))
+ SETSEMI(&av[i]);
+ else if (!strcmp(av[i].a_w.w_symbol->s_name, "_comma_"))
+ SETCOMMA(&av[i]);
+ }
+ }
+ if (!canvas_findbuf)
+ canvas_findbuf = binbuf_new();
+ binbuf_clear(canvas_findbuf);
+ binbuf_add(canvas_findbuf, ac, av);
+ canvas_find_index1 = 0;
+ canvas_find_index2 = -1;
+ canvas_whichfind = x;
+ if (!canvas_dofind(x, &myindex1))
+ {
+ binbuf_print(canvas_findbuf);
+ post("... couldn't find");
+ }
+}
+
+static void canvas_find_again(t_canvas *x)
+{
+ int myindex1 = 0;
+ if (!canvas_findbuf || !canvas_whichfind)
+ return;
+ if (!canvas_dofind(canvas_whichfind, &myindex1))
+ {
+ binbuf_print(canvas_findbuf);
+ post("... couldn't find");
+ }
+}
+
+static void canvas_find_parent(t_canvas *x)
+{
+ if (x->gl_owner)
+ canvas_vis(glist_getcanvas(x->gl_owner), 1);
+}
+
+static int glist_dofinderror(t_glist *gl, void *error_object)
+{
+ t_gobj *g;
+ for (g = gl->gl_list; g; g = g->g_next)
+ {
+ if ((void *)g == error_object)
+ {
+ /* got it... now show it. */
+ glist_noselect(gl);
+ canvas_vis(glist_getcanvas(gl), 1);
+ canvas_editmode(glist_getcanvas(gl), 1.);
+ /* we can't just select here ala glist_select(gl, g); instead,
+ as in "find", set "selectme" for when "map" function is called. */
+ canvas_selectme = g;
+ return (1);
+ }
+ else if (g->g_pd == canvas_class)
+ {
+ if (glist_dofinderror((t_canvas *)g, error_object))
+ return (1);
+ }
+ }
+ return (0);
+}
+
+void canvas_finderror(void *error_object)
+{
+ t_canvas *x;
+ /* find all root canvases */
+ for (x = canvas_list; x; x = x->gl_next)
+ {
+ if (glist_dofinderror(x, error_object))
+ return;
+ }
+ post("... sorry, I couldn't find the source of that error.");
+}
+
+void canvas_stowconnections(t_canvas *x)
+{
+ t_gobj *selhead = 0, *seltail = 0, *nonhead = 0, *nontail = 0, *y, *y2;
+ t_linetraverser t;
+ t_outconnect *oc;
+ if (!x->gl_editor) return;
+ /* split list to "selected" and "unselected" parts */
+ for (y = x->gl_list; y; y = y2)
+ {
+ y2 = y->g_next;
+ if (glist_isselected(x, y))
+ {
+ if (seltail)
+ {
+ seltail->g_next = y;
+ seltail = y;
+ y->g_next = 0;
+ }
+ else
+ {
+ selhead = seltail = y;
+ seltail->g_next = 0;
+ }
+ }
+ else
+ {
+ if (nontail)
+ {
+ nontail->g_next = y;
+ nontail = y;
+ y->g_next = 0;
+ }
+ else
+ {
+ nonhead = nontail = y;
+ nontail->g_next = 0;
+ }
+ }
+ }
+ /* move the selected part to the end */
+ if (!nonhead) x->gl_list = selhead;
+ else x->gl_list = nonhead, nontail->g_next = selhead;
+
+ /* add connections to binbuf */
+ binbuf_clear(x->gl_editor->e_connectbuf);
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ {
+ int srcno = 0, sinkno = 0;
+ int s1 = glist_isselected(x, &t.tr_ob->ob_g);
+ int s2 = glist_isselected(x, &t.tr_ob2->ob_g);
+ if (s1 != s2)
+ {
+ for (y = x->gl_list; y && y != &t.tr_ob->ob_g; y = y->g_next)
+ srcno++;
+ for (y = x->gl_list; y && y != &t.tr_ob2->ob_g; y = y->g_next)
+ sinkno++;
+ binbuf_addv(x->gl_editor->e_connectbuf, "ssiiii;",
+ gensym("#X"), gensym("connect"),
+ srcno, t.tr_outno, sinkno, t.tr_inno);
+ }
+ }
+}
+
+void canvas_restoreconnections(t_canvas *x)
+{
+ pd_bind(&x->gl_pd, gensym("#X"));
+ binbuf_eval(x->gl_editor->e_connectbuf, 0, 0, 0);
+ pd_unbind(&x->gl_pd, gensym("#X"));
+}
+
+
+static t_binbuf *copy_binbuf;
+
+static void canvas_copy(t_canvas *x)
+{
+ t_gobj *y;
+ t_linetraverser t;
+ t_outconnect *oc;
+ if (!x->gl_editor || !x->gl_editor->e_selection)
+ return;
+ binbuf_clear(copy_binbuf);
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ if (glist_isselected(x, y))
+ gobj_save(y, copy_binbuf);
+ }
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ {
+ int srcno = 0, sinkno = 0;
+ if (glist_isselected(x, &t.tr_ob->ob_g)
+ && glist_isselected(x, &t.tr_ob2->ob_g))
+ {
+ for (y = x->gl_list; y && y != &t.tr_ob->ob_g; y = y->g_next)
+ if (glist_isselected(x, y)) srcno++;
+ for (y = x->gl_list; y && y != &t.tr_ob2->ob_g; y = y->g_next)
+ if (glist_isselected(x, y)) sinkno++;
+ binbuf_addv(copy_binbuf, "ssiiii;", gensym("#X"),
+ gensym("connect"), srcno, t.tr_outno, sinkno, t.tr_inno);
+ }
+ }
+}
+
+extern t_pd *newest;
+static void canvas_doclear(t_canvas *x)
+{
+ t_gobj *y, *y2;
+ int dspstate;
+
+ dspstate = canvas_suspend_dsp();
+ /* if text is selected, deselecting it might remake the
+ object. So we deselect it and hunt for a "new" object on
+ the glist to reselect. */
+ if (x->gl_editor->e_textedfor)
+ {
+ newest = 0;
+ glist_noselect(x);
+ if (newest)
+ {
+ for (y = x->gl_list; y; y = y->g_next)
+ if (&y->g_pd == newest) glist_select(x, y);
+ }
+ }
+ while (1) /* this is pretty wierd... should rewrite it */
+ {
+ for (y = x->gl_list; y; y = y2)
+ {
+ y2 = y->g_next;
+ if (glist_isselected(x, y))
+ {
+ glist_delete(x, y);
+#if 0
+ if (y2) post("cut 5 %x %x", y2, y2->g_next);
+ else post("cut 6");
+#endif
+ goto next;
+ }
+ }
+ goto restore;
+ next: ;
+ }
+restore:
+ canvas_resume_dsp(dspstate);
+ canvas_dirty(x, 1);
+}
+
+static void canvas_cut(t_canvas *x)
+{
+ canvas_copy(x);
+ canvas_doclear(x);
+}
+
+static int paste_onset;
+static t_canvas *paste_canvas;
+
+static void glist_donewloadbangs(t_glist *x)
+{
+ if (x->gl_editor)
+ {
+ t_selection *sel;
+ for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
+ if (pd_class(&sel->sel_what->g_pd) == canvas_class)
+ canvas_loadbang((t_canvas *)(&sel->sel_what->g_pd));
+ }
+}
+
+static void canvas_paste(t_canvas *x)
+{
+ t_gobj *newgobj, *last, *g2;
+ int dspstate = canvas_suspend_dsp(), nbox, count;
+
+ canvas_editmode(x, 1.);
+ glist_noselect(x);
+ for (g2 = x->gl_list, nbox = 0; g2; g2 = g2->g_next) nbox++;
+
+ paste_onset = nbox;
+ paste_canvas = x;
+
+ pd_bind(&x->gl_pd, gensym("#X"));
+ binbuf_eval(copy_binbuf, 0, 0, 0);
+ pd_unbind(&x->gl_pd, gensym("#X"));
+ for (g2 = x->gl_list, count = 0; g2; g2 = g2->g_next, count++)
+ if (count >= nbox)
+ glist_select(x, g2);
+ paste_canvas = 0;
+ canvas_resume_dsp(dspstate);
+ canvas_dirty(x, 1);
+ glist_donewloadbangs(x);
+}
+
+static void canvas_duplicate(t_canvas *x)
+{
+ if (x->gl_editor->e_onmotion == MA_NONE)
+ {
+ t_selection *y;
+ canvas_copy(x);
+ canvas_paste(x);
+ for (y = x->gl_editor->e_selection; y; y = y->sel_next)
+ gobj_displace(y->sel_what, x,
+ 10, 10);
+ canvas_dirty(x, 1);
+ }
+}
+
+static void canvas_selectall(t_canvas *x)
+{
+ t_gobj *y;
+ if (!x->gl_edit)
+ canvas_editmode(x, 1);
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ if (!glist_isselected(x, y))
+ glist_select(x, y);
+ }
+}
+
+static void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno,
+ t_floatarg fwhoin, t_floatarg finno)
+{
+ int whoout = fwhoout, outno = foutno, whoin = fwhoin, inno = finno;
+ t_gobj *src = 0, *sink = 0;
+ t_object *objsrc, *objsink;
+ t_outconnect *oc;
+ int nin = whoin, nout = whoout;
+ if (paste_canvas == x) whoout += paste_onset, whoin += paste_onset;
+ for (src = x->gl_list; whoout; src = src->g_next, whoout--)
+ if (!src->g_next) goto bad; /* bug fix thanks to Hannes */
+ for (sink = x->gl_list; whoin; sink = sink->g_next, whoin--)
+ if (!sink->g_next) goto bad;
+ if (!(objsrc = pd_checkobject(&src->g_pd)) ||
+ !(objsink = pd_checkobject(&sink->g_pd)))
+ goto bad;
+ if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad;
+ if (glist_isvisible(x))
+ {
+ sys_vgui(".x%x.c create line %d %d %d %d -tags l%x\n",
+ glist_getcanvas(x), 0, 0, 0, 0, oc);
+ canvas_fixlinesfor(x, objsrc);
+ }
+ return;
+
+bad:
+ post("%s %d %d %d %d (%s->%s) connection failed",
+ x->gl_name->s_name, nout, outno, nin, inno,
+ (src? class_getname(pd_class(&src->g_pd)) : "???"),
+ (sink? class_getname(pd_class(&sink->g_pd)) : "???"));
+}
+
+#define XTOLERANCE 4
+#define YTOLERANCE 3
+#define NHIST 15
+
+ /* LATER might have to speed this up */
+static void canvas_tidy(t_canvas *x)
+{
+ t_gobj *y, *y2, *y3;
+ int ax1, ay1, ax2, ay2, bx1, by1, bx2, by2;
+ int histogram[NHIST], *ip, i, besthist, bestdist;
+ /* tidy horizontally */
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
+
+ for (y2 = x->gl_list; y2; y2 = y2->g_next)
+ {
+ gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
+ if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE &&
+ bx1 < ax1)
+ goto nothorizhead;
+ }
+
+ for (y2 = x->gl_list; y2; y2 = y2->g_next)
+ {
+ gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
+ if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE
+ && by1 != ay1)
+ gobj_displace(y2, x, 0, ay1-by1);
+ }
+ nothorizhead: ;
+ }
+ /* tidy vertically. First guess the user's favorite vertical spacing */
+ for (i = NHIST, ip = histogram; i--; ip++) *ip = 0;
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
+ for (y2 = x->gl_list; y2; y2 = y2->g_next)
+ {
+ gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
+ if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE)
+ {
+ int distance = by1-ay2;
+ if (distance >= 0 && distance < NHIST)
+ histogram[distance]++;
+ }
+ }
+ }
+ for (i = 1, besthist = 0, bestdist = 4, ip = histogram + 1;
+ i < (NHIST-1); i++, ip++)
+ {
+ int hit = ip[-1] + 2 * ip[0] + ip[1];
+ if (hit > besthist)
+ {
+ besthist = hit;
+ bestdist = i;
+ }
+ }
+ post("best vertical distance %d", bestdist);
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ int keep = 1;
+ gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
+ for (y2 = x->gl_list; y2; y2 = y2->g_next)
+ {
+ gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
+ if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
+ ay1 >= by2 - 10 && ay1 < by2 + NHIST)
+ goto nothead;
+ }
+ while (keep)
+ {
+ keep = 0;
+ for (y2 = x->gl_list; y2; y2 = y2->g_next)
+ {
+ gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
+ if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
+ by1 > ay1 && by1 < ay2 + NHIST)
+ {
+ int vmove = ay2 + bestdist - by1;
+ gobj_displace(y2, x, ax1-bx1, vmove);
+ ay1 = by1 + vmove;
+ ay2 = by2 + vmove;
+ keep = 1;
+ break;
+ }
+ }
+ }
+ nothead: ;
+ }
+ canvas_dirty(x, 1);
+}
+
+static void canvas_texteditor(t_canvas *x)
+{
+ t_rtext *foo;
+ char *buf;
+ int bufsize;
+ if (foo = x->gl_editor->e_textedfor)
+ rtext_gettext(foo, &buf, &bufsize);
+ else buf = "", bufsize = 0;
+ sys_vgui("pdtk_pd_texteditor {%.*s}\n", bufsize, buf);
+
+}
+
+
+void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av)
+{
+ /* canvas_editing can be zero; canvas_key checks for that */
+ canvas_key(canvas_editing, s, ac, av);
+}
+
+void canvas_editmode(t_canvas *x, t_floatarg fyesplease)
+{
+ int yesplease = fyesplease;
+ if (yesplease && x->gl_edit)
+ return;
+ if (x->gl_edit = !x->gl_edit)
+ canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
+ else
+ {
+ glist_noselect(x);
+ canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
+ }
+ sys_vgui("pdtk_canvas_editval .x%x %d\n",
+ glist_getcanvas(x), x->gl_edit);
+ if (yesplease) canvas_dirty(x, 1);
+}
+
+static void canvas_protectmode(t_canvas *x, t_floatarg fyesplease)
+{
+ int yesplease = fyesplease;
+
+ if (yesplease && x->gl_protect)
+ return;
+ x->gl_protect = !x->gl_protect;
+ sys_vgui("pdtk_canvas_protectval .x%x %d\n",
+ glist_getcanvas(x), x->gl_protect);
+}
+
+ /* called by canvas_font below */
+static void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize,
+ t_floatarg yresize)
+{
+ t_gobj *y;
+ x->gl_font = font;
+ if (xresize != 1 || yresize != 1)
+ {
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ int x1, x2, y1, y2, nx1, ny1;
+ gobj_getrect(y, x, &x1, &y1, &x2, &y2);
+ nx1 = x1 * xresize + 0.5;
+ ny1 = y1 * yresize + 0.5;
+ gobj_displace(y, x, nx1-x1, ny1-y1);
+ }
+ }
+ if (glist_isvisible(x))
+ glist_redraw(x);
+ for (y = x->gl_list; y; y = y->g_next)
+ if (pd_class(&y->g_pd) == canvas_class
+ && !canvas_isabstraction((t_canvas *)y))
+ canvas_dofont((t_canvas *)y, font, xresize, yresize);
+}
+
+ /* canvas_menufont calls up a TK dialog which calls this back */
+static void canvas_font(t_canvas *x, t_floatarg font, t_floatarg resize,
+ t_floatarg whichresize)
+{
+ float realresize, realresx = 1, realresy = 1;
+ t_canvas *x2 = canvas_getrootfor(x);
+ if (!resize) realresize = 1;
+ else
+ {
+ if (resize < 20) resize = 20;
+ if (resize > 500) resize = 500;
+ realresize = resize * 0.01;
+ }
+ if (whichresize != 3) realresx = realresize;
+ if (whichresize != 2) realresy = realresize;
+ canvas_dofont(x2, font, realresx, realresy);
+ sys_defaultfont = font;
+}
+
+static t_glist *canvas_last_glist;
+static int canvas_last_glist_x, canvas_last_glist_y;
+
+void glist_getnextxy(t_glist *gl, int *xpix, int *ypix)
+{
+ if (canvas_last_glist == gl)
+ *xpix = canvas_last_glist_x, *ypix = canvas_last_glist_y;
+ else *xpix = *ypix = 40;
+}
+
+static void glist_setlastxy(t_glist *gl, int xval, int yval)
+{
+ canvas_last_glist = gl;
+ canvas_last_glist_x = xval;
+ canvas_last_glist_y = yval;
+}
+
+
+void g_editor_setup(void)
+{
+/* ------------------------ events ---------------------------------- */
+ class_addmethod(canvas_class, (t_method)canvas_mousedown, gensym("mouse"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_mouseup, gensym("mouseup"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_key, gensym("key"),
+ A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_motion, gensym("motion"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+
+/* ------------------------ menu actions ---------------------------- */
+ class_addmethod(canvas_class, (t_method)canvas_menuclose,
+ gensym("menuclose"), A_DEFFLOAT, 0);
+ class_addmethod(canvas_class, (t_method)canvas_cut,
+ gensym("cut"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_copy,
+ gensym("copy"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_paste,
+ gensym("paste"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_duplicate,
+ gensym("duplicate"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_selectall,
+ gensym("selectall"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_tidy,
+ gensym("tidy"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_texteditor,
+ gensym("texteditor"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_editmode,
+ gensym("editmode"), A_DEFFLOAT, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_protectmode,
+ gensym("protectmode"), A_DEFFLOAT, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_print,
+ gensym("print"), A_SYMBOL, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_menufont,
+ gensym("menufont"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_font,
+ gensym("font"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_find,
+ gensym("find"), A_GIMME, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_find_again,
+ gensym("findagain"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_find_parent,
+ gensym("findparent"), A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_done_popup,
+ gensym("done-popup"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_donecanvasdialog,
+ gensym("donecanvasdialog"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(canvas_class, (t_method)glist_arraydialog,
+ gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+
+/* -------------- connect method used in reading files ------------------ */
+ class_addmethod(canvas_class, (t_method)canvas_connect,
+ gensym("connect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+
+/* -------------- copy buffer ------------------ */
+ copy_binbuf = binbuf_new();
+}
diff --git a/pd/src/g_graph.c b/pd/src/g_graph.c
new file mode 100644
index 00000000..65a5d056
--- /dev/null
+++ b/pd/src/g_graph.c
@@ -0,0 +1,1119 @@
+/* Copyright (c) 1997-2001 Miller Puckette and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* This file deals with the behavior of glists as either "text objects" or
+"graphs" inside another glist. LATER move the inlet/outlet code of g_canvas.c
+to this file... */
+
+#include <stdlib.h>
+#include "m_pd.h"
+#include "t_tk.h"
+#include "g_canvas.h"
+#include <stdio.h>
+#include <string.h>
+
+/* ---------------------- forward definitions ----------------- */
+
+static void graph_vis(t_gobj *gr, t_glist *unused_glist, int vis);
+static void graph_graphrect(t_gobj *z, t_glist *glist,
+ int *xp1, int *yp1, int *xp2, int *yp2);
+static void graph_getrect(t_gobj *z, t_glist *glist,
+ int *xp1, int *yp1, int *xp2, int *yp2);
+
+/* -------------------- maintaining the list -------------------- */
+
+void glist_add(t_glist *x, t_gobj *y)
+{
+ y->g_next = 0;
+ if (!x->gl_list) x->gl_list = y;
+ else
+ {
+ t_gobj *y2;
+ for (y2 = x->gl_list; y2->g_next; y2 = y2->g_next);
+ y2->g_next = y;
+ }
+ if (glist_isvisible(x))
+ gobj_vis(y, x, 1);
+ if (class_isdrawcommand(y->g_pd))
+ canvas_redrawallfortemplate(glist_getcanvas(x));
+}
+
+ /* this is to protect against a hairy problem in which deleting
+ a sub-canvas might delete an inlet on a box, after the box had
+ been invisible-ized, so that we have to protect against redrawing it! */
+int canvas_setdeleting(t_canvas *x, int flag)
+{
+ int ret = x->gl_isdeleting;
+ x->gl_isdeleting = flag;
+ return (ret);
+}
+
+ /* delete an object from a glist and free it */
+void glist_delete(t_glist *x, t_gobj *y)
+{
+ t_gobj *g;
+ t_gotfn chkdsp = zgetfn(&y->g_pd, gensym("dsp"));
+ t_canvas *canvas = glist_getcanvas(x);
+ int drawcommand = class_isdrawcommand(y->g_pd);
+ int wasdeleting;
+
+ wasdeleting = canvas_setdeleting(canvas, 1);
+ /* LATER decide whether all visible glists must have an editor? */
+ if (glist_isvisible(canvas) && x->gl_editor)
+ {
+ if (x->gl_editor->e_grab == y) x->gl_editor->e_grab = 0;
+ if (glist_isselected(x, y)) glist_deselect(x, y);
+
+ /* HACK -- we had phantom outlets not getting erased on the
+ screen because the canvas_setdeleting() mechanism is too
+ crude. LATER carefully set up rules for when the rtexts
+ should exist, so that they stay around until all the
+ steps of becoming invisible are done. In the meantime, just
+ zap the inlets and outlets here... */
+ if (pd_class(&y->g_pd) == canvas_class)
+ {
+ t_glist *gl = (t_glist *)y;
+ if (gl->gl_isgraph)
+ {
+ char tag[80];
+ sprintf(tag, "graph%x", (int)gl);
+ glist_eraseiofor(x, &gl->gl_obj, tag);
+ }
+ else
+ {
+ text_eraseborder(&gl->gl_obj, x,
+ rtext_gettag(glist_findrtext(x, &gl->gl_obj)));
+ }
+ }
+ }
+ gobj_delete(y, x);
+ if (glist_isvisible(canvas)) gobj_vis(y, x, 0);
+ if (x->gl_list == y) x->gl_list = y->g_next;
+ else for (g = x->gl_list; g; g = g->g_next)
+ if (g->g_next == y)
+ {
+ g->g_next = y->g_next;
+ break;
+ }
+ pd_free(&y->g_pd);
+ if (chkdsp) canvas_update_dsp();
+ if (drawcommand) canvas_redrawallfortemplate(canvas);
+ canvas_setdeleting(canvas, wasdeleting);
+ x->gl_valid = ++glist_valid;
+}
+
+ /* remove every object from a glist. Experimental. */
+void glist_clear(t_glist *x)
+{
+ t_gobj *y, *y2;
+ int dspstate = canvas_suspend_dsp();
+ while (y = x->gl_list)
+ glist_delete(x, y);
+ canvas_resume_dsp(dspstate);
+}
+
+void glist_retext(t_glist *glist, t_text *y)
+{
+ t_canvas *c = glist_getcanvas(glist);
+ /* check that we have built rtexts yet. LATER need a better test. */
+ if (glist->gl_editor && glist->gl_editor->e_rtext)
+ {
+ t_rtext *rt = glist_findrtext(glist, y);
+ if (rt)
+ rtext_retext(rt);
+ }
+}
+
+void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn,
+ t_glistkeyfn keyfn, int xpos, int ypos)
+{
+ t_glist *x2 = glist_getcanvas(x);
+ x2->gl_editor->e_onmotion = MA_PASSOUT;
+ x2->gl_editor->e_grab = y;
+ x2->gl_editor->e_motionfn = motionfn;
+ x2->gl_editor->e_keyfn = keyfn;
+ x2->gl_editor->e_xwas = xpos;
+ x2->gl_editor->e_ywas = ypos;
+}
+
+t_canvas *glist_getcanvas(t_glist *x)
+{
+ while (x->gl_owner && !x->gl_havewindow && x->gl_isgraph)
+ x = x->gl_owner;
+ return((t_canvas *)x);
+}
+
+static float gobj_getxforsort(t_gobj *g)
+{
+ if (pd_class(&g->g_pd) == scalar_class)
+ {
+ float x1, y1;
+ scalar_getbasexy((t_scalar *)g, &x1, &y1);
+ return(x1);
+ }
+ else return (0);
+}
+
+static t_gobj *glist_merge(t_glist *x, t_gobj *g1, t_gobj *g2)
+{
+ t_gobj *g = 0, *g9 = 0;
+ float f1 = 0, f2 = 0;
+ if (g1)
+ f1 = gobj_getxforsort(g1);
+ if (g2)
+ f2 = gobj_getxforsort(g2);
+ while (1)
+ {
+ if (g1)
+ {
+ if (g2)
+ {
+ if (f1 <= f2)
+ goto put1;
+ else goto put2;
+ }
+ else goto put1;
+ }
+ else if (g2)
+ goto put2;
+ else break;
+ put1:
+ if (g9)
+ g9->g_next = g1, g9 = g1;
+ else g9 = g = g1;
+ if (g1 = g1->g_next)
+ f1 = gobj_getxforsort(g1);
+ g9->g_next = 0;
+ continue;
+ put2:
+ if (g9)
+ g9->g_next = g2, g9 = g2;
+ else g9 = g = g2;
+ if (g2 = g2->g_next)
+ f2 = gobj_getxforsort(g2);
+ g9->g_next = 0;
+ continue;
+ }
+ return (g);
+}
+
+static t_gobj *glist_dosort(t_glist *x,
+ t_gobj *g, int nitems)
+{
+ if (nitems < 2)
+ return (g);
+ else
+ {
+ int n1 = nitems/2, n2 = nitems - n1, i;
+ t_gobj *g2, *g3;
+ for (g2 = g, i = n1-1; i--; g2 = g2->g_next)
+ ;
+ g3 = g2->g_next;
+ g2->g_next = 0;
+ g = glist_dosort(x, g, n1);
+ g3 = glist_dosort(x, g3, n2);
+ return (glist_merge(x, g, g3));
+ }
+}
+
+void glist_sort(t_glist *x)
+{
+ int nitems = 0, foo = 0;
+ float lastx = -1e37;
+ t_gobj *g;
+ for (g = x->gl_list; g; g = g->g_next)
+ {
+ float x1 = gobj_getxforsort(g);
+ if (x1 < lastx)
+ foo = 1;
+ lastx = x1;
+ nitems++;
+ }
+ if (foo)
+ x->gl_list = glist_dosort(x, x->gl_list, nitems);
+}
+
+void glist_cleanup(t_glist *x)
+{
+ freebytes(x->gl_xlabel, x->gl_nxlabels * sizeof(*(x->gl_xlabel)));
+ freebytes(x->gl_ylabel, x->gl_nylabels * sizeof(*(x->gl_ylabel)));
+ gstub_cutoff(x->gl_stub);
+}
+
+void glist_free(t_glist *x)
+{
+ glist_cleanup(x);
+ freebytes(x, sizeof(*x));
+}
+
+/* --------------- inlets and outlets ----------- */
+
+
+t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *s)
+{
+ t_inlet *ip = inlet_new(&x->gl_obj, who, s, 0);
+ if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
+ {
+ gobj_vis(&x->gl_gobj, x->gl_owner, 0);
+ gobj_vis(&x->gl_gobj, x->gl_owner, 1);
+ canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
+ }
+ if (!x->gl_loading) canvas_resortinlets(x);
+ return (ip);
+}
+
+void canvas_deletelinesforio(t_canvas *x, t_text *text,
+ t_inlet *inp, t_outlet *outp);
+
+void canvas_rminlet(t_canvas *x, t_inlet *ip)
+{
+ t_canvas *owner = x->gl_owner;
+ int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting)
+ && glist_istoplevel(owner));
+
+ if (owner) canvas_deletelinesforio(owner, &x->gl_obj, ip, 0);
+ if (redraw)
+ gobj_vis(&x->gl_gobj, x->gl_owner, 0);
+ inlet_free(ip);
+ if (redraw)
+ {
+ gobj_vis(&x->gl_gobj, x->gl_owner, 1);
+ canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
+ }
+}
+
+extern t_inlet *vinlet_getit(t_pd *x);
+extern void obj_moveinletfirst(t_object *x, t_inlet *i);
+
+void canvas_resortinlets(t_canvas *x)
+{
+ int ninlets = 0, i, j, xmax;
+ t_gobj *y, **vec, **vp, **maxp;
+
+ for (ninlets = 0, y = x->gl_list; y; y = y->g_next)
+ if (pd_class(&y->g_pd) == vinlet_class) ninlets++;
+
+ if (ninlets < 2) return;
+
+ vec = (t_gobj **)getbytes(ninlets * sizeof(*vec));
+
+ for (y = x->gl_list, vp = vec; y; y = y->g_next)
+ if (pd_class(&y->g_pd) == vinlet_class) *vp++ = y;
+
+ for (i = ninlets; i--;)
+ {
+ t_inlet *ip;
+ for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = ninlets;
+ j--; vp++)
+ {
+ int x1, y1, x2, y2;
+ t_gobj *g = *vp;
+ if (!g) continue;
+ gobj_getrect(g, x, &x1, &y1, &x2, &y2);
+ if (x1 > xmax) xmax = x1, maxp = vp;
+ }
+ if (!maxp) break;
+ y = *maxp;
+ *maxp = 0;
+ ip = vinlet_getit(&y->g_pd);
+
+ obj_moveinletfirst(&x->gl_obj, ip);
+ }
+ freebytes(vec, ninlets * sizeof(*vec));
+ if (x->gl_owner && glist_isvisible(x->gl_owner))
+ canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
+}
+
+t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *s)
+{
+ t_outlet *op = outlet_new(&x->gl_obj, s);
+ if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
+ {
+ gobj_vis(&x->gl_gobj, x->gl_owner, 0);
+ gobj_vis(&x->gl_gobj, x->gl_owner, 1);
+ canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
+ }
+ if (!x->gl_loading) canvas_resortoutlets(x);
+ return (op);
+}
+
+void canvas_rmoutlet(t_canvas *x, t_outlet *op)
+{
+ t_canvas *owner = x->gl_owner;
+ int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting)
+ && glist_istoplevel(owner));
+
+ if (owner) canvas_deletelinesforio(owner, &x->gl_obj, 0, op);
+ if (redraw)
+ gobj_vis(&x->gl_gobj, x->gl_owner, 0);
+
+ outlet_free(op);
+ if (redraw)
+ {
+ gobj_vis(&x->gl_gobj, x->gl_owner, 1);
+ canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
+ }
+}
+
+extern t_outlet *voutlet_getit(t_pd *x);
+extern void obj_moveoutletfirst(t_object *x, t_outlet *i);
+
+void canvas_resortoutlets(t_canvas *x)
+{
+ int noutlets = 0, i, j, xmax;
+ t_gobj *y, **vec, **vp, **maxp;
+
+ for (noutlets = 0, y = x->gl_list; y; y = y->g_next)
+ if (pd_class(&y->g_pd) == voutlet_class) noutlets++;
+
+ if (noutlets < 2) return;
+
+ vec = (t_gobj **)getbytes(noutlets * sizeof(*vec));
+
+ for (y = x->gl_list, vp = vec; y; y = y->g_next)
+ if (pd_class(&y->g_pd) == voutlet_class) *vp++ = y;
+
+ for (i = noutlets; i--;)
+ {
+ t_outlet *ip;
+ for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = noutlets;
+ j--; vp++)
+ {
+ int x1, y1, x2, y2;
+ t_gobj *g = *vp;
+ if (!g) continue;
+ gobj_getrect(g, x, &x1, &y1, &x2, &y2);
+ if (x1 > xmax) xmax = x1, maxp = vp;
+ }
+ if (!maxp) break;
+ y = *maxp;
+ *maxp = 0;
+ ip = voutlet_getit(&y->g_pd);
+
+ obj_moveoutletfirst(&x->gl_obj, ip);
+ }
+ freebytes(vec, noutlets * sizeof(*vec));
+ if (x->gl_owner && glist_isvisible(x->gl_owner))
+ canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
+}
+
+/* ----------calculating coordinates and controlling appearance --------- */
+
+
+static void graph_bounds(t_glist *x, t_floatarg x1, t_floatarg y1,
+ t_floatarg x2, t_floatarg y2)
+{
+ x->gl_x1 = x1;
+ x->gl_x2 = x2;
+ x->gl_y1 = y1;
+ x->gl_y2 = y2;
+ if (x->gl_x2 == x->gl_x1 ||
+ x->gl_y2 == x->gl_y1)
+ {
+ error("graph: empty bounds rectangle");
+ x1 = y1 = 0;
+ x2 = y2 = 1;
+ }
+ glist_redraw(x);
+}
+
+static void graph_xticks(t_glist *x,
+ t_floatarg point, t_floatarg inc, t_floatarg f)
+{
+ x->gl_xtick.k_point = point;
+ x->gl_xtick.k_inc = inc;
+ x->gl_xtick.k_lperb = f;
+ glist_redraw(x);
+}
+
+static void graph_yticks(t_glist *x,
+ t_floatarg point, t_floatarg inc, t_floatarg f)
+{
+ x->gl_ytick.k_point = point;
+ x->gl_ytick.k_inc = inc;
+ x->gl_ytick.k_lperb = f;
+ glist_redraw(x);
+}
+
+static void graph_xlabel(t_glist *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int i;
+ if (argc < 1) error("graph_xlabel: no y value given");
+ else
+ {
+ x->gl_xlabely = atom_getfloat(argv);
+ argv++; argc--;
+ x->gl_xlabel = (t_symbol **)t_resizebytes(x->gl_xlabel,
+ x->gl_nxlabels * sizeof (t_symbol *), argc * sizeof (t_symbol *));
+ x->gl_nxlabels = argc;
+ for (i = 0; i < argc; i++) x->gl_xlabel[i] = atom_gensym(&argv[i]);
+ }
+ glist_redraw(x);
+}
+
+static void graph_ylabel(t_glist *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int i;
+ if (argc < 1) error("graph_ylabel: no x value given");
+ else
+ {
+ x->gl_ylabelx = atom_getfloat(argv);
+ argv++; argc--;
+ x->gl_ylabel = (t_symbol **)t_resizebytes(x->gl_ylabel,
+ x->gl_nylabels * sizeof (t_symbol *), argc * sizeof (t_symbol *));
+ x->gl_nylabels = argc;
+ for (i = 0; i < argc; i++) x->gl_ylabel[i] = atom_gensym(&argv[i]);
+ }
+ glist_redraw(x);
+}
+
+/****** routines to convert pixels to X or Y value and vice versa ******/
+
+ /* convert an x pixel value to an x coordinate value */
+float glist_pixelstox(t_glist *x, float xpix)
+{
+ /* if we appear as a text box on parent, our range in our
+ coordinates (x1, etc.) specifies the coordinate range
+ of a one-pixel square at top left of the window. */
+ if (!x->gl_isgraph)
+ return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * xpix);
+
+ /* if we're a graph when shown on parent, but own our own
+ window right now, our range in our coordinates (x1, etc.) is spread
+ over the visible window size, given by screenx1, etc. */
+ else if (x->gl_isgraph && x->gl_havewindow)
+ return (x->gl_x1 + (x->gl_x2 - x->gl_x1) *
+ (xpix) / (x->gl_screenx2 - x->gl_screenx1));
+
+ /* otherwise, we appear in a graph within a parent glist,
+ so get our screen rectangle on parent and transform. */
+ else
+ {
+ int x1, y1, x2, y2;
+ if (!x->gl_owner)
+ bug("glist_pixelstox");
+ graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
+ return (x->gl_x1 + (x->gl_x2 - x->gl_x1) *
+ (xpix - x1) / (x2 - x1));
+ }
+}
+
+float glist_pixelstoy(t_glist *x, float ypix)
+{
+ if (!x->gl_isgraph)
+ return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * ypix);
+ else if (x->gl_isgraph && x->gl_havewindow)
+ return (x->gl_y1 + (x->gl_y2 - x->gl_y1) *
+ (ypix) / (x->gl_screeny2 - x->gl_screeny1));
+ else
+ {
+ int x1, y1, x2, y2;
+ if (!x->gl_owner)
+ bug("glist_pixelstox");
+ graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
+ return (x->gl_y1 + (x->gl_y2 - x->gl_y1) *
+ (ypix - y1) / (y2 - y1));
+ }
+}
+
+ /* convert an x coordinate value to an x pixel location in window */
+float glist_xtopixels(t_glist *x, float xval)
+{
+ if (!x->gl_isgraph)
+ return ((xval - x->gl_x1) / (x->gl_x2 - x->gl_x1));
+ else if (x->gl_isgraph && x->gl_havewindow)
+ return (x->gl_screenx2 - x->gl_screenx1) *
+ (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1);
+ else
+ {
+ int x1, y1, x2, y2;
+ if (!x->gl_owner)
+ bug("glist_pixelstox");
+ graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
+ return (x1 + (x2 - x1) * (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1));
+ }
+}
+
+float glist_ytopixels(t_glist *x, float yval)
+{
+ if (!x->gl_isgraph)
+ return ((yval - x->gl_y1) / (x->gl_y2 - x->gl_y1));
+ else if (x->gl_isgraph && x->gl_havewindow)
+ return (x->gl_screeny2 - x->gl_screeny1) *
+ (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1);
+ else
+ {
+ int x1, y1, x2, y2;
+ if (!x->gl_owner)
+ bug("glist_pixelstox");
+ graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);
+ return (y1 + (y2 - y1) * (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1));
+ }
+}
+
+ /* convert an X screen distance to an X coordinate increment.
+ This is terribly inefficient;
+ but probably not a big enough CPU hog to warrant optimizing. */
+float glist_dpixtodx(t_glist *x, float dxpix)
+{
+ return (dxpix * (glist_pixelstox(x, 1) - glist_pixelstox(x, 0)));
+}
+
+float glist_dpixtody(t_glist *x, float dypix)
+{
+ return (dypix * (glist_pixelstoy(x, 1) - glist_pixelstoy(x, 0)));
+}
+
+ /* get the window location in pixels of a "text" object. The
+ object's x and y positions are in pixels when the glist they're
+ in is toplevel. If it's not, we convert to pixels on the parent
+ window. */
+int text_xpix(t_text *x, t_glist *glist)
+{
+ if (glist->gl_havewindow || !glist->gl_isgraph)
+ return (x->te_xpix);
+ else return (glist_xtopixels(glist,
+ glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) *
+ x->te_xpix / (glist->gl_screenx2 - glist->gl_screenx1)));
+}
+
+int text_ypix(t_text *x, t_glist *glist)
+{
+ if (glist->gl_havewindow || !glist->gl_isgraph)
+ return (x->te_ypix);
+ else return (glist_ytopixels(glist,
+ glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) *
+ x->te_ypix / (glist->gl_screeny2 - glist->gl_screeny1)));
+}
+
+ /* redraw all the items in a glist. We construe this to mean
+ redrawing in its own window and on parent, as needed in each case.
+ This is too conservative -- for instance, when you draw an "open"
+ rectangle on the parent, you shouldn't have to redraw the window! */
+void glist_redraw(t_glist *x)
+{
+ if (glist_isvisible(x))
+ {
+ /* LATER fix the graph_vis() code to handle both cases */
+ if (glist_istoplevel(x))
+ {
+ t_gobj *g;
+ t_linetraverser t;
+ t_outconnect *oc;
+ for (g = x->gl_list; g; g = g->g_next)
+ {
+ gobj_vis(g, x, 0);
+ gobj_vis(g, x, 1);
+ }
+ /* redraw all the lines */
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ sys_vgui(".x%x.c coords l%x %d %d %d %d\n",
+ glist_getcanvas(x), oc,
+ t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2);
+ }
+ if (x->gl_owner)
+ {
+ graph_vis(&x->gl_gobj, x->gl_owner, 0);
+ graph_vis(&x->gl_gobj, x->gl_owner, 1);
+ }
+ }
+}
+
+t_class *graph_class;
+
+/* --------------------------- widget behavior ------------------- */
+
+extern t_widgetbehavior text_widgetbehavior;
+
+ /* Note that some code in here would also be useful for drawing
+ graph decorations in toplevels... */
+static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)
+{
+ t_glist *x = (t_glist *)gr;
+ char tag[50];
+ t_gobj *g;
+ int x1, y1, x2, y2;
+ /* ordinary subpatches: just act like a text object */
+ if (!x->gl_isgraph)
+ {
+ text_widgetbehavior.w_visfn(gr, parent_glist, vis);
+ return;
+ }
+
+ if (vis)
+ rtext_new(parent_glist, &x->gl_obj,
+ parent_glist->gl_editor->e_rtext,
+ canvas_showtext(x));
+ graph_getrect(gr, parent_glist, &x1, &y1, &x2, &y2);
+ if (!vis)
+ rtext_free(glist_findrtext(parent_glist, &x->gl_obj));
+
+ sprintf(tag, "graph%x", (int)x);
+ if (vis)
+ glist_drawiofor(parent_glist, &x->gl_obj, 1,
+ tag, x1, y1, x2, y2);
+ else glist_eraseiofor(parent_glist, &x->gl_obj, tag);
+ /* if we look like a graph but have been moved to a toplevel,
+ just show the bounding rectangle */
+ if (x->gl_havewindow)
+ {
+ if (vis)
+ {
+ sys_vgui(".x%x.c create polygon\
+ %d %d %d %d %d %d %d %d %d %d -tags %s -fill #c0c0c0\n",
+ glist_getcanvas(x->gl_owner),
+ x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
+ }
+ else
+ {
+ sys_vgui(".x%x.c delete %s\n",
+ glist_getcanvas(x->gl_owner), tag);
+ }
+ return;
+ }
+ /* otherwise draw (or erase) us as a graph inside another glist. */
+ if (vis)
+ {
+ int i;
+ float f;
+
+ /* draw a rectangle around the graph */
+ sys_vgui(".x%x.c create line\
+ %d %d %d %d %d %d %d %d %d %d -tags %s\n",
+ glist_getcanvas(x->gl_owner),
+ x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);
+
+ /* draw ticks on horizontal borders. If lperb field is
+ zero, this is disabled. */
+ if (x->gl_xtick.k_lperb)
+ {
+ float upix, lpix;
+ if (y2 < y1)
+ upix = y1, lpix = y2;
+ else upix = y2, lpix = y1;
+ for (i = 0, f = x->gl_xtick.k_point;
+ f < 0.99 * x->gl_x2 + 0.01*x->gl_x1; i++,
+ f += x->gl_xtick.k_inc)
+ {
+ int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4);
+ sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
+ glist_getcanvas(x->gl_owner),
+ (int)glist_xtopixels(x, f), (int)upix,
+ (int)glist_xtopixels(x, f), (int)upix - tickpix, tag);
+ sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
+ glist_getcanvas(x->gl_owner),
+ (int)glist_xtopixels(x, f), (int)lpix,
+ (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag);
+ }
+ for (i = 1, f = x->gl_xtick.k_point - x->gl_xtick.k_inc;
+ f > 0.99 * x->gl_x1 + 0.01*x->gl_x2;
+ i++, f -= x->gl_xtick.k_inc)
+ {
+ int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4);
+ sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
+ glist_getcanvas(x->gl_owner),
+ (int)glist_xtopixels(x, f), (int)upix,
+ (int)glist_xtopixels(x, f), (int)upix - tickpix, tag);
+ sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
+ glist_getcanvas(x->gl_owner),
+ (int)glist_xtopixels(x, f), (int)lpix,
+ (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag);
+ }
+ }
+
+ /* draw ticks in vertical borders*/
+ if (x->gl_ytick.k_lperb)
+ {
+ float ubound, lbound;
+ if (x->gl_y2 < x->gl_y1)
+ ubound = x->gl_y1, lbound = x->gl_y2;
+ else ubound = x->gl_y2, lbound = x->gl_y1;
+ for (i = 0, f = x->gl_ytick.k_point;
+ f < 0.99 * ubound + 0.01 * lbound;
+ i++, f += x->gl_ytick.k_inc)
+ {
+ int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4);
+ sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
+ glist_getcanvas(x->gl_owner),
+ x1, (int)glist_ytopixels(x, f),
+ x1 + tickpix, (int)glist_ytopixels(x, f), tag);
+ sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
+ glist_getcanvas(x->gl_owner),
+ x2, (int)glist_ytopixels(x, f),
+ x2 - tickpix, (int)glist_ytopixels(x, f), tag);
+ }
+ for (i = 1, f = x->gl_ytick.k_point - x->gl_ytick.k_inc;
+ f > 0.99 * lbound + 0.01 * ubound;
+ i++, f -= x->gl_ytick.k_inc)
+ {
+ int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4);
+ sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
+ glist_getcanvas(x->gl_owner),
+ x1, (int)glist_ytopixels(x, f),
+ x1 + tickpix, (int)glist_ytopixels(x, f), tag);
+ sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n",
+ glist_getcanvas(x->gl_owner),
+ x2, (int)glist_ytopixels(x, f),
+ x2 - tickpix, (int)glist_ytopixels(x, f), tag);
+ }
+ }
+ /* draw x labels */
+ for (i = 0; i < x->gl_nxlabels; i++)
+ sys_vgui(".x%x.c create text\
+ %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n",
+ glist_getcanvas(x),
+ (int)glist_xtopixels(x, atof(x->gl_xlabel[i]->s_name)),
+ (int)glist_ytopixels(x, x->gl_xlabely), x->gl_xlabel[i]->s_name,
+ glist_getfont(x), tag);
+
+ /* draw y labels */
+ for (i = 0; i < x->gl_nylabels; i++)
+ sys_vgui(".x%x.c create text\
+ %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n",
+ glist_getcanvas(x),
+ (int)glist_xtopixels(x, x->gl_ylabelx),
+ (int)glist_ytopixels(x, atof(x->gl_ylabel[i]->s_name)),
+ x->gl_ylabel[i]->s_name,
+ glist_getfont(x), tag);
+
+ /* draw contents of graph as glist */
+ for (g = x->gl_list; g; g = g->g_next)
+ gobj_vis(g, x, 1);
+ }
+ else
+ {
+ sys_vgui(".x%x.c delete %s\n",
+ glist_getcanvas(x->gl_owner), tag);
+ for (g = x->gl_list; g; g = g->g_next)
+ gobj_vis(g, x, 0);
+ }
+}
+
+ /* get the graph's rectangle, not counting extra swelling for controls
+ to keep them inside the graph. This is the "logical" pixel size. */
+
+static void graph_graphrect(t_gobj *z, t_glist *glist,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_glist *x = (t_glist *)z;
+ int x1 = text_xpix(&x->gl_obj, glist);
+ int y1 = text_ypix(&x->gl_obj, glist);
+ int x2, y2;
+#if 0 /* this used to adjust graph size when it was in another graph;
+ now we just preserve the size. */
+ /* same logic here as in text_xpix(): */
+ if (glist->gl_havewindow)
+ {
+ x2 = x1 + x->gl_pixwidth;
+ y2 = y1 + x->gl_pixheight;
+ }
+ else
+ {
+ x2 = glist_xtopixels(glist,
+ glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) *
+ (x->gl_obj.te_xpix + x->gl_pixwidth) /
+ (glist->gl_screenx2 - glist->gl_screenx1));
+ y2 = glist_ytopixels(glist,
+ glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) *
+ (x->gl_obj.te_ypix + x->gl_pixheight) /
+ (glist->gl_screeny2 - glist->gl_screeny1));
+ }
+#endif
+ x2 = x1 + x->gl_pixwidth;
+ y2 = y1 + x->gl_pixheight;
+
+ *xp1 = x1;
+ *yp1 = y1;
+ *xp2 = x2;
+ *yp2 = y2;
+}
+
+ /* get the rectangle, enlarged to contain all the "contents" --
+ meaning their formal bounds rectangles. */
+static void graph_getrect(t_gobj *z, t_glist *glist,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ int x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff;
+ t_glist *x = (t_glist *)z;
+ if (x->gl_isgraph)
+ {
+ int hadwindow;
+ t_gobj *g;
+ t_text *ob;
+ int x21, y21, x22, y22;
+
+ graph_graphrect(z, glist, &x1, &y1, &x2, &y2);
+ if (canvas_showtext(x))
+ {
+ text_widgetbehavior.w_getrectfn(z, glist, &x21, &y21, &x22, &y22);
+ if (x22 > x2)
+ x2 = x22;
+ if (y22 > y2)
+ y2 = y22;
+ }
+ /* lie about whether we have our own window to affect gobj_getrect
+ calls below. (LATER add argument to gobj_getrect()?) */
+ hadwindow = x->gl_havewindow;
+ x->gl_havewindow = 0;
+ for (g = x->gl_list; g; g = g->g_next)
+ if ((!(ob = pd_checkobject(&g->g_pd))) || text_shouldvis(ob, x))
+ {
+ /* don't do this for arrays, just let them hang outsize the
+ box. */
+ if (pd_class(&g->g_pd) == garray_class)
+ continue;
+ gobj_getrect(g, x, &x21, &y21, &x22, &y22);
+ if (x22 > x2)
+ x2 = x22;
+ if (y22 > y2)
+ y2 = y22;
+ }
+ x->gl_havewindow = hadwindow;
+ }
+ else text_widgetbehavior.w_getrectfn(z, glist, &x1, &y1, &x2, &y2);
+ *xp1 = x1;
+ *yp1 = y1;
+ *xp2 = x2;
+ *yp2 = y2;
+}
+
+static void graph_displace(t_gobj *z, t_glist *glist, int dx, int dy)
+{
+ t_glist *x = (t_glist *)z;
+ if (!x->gl_isgraph)
+ text_widgetbehavior.w_displacefn(z, glist, dx, dy);
+ else
+ {
+ x->gl_obj.te_xpix += dx;
+ x->gl_obj.te_ypix += dy;
+ glist_redraw(x);
+ canvas_fixlinesfor(glist_getcanvas(glist), &x->gl_obj);
+ }
+}
+
+static void graph_select(t_gobj *z, t_glist *glist, int state)
+{
+ t_glist *x = (t_glist *)z;
+ if (!x->gl_isgraph)
+ text_widgetbehavior.w_selectfn(z, glist, state);
+ else
+ {
+ t_rtext *y = glist_findrtext(glist, &x->gl_obj);
+ if (canvas_showtext(x))
+ rtext_select(y, state);
+ sys_vgui(".x%x.c itemconfigure %sR -fill %s\n", glist,
+ rtext_gettag(y), (state? "blue" : "black"));
+ sys_vgui(".x%x.c itemconfigure graph%x -fill %s\n",
+ glist_getcanvas(glist), z, (state? "blue" : "black"));
+ }
+}
+
+static void graph_activate(t_gobj *z, t_glist *glist, int state)
+{
+ t_glist *x = (t_glist *)z;
+ if (canvas_showtext(x))
+ text_widgetbehavior.w_activatefn(z, glist, state);
+}
+
+#if 0
+static void graph_delete(t_gobj *z, t_glist *glist)
+{
+ t_glist *x = (t_glist *)z;
+ if (!x->gl_isgraph)
+ text_widgetbehavior.w_deletefn(z, glist);
+ else
+ {
+ t_gobj *y;
+ while (y = x->gl_list) glist_delete(x, y);
+#if 0 /* I think this was just wrong. */
+ if (glist_isvisible(x))
+ sys_vgui(".x%x.c delete graph%x\n", glist_getcanvas(glist), x);
+#endif
+ }
+}
+#endif
+
+static void graph_delete(t_gobj *z, t_glist *glist)
+{
+ t_glist *x = (t_glist *)z;
+ t_gobj *y;
+ text_widgetbehavior.w_deletefn(z, glist);
+ while (y = x->gl_list)
+ glist_delete(x, y);
+}
+
+static float graph_lastxpix, graph_lastypix;
+
+static void graph_motion(void *z, t_floatarg dx, t_floatarg dy)
+{
+ t_glist *x = (t_glist *)z;
+ float newxpix = graph_lastxpix + dx, newypix = graph_lastypix + dy;
+ t_garray *a = (t_garray *)(x->gl_list);
+ int oldx = 0.5 + glist_pixelstox(x, graph_lastxpix);
+ int newx = 0.5 + glist_pixelstox(x, newxpix);
+ t_float *vec;
+ int nelem, i;
+ float oldy = glist_pixelstoy(x, graph_lastypix);
+ float newy = glist_pixelstoy(x, newypix);
+ graph_lastxpix = newxpix;
+ graph_lastypix = newypix;
+ /* verify that the array is OK */
+ if (!a || pd_class((t_pd *)a) != garray_class)
+ return;
+ if (!garray_getfloatarray(a, &nelem, &vec))
+ return;
+ if (oldx < 0) oldx = 0;
+ if (oldx >= nelem)
+ oldx = nelem - 1;
+ if (newx < 0) newx = 0;
+ if (newx >= nelem)
+ newx = nelem - 1;
+ if (oldx < newx - 1)
+ {
+ for (i = oldx + 1; i <= newx; i++)
+ vec[i] = newy + (oldy - newy) *
+ ((float)(newx - i))/(float)(newx - oldx);
+ }
+ else if (oldx > newx + 1)
+ {
+ for (i = oldx - 1; i >= newx; i--)
+ vec[i] = newy + (oldy - newy) *
+ ((float)(newx - i))/(float)(newx - oldx);
+ }
+ else vec[newx] = newy;
+ garray_redraw(a);
+}
+
+static int graph_click(t_gobj *z, struct _glist *glist,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ t_glist *x = (t_glist *)z;
+ t_gobj *y;
+ int clickreturned = 0;
+ if (!x->gl_isgraph)
+ return (text_widgetbehavior.w_clickfn(z, glist,
+ xpix, ypix, shift, alt, dbl, doit));
+ else if (x->gl_havewindow)
+ return (0);
+ else
+ {
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ int x1, y1, x2, y2;
+ /* check if the object wants to be clicked */
+ if (canvas_hitbox(x, y, xpix, ypix, &x1, &y1, &x2, &y2)
+ && (clickreturned = gobj_click(y, x, xpix, ypix,
+ shift, alt, 0, doit)))
+ break;
+ }
+ if (!doit)
+ {
+ if (y)
+ canvas_setcursor(glist_getcanvas(x), clickreturned);
+ else canvas_setcursor(glist_getcanvas(x), CURSOR_RUNMODE_NOTHING);
+ }
+ return (clickreturned);
+ }
+}
+
+static void graph_save(t_gobj *z, t_binbuf *b)
+{
+ t_glist *x = (t_glist *)z;
+ text_widgetbehavior.w_savefn(z, b);
+}
+
+void garray_properties(t_garray *x);
+
+static void graph_properties(t_gobj *z, t_glist *owner)
+{
+ t_glist *x = (t_glist *)z;
+ {
+ t_gobj *y;
+ char graphbuf[200];
+ sprintf(graphbuf, "pdtk_graph_dialog %%s %g %g %g %g %d %d\n",
+ x->gl_x1, x->gl_y1, x->gl_x2, x->gl_y2,
+ x->gl_pixwidth, x->gl_pixheight);
+ gfxstub_new(&x->gl_pd, x, graphbuf);
+
+ for (y = x->gl_list; y; y = y->g_next)
+ if (pd_class(&y->g_pd) == garray_class)
+ garray_properties((t_garray *)y);
+ }
+}
+
+t_widgetbehavior graph_widgetbehavior =
+{
+ graph_getrect,
+ graph_displace,
+ graph_select,
+ graph_activate,
+ graph_delete,
+ graph_vis,
+ graph_click,
+ graph_save,
+ graph_properties,
+};
+
+ /* find the graph most recently added to this glist;
+ if none exists, return 0. */
+
+t_glist *glist_findgraph(t_glist *x)
+{
+ t_gobj *y = 0, *z;
+ for (z = x->gl_list; z; z = z->g_next)
+ if (pd_class(&z->g_pd) == canvas_class && ((t_glist *)z)->gl_isgraph)
+ y = z;
+ return ((t_glist *)y);
+}
+
+ /* message back from dialog GUI to set parameters. Args are:
+ 1-4: bounds in our coordinates; 5-6: size in parent */
+static void graph_dialog(t_glist *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_float x1 = atom_getfloatarg(0, argc, argv);
+ t_float y1 = atom_getfloatarg(1, argc, argv);
+ t_float x2 = atom_getfloatarg(2, argc, argv);
+ t_float y2 = atom_getfloatarg(3, argc, argv);
+ t_float xpix = atom_getfloatarg(4, argc, argv);
+ t_float ypix = atom_getfloatarg(5, argc, argv);
+ if (x1 != x->gl_x1 || x2 != x->gl_x2 ||
+ y1 != x->gl_y1 || y2 != x->gl_y2)
+ graph_bounds(x, x1, y1, x2, y2);
+ if (xpix != x->gl_pixwidth || ypix != x->gl_pixheight)
+ {
+ x->gl_pixwidth = xpix;
+ x->gl_pixheight = ypix;
+ glist_redraw(x);
+ if (x->gl_owner)
+ canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
+ }
+}
+
+extern void canvas_menuarray(t_glist *canvas);
+
+void g_graph_setup(void)
+{
+ class_setwidget(canvas_class, &graph_widgetbehavior);
+ class_addmethod(canvas_class, (t_method)graph_bounds, gensym("bounds"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(canvas_class, (t_method)graph_xticks, gensym("xticks"),
+ A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(canvas_class, (t_method)graph_xlabel, gensym("xlabel"),
+ A_GIMME, 0);
+ class_addmethod(canvas_class, (t_method)graph_yticks, gensym("yticks"),
+ A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(canvas_class, (t_method)graph_ylabel, gensym("ylabel"),
+ A_GIMME, 0);
+ class_addmethod(canvas_class, (t_method)graph_array, gensym("array"),
+ A_SYMBOL, A_FLOAT, A_SYMBOL, A_DEFFLOAT, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_menuarray,
+ gensym("menuarray"), A_NULL);
+ class_addmethod(canvas_class, (t_method)graph_dialog, gensym("dialog"),
+ A_GIMME, 0);
+ class_addmethod(canvas_class, (t_method)glist_arraydialog,
+ gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(canvas_class, (t_method)glist_sort,
+ gensym("sort"), A_NULL);
+}
diff --git a/pd/src/g_guiconnect.c b/pd/src/g_guiconnect.c
new file mode 100644
index 00000000..aef8acb6
--- /dev/null
+++ b/pd/src/g_guiconnect.c
@@ -0,0 +1,94 @@
+/* Copyright (c) 1997-2000 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* a thing to forward messages from the GUI, dealing with race conditions
+in which the "target" gets deleted while the GUI is sending it something.
+*/
+
+#include "m_pd.h"
+#include "g_canvas.h"
+
+struct _guiconnect
+{
+ t_object x_obj;
+ t_pd *x_who;
+ t_symbol *x_sym;
+ t_clock *x_clock;
+};
+
+static t_class *guiconnect_class;
+
+t_guiconnect *guiconnect_new(t_pd *who, t_symbol *sym)
+{
+ t_guiconnect *x = (t_guiconnect *)pd_new(guiconnect_class);
+ x->x_who = who;
+ x->x_sym = sym;
+ pd_bind(&x->x_obj.ob_pd, sym);
+ return (x);
+}
+
+ /* cleanup routine; delete any resources we have */
+static void guiconnect_free(t_guiconnect *x)
+{
+ if (x->x_sym)
+ pd_unbind(&x->x_obj.ob_pd, x->x_sym);
+ if (x->x_clock)
+ clock_free(x->x_clock);
+}
+
+ /* this is called when the clock times out to indicate the GUI should
+ be gone by now. */
+static void guiconnect_tick(t_guiconnect *x)
+{
+ pd_free(&x->x_obj.ob_pd);
+}
+
+ /* the target calls this to disconnect. If the gui has "signed off"
+ we're ready to delete the object; otherwise we wait either for signoff
+ or for a timeout. */
+void guiconnect_notarget(t_guiconnect *x, double timedelay)
+{
+ if (!x->x_sym)
+ pd_free(&x->x_obj.ob_pd);
+ else
+ {
+ x->x_who = 0;
+ if (timedelay > 0)
+ {
+ x->x_clock = clock_new(x, (t_method)guiconnect_tick);
+ clock_delay(x->x_clock, timedelay);
+ }
+ }
+}
+
+ /* the GUI calls this to send messages to the target. */
+static void guiconnect_anything(t_guiconnect *x,
+ t_symbol *s, int ac, t_atom *av)
+{
+ if (x->x_who)
+ typedmess(x->x_who, s, ac, av);
+}
+
+ /* the GUI calls this when it disappears. (If there's any chance the
+ GUI will fail to do this, the "target", when it signs off, should specify
+ a timeout after which the guiconnect will disappear.) */
+static void guiconnect_signoff(t_guiconnect *x)
+{
+ if (!x->x_who)
+ pd_free(&x->x_obj.ob_pd);
+ else
+ {
+ pd_unbind(&x->x_obj.ob_pd, x->x_sym);
+ x->x_sym = 0;
+ }
+}
+
+void g_guiconnect_setup(void)
+{
+ guiconnect_class = class_new(gensym("guiconnect"), 0,
+ (t_method)guiconnect_free, sizeof(t_guiconnect), CLASS_PD, 0);
+ class_addanything(guiconnect_class, guiconnect_anything);
+ class_addmethod(guiconnect_class, (t_method)guiconnect_signoff,
+ gensym("signoff"), 0);
+}
diff --git a/pd/src/g_hdial.c b/pd/src/g_hdial.c
new file mode 100644
index 00000000..6e9f08a7
--- /dev/null
+++ b/pd/src/g_hdial.c
@@ -0,0 +1,675 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */
+/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include "t_tk.h"
+#include "g_all_guis.h"
+#include <math.h>
+
+#ifdef NT
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+/* ------------- hdl gui-horicontal dial ---------------------- */
+
+t_widgetbehavior hdial_widgetbehavior;
+static t_class *hdial_class;
+
+/* widget helper functions */
+
+void hdial_draw_update(t_hdial *x, t_glist *glist)
+{
+ if(glist_isvisible(glist))
+ {
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n",
+ canvas, x, x->x_on_old,
+ x->x_gui.x_bcol, x->x_gui.x_bcol);
+ sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n",
+ canvas, x, x->x_on,
+ x->x_gui.x_fcol, x->x_gui.x_fcol);
+ }
+}
+
+void hdial_draw_new(t_hdial *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int n=x->x_number, i, dx=x->x_gui.x_w, s4=dx/4;
+ int yy11=text_ypix(&x->x_gui.x_obj, glist), yy12=yy11+dx;
+ int yy21=yy11+s4, yy22=yy12-s4;
+ int xx11b=text_xpix(&x->x_gui.x_obj, glist), xx11=xx11b, xx21=xx11b+s4;
+ int xx22=xx11b+dx-s4;
+
+
+ for(i=0; i<n; i++)
+ {
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE%d\n",
+ canvas, xx11, yy11, xx11+dx, yy12,
+ x->x_gui.x_bcol, x, i);
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -outline #%6.6x -tags %xBUT%d\n",
+ canvas, xx21, yy21, xx22, yy22,
+ (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol,
+ (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, x, i);
+ xx11 += dx;
+ xx21 += dx;
+ xx22 += dx;
+ }
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n",
+ canvas, xx11b+x->x_gui.x_ldx, yy11+x->x_gui.x_ldy,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"",
+ x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_lcol, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas, xx11b, yy12-1, xx11b + IOWIDTH, yy12, x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas, xx11b, yy11, xx11b + IOWIDTH, yy11+1, x, 0);
+
+}
+
+void hdial_draw_move(t_hdial *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int n=x->x_number, i, dx=x->x_gui.x_w, s4=dx/4;
+ int yy11=text_ypix(&x->x_gui.x_obj, glist), yy12=yy11+dx;
+ int yy21=yy11+s4, yy22=yy12-s4;
+ int xx11b=text_xpix(&x->x_gui.x_obj, glist), xx11=xx11b, xx21=xx11b+s4;
+ int xx22=xx11b+dx-s4;
+
+ xx11 = xx11b;
+ xx21=xx11b+s4;
+ xx22=xx11b+dx-s4;
+ for(i=0; i<n; i++)
+ {
+ sys_vgui(".x%x.c coords %xBASE%d %d %d %d %d\n",
+ canvas, x, i, xx11, yy11, xx11+dx, yy12);
+ sys_vgui(".x%x.c coords %xBUT%d %d %d %d %d\n",
+ canvas, x, i, xx21, yy21, xx22, yy22);
+ xx11 += dx;
+ xx21 += dx;
+ xx22 += dx;
+ }
+ sys_vgui(".x%x.c coords %xLABEL %d %d\n",
+ canvas, x, xx11b+x->x_gui.x_ldx, yy11+x->x_gui.x_ldy);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n",
+ canvas, x, 0, xx11b, yy12-1, xx11b + IOWIDTH, yy12);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n",
+ canvas, x, 0, xx11b, yy11, xx11b + IOWIDTH, yy11+1);
+}
+
+void hdial_draw_erase(t_hdial* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int n=x->x_number, i;
+
+ for(i=0; i<n; i++)
+ {
+ sys_vgui(".x%x.c delete %xBASE%d\n", canvas, x, i);
+ sys_vgui(".x%x.c delete %xBUT%d\n", canvas, x, i);
+ }
+ sys_vgui(".x%x.c delete %xLABEL\n", canvas, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+void hdial_draw_config(t_hdial* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int n=x->x_number, i;
+
+ sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n",
+ canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"");
+ for(i=0; i<n; i++)
+ {
+ sys_vgui(".x%x.c itemconfigure %xBASE%d -fill #%6.6x\n", canvas, x, i,
+ x->x_gui.x_bcol);
+ sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", canvas, x, i,
+ (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol,
+ (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol);
+ }
+}
+
+void hdial_draw_io(t_hdial* x, t_glist* glist, int old_snd_rcv_flags)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+
+ if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas,
+ xpos, ypos + x->x_gui.x_w-1,
+ xpos + IOWIDTH, ypos + x->x_gui.x_w,
+ x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas,
+ xpos, ypos,
+ xpos + IOWIDTH, ypos+1, x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+void hdial_draw_select(t_hdial* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int n=x->x_number, i;
+
+ if(x->x_gui.x_fsf.x_selected)
+ {
+ pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ for(i=0; i<n; i++)
+ {
+ sys_vgui(".x%x.c itemconfigure %xBASE%d -outline #%6.6x\n", canvas, x, i,
+ IEM_GUI_COLOR_SELECTED);
+ }
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ }
+ else
+ {
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ for(i=0; i<n; i++)
+ {
+ sys_vgui(".x%x.c itemconfigure %xBASE%d -outline #%6.6x\n", canvas, x, i,
+ IEM_GUI_COLOR_NORMAL);
+ }
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x,
+ x->x_gui.x_lcol);
+ }
+}
+
+void hdial_draw(t_hdial *x, t_glist *glist, int mode)
+{
+ if(mode == IEM_GUI_DRAW_MODE_UPDATE)
+ hdial_draw_update(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_MOVE)
+ hdial_draw_move(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_NEW)
+ hdial_draw_new(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_SELECT)
+ hdial_draw_select(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_ERASE)
+ hdial_draw_erase(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_CONFIG)
+ hdial_draw_config(x, glist);
+ else if(mode >= IEM_GUI_DRAW_MODE_IO)
+ hdial_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO);
+}
+
+/* ------------------------ hdl widgetbehaviour----------------------------- */
+
+static void hdial_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_hdial *x = (t_hdial *)z;
+
+ *xp1 = text_xpix(&x->x_gui.x_obj, glist);
+ *yp1 = text_ypix(&x->x_gui.x_obj, glist);
+ *xp2 = *xp1 + x->x_gui.x_w*x->x_number;
+ *yp2 = *yp1 + x->x_gui.x_h;
+}
+
+static void hdial_save(t_gobj *z, t_binbuf *b)
+{
+ t_hdial *x = (t_hdial *)z;
+ int bflcol[3], *ip1, *ip2;
+ t_symbol *srl[3];
+
+ iemgui_save(&x->x_gui, srl, bflcol);
+ ip1 = (int *)(&x->x_gui.x_isa);
+ ip2 = (int *)(&x->x_gui.x_fsf);
+ binbuf_addv(b, "ssiisiiiisssiiiiiiii", gensym("#X"),gensym("obj"),
+ (t_int)text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist), (t_int)text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist),
+ gensym("hdl"), x->x_gui.x_w,
+ x->x_change, (*ip1)&IEM_INIT_ARGS_ALL, x->x_number,
+ srl[0], srl[1], srl[2],
+ x->x_gui.x_ldx, x->x_gui.x_ldy,
+ (*ip2)&IEM_FSTYLE_FLAGS_ALL, x->x_gui.x_fontsize,
+ bflcol[0], bflcol[1], bflcol[2], x->x_on);
+ binbuf_addv(b, ";");
+}
+
+static void hdial_properties(t_gobj *z, t_glist *owner)
+{
+ t_hdial *x = (t_hdial *)z;
+ char buf[800];
+ t_symbol *srl[3];
+
+ iemgui_properties(&x->x_gui, srl);
+ sprintf(buf, "pdtk_iemgui_dialog %%s HDIAL \
+ ----------dimensions(pix):----------- %d %d size: 0 0 empty \
+ empty 0.0 empty 0.0 empty %d \
+ %d new-only new&old %d %d number: %d \
+ %s %s \
+ %s %d %d \
+ %d %d \
+ %d %d %d\n",
+ x->x_gui.x_w, IEM_GUI_MINSIZE,
+ 0,/*no_schedule*/
+ x->x_change, x->x_gui.x_isa.x_loadinit, -1, x->x_number,
+ srl[0]->s_name, srl[1]->s_name,
+ srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy,
+ x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize,
+ 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol);
+ gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf);
+}
+
+static void hdial_dialog(t_hdial *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *srl[3];
+ int a = (int)atom_getintarg(0, argc, argv);
+ int chg = (int)atom_getintarg(4, argc, argv);
+ int num = (int)atom_getintarg(6, argc, argv);
+ int sr_flags;
+
+ if(chg != 0) chg = 1;
+ x->x_change = chg;
+ sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);
+ x->x_gui.x_w = iemgui_clip_size(a);
+ x->x_gui.x_h = x->x_gui.x_w;
+ if(x->x_number != num)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE);
+ x->x_number = num;
+ if(x->x_on >= x->x_number)
+ {
+ x->x_on = x->x_number - 1;
+ x->x_on_old = x->x_on;
+ }
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW);
+ }
+ else
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+ }
+
+}
+
+static void hdial_set(t_hdial *x, t_floatarg f)
+{
+ int i=(int)f;
+ int old=x->x_on_old;
+
+ if(i < 0)
+ i = 0;
+ if(i >= x->x_number)
+ i = x->x_number-1;
+ if(x->x_on != x->x_on_old)
+ {
+ old = x->x_on_old;
+ x->x_on_old = x->x_on;
+ x->x_on = i;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ x->x_on_old = old;
+ }
+ else
+ {
+ x->x_on = i;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ }
+}
+
+static void hdial_bang(t_hdial *x)
+{
+ if((x->x_change)&&(x->x_on != x->x_on_old))
+ {
+ SETFLOAT(x->x_at, (float)x->x_on_old);
+ SETFLOAT(x->x_at+1, 0.0);
+ outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);
+ }
+ x->x_on_old = x->x_on;
+ SETFLOAT(x->x_at, (float)x->x_on);
+ SETFLOAT(x->x_at+1, 1.0);
+ outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);
+}
+
+static void hdial_fout(t_hdial *x, t_floatarg f)
+{
+ int i=(int)f;
+
+ if(i < 0)
+ i = 0;
+ if(i >= x->x_number)
+ i = x->x_number-1;
+
+ if((x->x_change)&&(i != x->x_on_old))
+ {
+ SETFLOAT(x->x_at, (float)x->x_on_old);
+ SETFLOAT(x->x_at+1, 0.0);
+ outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);
+ }
+ if(x->x_on != x->x_on_old)
+ x->x_on_old = x->x_on;
+ x->x_on = i;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ x->x_on_old = x->x_on;
+ SETFLOAT(x->x_at, (float)x->x_on);
+ SETFLOAT(x->x_at+1, 1.0);
+ outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);
+}
+
+static void hdial_float(t_hdial *x, t_floatarg f)
+{
+ int i=(int)f;
+
+ if(i < 0)
+ i = 0;
+ if(i >= x->x_number)
+ i = x->x_number-1;
+
+ if((x->x_change)&&(i != x->x_on_old))
+ {
+ if(x->x_gui.x_fsf.x_put_in2out)
+ {
+ SETFLOAT(x->x_at, (float)x->x_on_old);
+ SETFLOAT(x->x_at+1, 0.0);
+ outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);
+ }
+ }
+ if(x->x_on != x->x_on_old)
+ x->x_on_old = x->x_on;
+ x->x_on = i;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ x->x_on_old = x->x_on;
+ if(x->x_gui.x_fsf.x_put_in2out)
+ {
+ SETFLOAT(x->x_at, (float)x->x_on);
+ SETFLOAT(x->x_at+1, 1.0);
+ outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);
+ }
+}
+
+static void hdial_click(t_hdial *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
+{
+ int xx = (int)xpos - (int)text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist);
+
+ hdial_fout(x, (float)(xx / x->x_gui.x_w));
+}
+
+static int hdial_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ if(doit)
+ hdial_click((t_hdial *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt);
+ return (1);
+}
+
+static void hdial_loadbang(t_hdial *x)
+{
+ if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit)
+ hdial_bang(x);
+}
+
+static void hdial_number(t_hdial *x, t_floatarg num)
+{
+ int n=(int)num;
+
+ if(n < 1)
+ n = 1;
+ if(n > IEM_RADIO_MAX)
+ n = IEM_RADIO_MAX;
+ if(n != x->x_number)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE);
+ x->x_number = n;
+ if(x->x_on >= x->x_number)
+ x->x_on = x->x_number - 1;
+ x->x_on_old = x->x_on;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW);
+ }
+}
+
+static void hdial_size(t_hdial *x, t_symbol *s, int ac, t_atom *av)
+{
+ x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av));
+ x->x_gui.x_h = x->x_gui.x_w;
+ iemgui_size((void *)x, &x->x_gui);
+}
+
+static void hdial_delta(t_hdial *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}
+
+static void hdial_pos(t_hdial *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void hdial_color(t_hdial *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_color((void *)x, &x->x_gui, s, ac, av);}
+
+static void hdial_send(t_hdial *x, t_symbol *s)
+{iemgui_send(x, &x->x_gui, s);}
+
+static void hdial_receive(t_hdial *x, t_symbol *s)
+{iemgui_receive(x, &x->x_gui, s);}
+
+static void hdial_label(t_hdial *x, t_symbol *s)
+{iemgui_label((void *)x, &x->x_gui, s);}
+
+static void hdial_label_pos(t_hdial *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void hdial_label_font(t_hdial *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);}
+
+static void hdial_init(t_hdial *x, t_floatarg f)
+{
+ x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1;
+}
+
+static void hdial_double_change(t_hdial *x)
+{x->x_change = 1;}
+
+static void hdial_single_change(t_hdial *x)
+{x->x_change = 0;}
+
+static void hdial_list(t_hdial *x, t_symbol *s, int ac, t_atom *av)
+{
+ int l=iemgui_list((void *)x, &x->x_gui, s, ac, av);
+
+ if(l < 0)
+ {
+ if(IS_A_FLOAT(av,0))
+ hdial_float(x, atom_getfloatarg(0, ac, av));
+ }
+ else if(l > 0)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+ }
+}
+
+static void *hdial_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_hdial *x = (t_hdial *)pd_new(hdial_class);
+ int bflcol[]={-262144, -1, -1};
+ t_symbol *srl[3];
+ int a=IEM_GUI_DEFAULTSIZE, on=0, f=0;
+ int ldx=0, ldy=-6, chg=1, num=8;
+ int fs=8, iinit=0, ifstyle=0;
+ int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME;
+ t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit);
+ t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle);
+ char str[144];
+
+ srl[0] = gensym("empty");
+ srl[1] = gensym("empty");
+ srl[2] = gensym("empty");
+
+ if((argc == 15)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2)
+ &&IS_A_FLOAT(argv,3)
+ &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4))
+ &&(IS_A_SYMBOL(argv,5)||IS_A_FLOAT(argv,5))
+ &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6))
+ &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8)
+ &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,11)
+ &&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13)&&IS_A_FLOAT(argv,14))
+ {
+ a = (int)atom_getintarg(0, argc, argv);
+ chg = (int)atom_getintarg(1, argc, argv);
+ iinit = (int)atom_getintarg(2, argc, argv);
+ num = (int)atom_getintarg(3, argc, argv);
+ if(IS_A_SYMBOL(argv,4))
+ srl[0] = atom_getsymbolarg(4, argc, argv);
+ else if(IS_A_FLOAT(argv,4))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(4, argc, argv));
+ srl[0] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,5))
+ srl[1] = atom_getsymbolarg(5, argc, argv);
+ else if(IS_A_FLOAT(argv,5))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(5, argc, argv));
+ srl[1] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,6))
+ srl[2] = atom_getsymbolarg(6, argc, argv);
+ else if(IS_A_FLOAT(argv,6))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(6, argc, argv));
+ srl[2] = gensym(str);
+ }
+ ldx = (int)atom_getintarg(7, argc, argv);
+ ldy = (int)atom_getintarg(8, argc, argv);
+ ifstyle = (int)atom_getintarg(9, argc, argv);
+ fs = (int)atom_getintarg(10, argc, argv);
+ bflcol[0] = (int)atom_getintarg(11, argc, argv);
+ bflcol[1] = (int)atom_getintarg(12, argc, argv);
+ bflcol[2] = (int)atom_getintarg(13, argc, argv);
+ on = (int)atom_getintarg(14, argc, argv);
+ }
+ x->x_gui.x_draw = (t_iemfunptr)hdial_draw;
+ iinit &= IEM_INIT_ARGS_ALL;
+ ifstyle &= IEM_FSTYLE_FLAGS_ALL;
+ fstyle->x_snd_able = 1;
+ fstyle->x_rcv_able = 1;
+ x->x_gui.x_glist = (t_glist *)canvas_getcurrent();
+ x->x_gui.x_isa = *init;
+ if(!strcmp(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0;
+ if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0;
+ if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica");
+ else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times");
+ else { fstyle->x_font_style = 0;
+ strcpy(x->x_gui.x_font, "courier"); }
+ x->x_gui.x_fsf = *fstyle;
+ x->x_gui.x_unique_num = 0;
+ if(num < 1)
+ num = 1;
+ if(num > IEM_RADIO_MAX)
+ num = IEM_RADIO_MAX;
+ x->x_number = num;
+ if(on < 0)
+ on = 0;
+ if(on >= x->x_number)
+ on = x->x_number - 1;
+ if(x->x_gui.x_isa.x_loadinit)
+ x->x_on = on;
+ else
+ x->x_on = 0;
+ x->x_on_old = x->x_on;
+ x->x_change = (chg==0)?0:1;
+ iemgui_first_dollararg2sym(&x->x_gui, srl);
+ if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]);
+ x->x_gui.x_snd = srl[0];
+ x->x_gui.x_rcv = srl[1];
+ x->x_gui.x_lab = srl[2];
+ x->x_gui.x_ldx = ldx;
+ x->x_gui.x_ldy = ldy;
+ if(fs < 4)
+ fs = 4;
+ x->x_gui.x_fontsize = fs;
+ x->x_gui.x_w = iemgui_clip_size(a);
+ x->x_gui.x_h = x->x_gui.x_w;
+ iemgui_verify_snd_ne_rcv(&x->x_gui);
+ iemgui_all_colfromload(&x->x_gui, bflcol);
+ outlet_new(&x->x_gui.x_obj, &s_list);
+ return (x);
+}
+
+static void hdial_ff(t_hdial *x)
+{
+ if(x->x_gui.x_fsf.x_selected)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ if(x->x_gui.x_fsf.x_rcv_able)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);
+ gfxstub_deleteforkey(x);
+}
+
+void g_hdial_setup(void)
+{
+ hdial_class = class_new(gensym("hdl"), (t_newmethod)hdial_new,
+ (t_method)hdial_ff, sizeof(t_hdial), 0, A_GIMME, 0);
+ class_addcreator((t_newmethod)hdial_new, gensym("rdb"), A_GIMME, 0);
+ class_addcreator((t_newmethod)hdial_new, gensym("radiobut"), A_GIMME, 0);
+ class_addcreator((t_newmethod)hdial_new, gensym("radiobutton"), A_GIMME, 0);
+ class_addbang(hdial_class, hdial_bang);
+ class_addfloat(hdial_class, hdial_float);
+ class_addlist(hdial_class, hdial_list);
+ class_addmethod(hdial_class, (t_method)hdial_click, gensym("click"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(hdial_class, (t_method)hdial_dialog, gensym("dialog"),
+ A_GIMME, 0);
+ class_addmethod(hdial_class, (t_method)hdial_loadbang, gensym("loadbang"), 0);
+ class_addmethod(hdial_class, (t_method)hdial_set, gensym("set"), A_FLOAT, 0);
+ class_addmethod(hdial_class, (t_method)hdial_size, gensym("size"), A_GIMME, 0);
+ class_addmethod(hdial_class, (t_method)hdial_delta, gensym("delta"), A_GIMME, 0);
+ class_addmethod(hdial_class, (t_method)hdial_pos, gensym("pos"), A_GIMME, 0);
+ class_addmethod(hdial_class, (t_method)hdial_color, gensym("color"), A_GIMME, 0);
+ class_addmethod(hdial_class, (t_method)hdial_send, gensym("send"), A_DEFSYM, 0);
+ class_addmethod(hdial_class, (t_method)hdial_receive, gensym("receive"), A_DEFSYM, 0);
+ class_addmethod(hdial_class, (t_method)hdial_label, gensym("label"), A_DEFSYM, 0);
+ class_addmethod(hdial_class, (t_method)hdial_label_pos, gensym("label_pos"), A_GIMME, 0);
+ class_addmethod(hdial_class, (t_method)hdial_label_font, gensym("label_font"), A_GIMME, 0);
+ class_addmethod(hdial_class, (t_method)hdial_init, gensym("init"), A_FLOAT, 0);
+ class_addmethod(hdial_class, (t_method)hdial_number, gensym("number"), A_FLOAT, 0);
+ class_addmethod(hdial_class, (t_method)hdial_single_change, gensym("single_change"), 0);
+ class_addmethod(hdial_class, (t_method)hdial_double_change, gensym("double_change"), 0);
+ if(!iemgui_key_sym)
+ iemgui_key_sym = gensym("#keyname");
+ hdial_widgetbehavior.w_getrectfn = hdial_getrect;
+ hdial_widgetbehavior.w_displacefn = iemgui_displace;
+ hdial_widgetbehavior.w_selectfn = iemgui_select;
+ hdial_widgetbehavior.w_activatefn = NULL;
+ hdial_widgetbehavior.w_deletefn = iemgui_delete;
+ hdial_widgetbehavior.w_visfn = iemgui_vis;
+ hdial_widgetbehavior.w_clickfn = hdial_newclick;
+ hdial_widgetbehavior.w_propertiesfn = hdial_properties;
+ hdial_widgetbehavior.w_savefn = hdial_save;
+ class_setwidget(hdial_class, &hdial_widgetbehavior);
+ class_sethelpsymbol(hdial_class, gensym("hdial"));
+}
diff --git a/pd/src/g_hslider.c b/pd/src/g_hslider.c
new file mode 100644
index 00000000..0e5415a9
--- /dev/null
+++ b/pd/src/g_hslider.c
@@ -0,0 +1,708 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */
+/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include "t_tk.h"
+#include "g_all_guis.h"
+#include <math.h>
+
+#ifdef NT
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+
+/* ------------ hsl gui-horicontal slider ----------------------- */
+
+t_widgetbehavior hslider_widgetbehavior;
+static t_class *hslider_class;
+
+/* widget helper functions */
+
+static void hslider_draw_update(t_hslider *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+
+ if (glist_isvisible(glist))
+ {
+ int r = text_xpix(&x->x_gui.x_obj, glist) + (x->x_val + 50)/100;
+ sys_vgui(".x%x.c coords %xKNOB %d %d %d %d\n",
+ canvas, x, r, ypos+1,
+ r, ypos + x->x_gui.x_h);
+ if(x->x_val == x->x_center)
+ {
+ if(!x->x_thick)
+ {
+ sys_vgui(".x%x.c itemconfigure %xKNOB -width 7\n", canvas, x);
+ x->x_thick = 1;
+ }
+ }
+ else
+ {
+ if(x->x_thick)
+ {
+ sys_vgui(".x%x.c itemconfigure %xKNOB -width 3\n", canvas, x);
+ x->x_thick = 0;
+ }
+ }
+ }
+}
+
+static void hslider_draw_new(t_hslider *x, t_glist *glist)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ int r = xpos + (x->x_val + 50)/100;
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n",
+ canvas, xpos-3, ypos,
+ xpos + x->x_gui.x_w+2, ypos + x->x_gui.x_h,
+ x->x_gui.x_bcol, x);
+ sys_vgui(".x%x.c create line %d %d %d %d -width 3 -fill #%6.6x -tags %xKNOB\n",
+ canvas, r, ypos+1, r,
+ ypos + x->x_gui.x_h, x->x_gui.x_fcol, x);
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n",
+ canvas, xpos+x->x_gui.x_ldx,
+ ypos+x->x_gui.x_ldy,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"",
+ x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas, xpos-3, ypos + x->x_gui.x_h-1,
+ xpos+4, ypos + x->x_gui.x_h, x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas, xpos-3, ypos,
+ xpos+4, ypos+1, x, 0);
+}
+
+static void hslider_draw_move(t_hslider *x, t_glist *glist)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ int r = xpos + (x->x_val + 50)/100;
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n",
+ canvas, x,
+ xpos-3, ypos,
+ xpos + x->x_gui.x_w+2, ypos + x->x_gui.x_h);
+ sys_vgui(".x%x.c coords %xKNOB %d %d %d %d\n",
+ canvas, x, r, ypos+1,
+ r, ypos + x->x_gui.x_h);
+ sys_vgui(".x%x.c coords %xLABEL %d %d\n",
+ canvas, x, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n",
+ canvas, x, 0,
+ xpos-3, ypos + x->x_gui.x_h-1,
+ xpos+4, ypos + x->x_gui.x_h);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n",
+ canvas, x, 0,
+ xpos-3, ypos,
+ xpos+4, ypos+1);
+}
+
+static void hslider_draw_erase(t_hslider* x,t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c delete %xBASE\n", canvas, x);
+ sys_vgui(".x%x.c delete %xKNOB\n", canvas, x);
+ sys_vgui(".x%x.c delete %xLABEL\n", canvas, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+static void hslider_draw_config(t_hslider* x,t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n",
+ canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"");
+ sys_vgui(".x%x.c itemconfigure %xKNOB -fill #%6.6x\n", canvas, x, x->x_gui.x_fcol);
+ sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, x, x->x_gui.x_bcol);
+}
+
+static void hslider_draw_io(t_hslider* x,t_glist* glist, int old_snd_rcv_flags)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas, xpos-3, ypos + x->x_gui.x_h-1,
+ xpos+4, ypos + x->x_gui.x_h, x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas, xpos-3, ypos,
+ xpos+4, ypos+1, x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+static void hslider_draw_select(t_hslider* x,t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if(x->x_gui.x_fsf.x_selected)
+ {
+ pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ }
+ else
+ {
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL);
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol);
+ }
+}
+
+void hslider_draw(t_hslider *x, t_glist *glist, int mode)
+{
+ if(mode == IEM_GUI_DRAW_MODE_UPDATE)
+ hslider_draw_update(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_MOVE)
+ hslider_draw_move(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_NEW)
+ hslider_draw_new(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_SELECT)
+ hslider_draw_select(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_ERASE)
+ hslider_draw_erase(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_CONFIG)
+ hslider_draw_config(x, glist);
+ else if(mode >= IEM_GUI_DRAW_MODE_IO)
+ hslider_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO);
+}
+
+/* ------------------------ hsl widgetbehaviour----------------------------- */
+
+
+static void hslider_getrect(t_gobj *z, t_glist *glist,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_hslider* x = (t_hslider*)z;
+
+ *xp1 = text_xpix(&x->x_gui.x_obj, glist) - 3;
+ *yp1 = text_ypix(&x->x_gui.x_obj, glist);
+ *xp2 = *xp1 + x->x_gui.x_w + 5;
+ *yp2 = *yp1 + x->x_gui.x_h;
+}
+
+static void hslider_save(t_gobj *z, t_binbuf *b)
+{
+ t_hslider *x = (t_hslider *)z;
+ int bflcol[3], *ip1, *ip2;
+ t_symbol *srl[3];
+
+ iemgui_save(&x->x_gui, srl, bflcol);
+ ip1 = (int *)(&x->x_gui.x_isa);
+ ip2 = (int *)(&x->x_gui.x_fsf);
+ binbuf_addv(b, "ssiisiiffiisssiiiiiiiii", gensym("#X"),gensym("obj"),
+ (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix,
+ gensym("hsl"), x->x_gui.x_w, x->x_gui.x_h,
+ (float)x->x_min, (float)x->x_max,
+ x->x_lin0_log1, (*ip1)&IEM_INIT_ARGS_ALL,
+ srl[0], srl[1], srl[2],
+ x->x_gui.x_ldx, x->x_gui.x_ldy,
+ (*ip2)&IEM_FSTYLE_FLAGS_ALL, x->x_gui.x_fontsize,
+ bflcol[0], bflcol[1], bflcol[2],
+ x->x_val, x->x_steady);
+ binbuf_addv(b, ";");
+}
+
+void hslider_check_width(t_hslider *x, int w)
+{
+ if(w < IEM_SL_MINSIZE)
+ w = IEM_SL_MINSIZE;
+ x->x_gui.x_w = w;
+ x->x_center = (x->x_gui.x_w-1)*50;
+ if(x->x_val > (x->x_gui.x_w*100 - 100))
+ {
+ x->x_pos = x->x_gui.x_w*100 - 100;
+ x->x_val = x->x_pos;
+ }
+ if(x->x_lin0_log1)
+ x->x_k = log(x->x_max/x->x_min)/(double)(x->x_gui.x_w - 1);
+ else
+ x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_w - 1);
+}
+
+void hslider_check_minmax(t_hslider *x, double min, double max)
+{
+ if(x->x_lin0_log1)
+ {
+ if((min == 0.0)&&(max == 0.0))
+ max = 1.0;
+ if(max > 0.0)
+ {
+ if(min <= 0.0)
+ min = 0.01*max;
+ }
+ else
+ {
+ if(min > 0.0)
+ max = 0.01*min;
+ }
+ }
+ x->x_min = min;
+ x->x_max = max;
+ if(x->x_min > x->x_max) /* bugfix */
+ x->x_gui.x_isa.x_reverse = 1;
+ else
+ x->x_gui.x_isa.x_reverse = 0;
+ if(x->x_lin0_log1)
+ x->x_k = log(x->x_max/x->x_min)/(double)(x->x_gui.x_w - 1);
+ else
+ x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_w - 1);
+}
+
+static void hslider_properties(t_gobj *z, t_glist *owner)
+{
+ t_hslider *x = (t_hslider *)z;
+ char buf[800];
+ t_symbol *srl[3];
+
+ iemgui_properties(&x->x_gui, srl);
+ sprintf(buf, "pdtk_iemgui_dialog %%s HSLIDER \
+ --------dimensions(pix)(pix):-------- %d %d width: %d %d height: \
+ -----------output-range:----------- %g left: %g right: %g \
+ %d lin log %d %d empty %d \
+ %s %s \
+ %s %d %d \
+ %d %d \
+ %d %d %d\n",
+ x->x_gui.x_w, IEM_SL_MINSIZE, x->x_gui.x_h, IEM_GUI_MINSIZE,
+ x->x_min, x->x_max, 0.0,/*no_schedule*/
+ x->x_lin0_log1, x->x_gui.x_isa.x_loadinit, x->x_steady, -1,/*no multi, but iem-characteristic*/
+ srl[0]->s_name, srl[1]->s_name,
+ srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy,
+ x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize,
+ 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol);
+ gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf);
+}
+
+static void hslider_set(t_hslider *x, t_floatarg f) /* bugfix */
+{
+ double g;
+
+ if(x->x_gui.x_isa.x_reverse) /* bugfix */
+ {
+ if(f > x->x_min)
+ f = x->x_min;
+ if(f < x->x_max)
+ f = x->x_max;
+ }
+ else
+ {
+ if(f > x->x_max)
+ f = x->x_max;
+ if(f < x->x_min)
+ f = x->x_min;
+ }
+ if(x->x_lin0_log1)
+ g = log(f/x->x_min)/x->x_k;
+ else
+ g = (f - x->x_min) / x->x_k;
+ x->x_val = (int)(100.0*g + 0.49999);
+ x->x_pos = x->x_val;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+}
+
+static void hslider_bang(t_hslider *x)
+{
+ double out;
+
+ if(x->x_lin0_log1)
+ out = x->x_min*exp(x->x_k*(double)(x->x_val)*0.01);
+ else
+ out = (double)(x->x_val)*0.01*x->x_k + x->x_min;
+ if((out < 1.0e-10)&&(out > -1.0e-10))
+ out = 0.0;
+ outlet_float(x->x_gui.x_obj.ob_outlet, out);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_float(x->x_gui.x_snd->s_thing, out);
+}
+
+static void hslider_dialog(t_hslider *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *srl[3];
+ int w = (int)atom_getintarg(0, argc, argv);
+ int h = (int)atom_getintarg(1, argc, argv);
+ double min = (double)atom_getfloatarg(2, argc, argv);
+ double max = (double)atom_getfloatarg(3, argc, argv);
+ int lilo = (int)atom_getintarg(4, argc, argv);
+ int steady = (int)atom_getintarg(17, argc, argv);
+ int sr_flags;
+
+ if(lilo != 0) lilo = 1;
+ x->x_lin0_log1 = lilo;
+ if(steady)
+ x->x_steady = 1;
+ else
+ x->x_steady = 0;
+ sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);
+ x->x_gui.x_h = iemgui_clip_size(h);
+ hslider_check_width(x, w);
+ hslider_check_minmax(x, min, max);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+}
+
+static void hslider_motion(t_hslider *x, t_floatarg dx, t_floatarg dy)
+{
+ int old = x->x_val;
+
+ if(x->x_gui.x_fsf.x_finemoved)
+ x->x_pos += (int)dx;
+ else
+ x->x_pos += 100*(int)dx;
+ x->x_val = x->x_pos;
+ if(x->x_val > (100*x->x_gui.x_w - 100))
+ {
+ x->x_val = 100*x->x_gui.x_w - 100;
+ x->x_pos += 50;
+ x->x_pos -= x->x_pos%100;
+ }
+ if(x->x_val < 0)
+ {
+ x->x_val = 0;
+ x->x_pos -= 50;
+ x->x_pos -= x->x_pos%100;
+ }
+ if(old != x->x_val)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ hslider_bang(x);
+ }
+}
+
+static void hslider_click(t_hslider *x, t_floatarg xpos, t_floatarg ypos,
+ t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
+{
+ if(!x->x_steady)
+ x->x_val = (int)(100.0 * (xpos - text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist)));
+ if(x->x_val > (100*x->x_gui.x_w - 100))
+ x->x_val = 100*x->x_gui.x_w - 100;
+ if(x->x_val < 0)
+ x->x_val = 0;
+ x->x_pos = x->x_val;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ hslider_bang(x);
+ glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.te_g, (t_glistmotionfn)hslider_motion,
+ 0, xpos, ypos);
+}
+
+static int hslider_newclick(t_gobj *z, struct _glist *glist,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ t_hslider* x = (t_hslider *)z;
+
+ if(doit)
+ {
+ hslider_click( x, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift,
+ 0, (t_floatarg)alt);
+ if(shift)
+ x->x_gui.x_fsf.x_finemoved = 1;
+ else
+ x->x_gui.x_fsf.x_finemoved = 0;
+ }
+ return (1);
+}
+
+static void hslider_size(t_hslider *x, t_symbol *s, int ac, t_atom *av)
+{
+ hslider_check_width(x, (int)atom_getintarg(0, ac, av));
+ if(ac > 1)
+ x->x_gui.x_h = iemgui_clip_size((int)atom_getintarg(1, ac, av));
+ iemgui_size((void *)x, &x->x_gui);
+}
+
+static void hslider_delta(t_hslider *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}
+
+static void hslider_pos(t_hslider *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void hslider_range(t_hslider *x, t_symbol *s, int ac, t_atom *av)
+{
+ hslider_check_minmax(x, (double)atom_getfloatarg(0, ac, av),
+ (double)atom_getfloatarg(1, ac, av));
+}
+
+static void hslider_color(t_hslider *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_color((void *)x, &x->x_gui, s, ac, av);}
+
+static void hslider_send(t_hslider *x, t_symbol *s)
+{iemgui_send(x, &x->x_gui, s);}
+
+static void hslider_receive(t_hslider *x, t_symbol *s)
+{iemgui_receive(x, &x->x_gui, s);}
+
+static void hslider_label(t_hslider *x, t_symbol *s)
+{iemgui_label((void *)x, &x->x_gui, s);}
+
+static void hslider_label_pos(t_hslider *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void hslider_label_font(t_hslider *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);}
+
+static void hslider_log(t_hslider *x)
+{
+ x->x_lin0_log1 = 1;
+ hslider_check_minmax(x, x->x_min, x->x_max);
+}
+
+static void hslider_lin(t_hslider *x)
+{
+ x->x_lin0_log1 = 0;
+ x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_w - 1);
+}
+
+static void hslider_init(t_hslider *x, t_floatarg f)
+{
+ x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1;
+}
+
+static void hslider_steady(t_hslider *x, t_floatarg f)
+{
+ x->x_steady = (f==0.0)?0:1;
+}
+
+static void hslider_float(t_hslider *x, t_floatarg f)
+{
+ double out;
+
+ hslider_set(x, f);
+ if(x->x_lin0_log1)
+ out = x->x_min*exp(x->x_k*(double)(x->x_val)*0.01);
+ else
+ out = (double)(x->x_val)*0.01*x->x_k + x->x_min;
+ if((out < 1.0e-10)&&(out > -1.0e-10))
+ out = 0.0;
+ if(x->x_gui.x_fsf.x_put_in2out)
+ {
+ outlet_float(x->x_gui.x_obj.ob_outlet, out);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_float(x->x_gui.x_snd->s_thing, out);
+ }
+}
+
+static void hslider_loadbang(t_hslider *x)
+{
+ if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ hslider_bang(x);
+ }
+}
+
+static void hslider_list(t_hslider *x, t_symbol *s, int ac, t_atom *av)
+{
+ int l=iemgui_list((void *)x, &x->x_gui, s, ac, av);
+
+ if(l < 0)
+ {
+ if(IS_A_FLOAT(av,0))
+ hslider_float(x, atom_getfloatarg(0, ac, av));
+ }
+ else if(l > 0)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+ }
+}
+
+static void *hslider_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_hslider *x = (t_hslider *)pd_new(hslider_class);
+ int bflcol[]={-262144, -1, -1};
+ t_symbol *srl[3];
+ int w=IEM_SL_DEFAULTSIZE, h=IEM_GUI_DEFAULTSIZE;
+ int lilo=0, ldx=-2, ldy=-6, f=0, v=0, steady=1;
+ int fs=8, iinit=0, ifstyle=0;
+ double min=0.0, max=(double)(IEM_SL_DEFAULTSIZE-1);
+ t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit);
+ t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle);
+ char str[144];
+
+ srl[0] = gensym("empty");
+ srl[1] = gensym("empty");
+ srl[2] = gensym("empty");
+
+ if(((argc == 17)||(argc == 18))&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)
+ &&IS_A_FLOAT(argv,2)&&IS_A_FLOAT(argv,3)
+ &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5)
+ &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6))
+ &&(IS_A_SYMBOL(argv,7)||IS_A_FLOAT(argv,7))
+ &&(IS_A_SYMBOL(argv,8)||IS_A_FLOAT(argv,8))
+ &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)
+ &&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13)
+ &&IS_A_FLOAT(argv,14)&&IS_A_FLOAT(argv,15)&&IS_A_FLOAT(argv,16))
+ {
+ w = (int)atom_getintarg(0, argc, argv);
+ h = (int)atom_getintarg(1, argc, argv);
+ min = (double)atom_getfloatarg(2, argc, argv);
+ max = (double)atom_getfloatarg(3, argc, argv);
+ lilo = (int)atom_getintarg(4, argc, argv);
+ iinit = (int)atom_getintarg(5, argc, argv);
+ if(IS_A_SYMBOL(argv,6))
+ srl[0] = atom_getsymbolarg(6, argc, argv);
+ else if(IS_A_FLOAT(argv,6))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(6, argc, argv));
+ srl[0] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,7))
+ srl[1] = atom_getsymbolarg(7, argc, argv);
+ else if(IS_A_FLOAT(argv,7))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(7, argc, argv));
+ srl[1] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,8))
+ srl[2] = atom_getsymbolarg(8, argc, argv);
+ else if(IS_A_FLOAT(argv,8))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(8, argc, argv));
+ srl[2] = gensym(str);
+ }
+ ldx = (int)atom_getintarg(9, argc, argv);
+ ldy = (int)atom_getintarg(10, argc, argv);
+ ifstyle = (int)atom_getintarg(11, argc, argv);
+ fs = (int)atom_getintarg(12, argc, argv);
+ bflcol[0] = (int)atom_getintarg(13, argc, argv);
+ bflcol[1] = (int)atom_getintarg(14, argc, argv);
+ bflcol[2] = (int)atom_getintarg(15, argc, argv);
+ v = (int)atom_getintarg(16, argc, argv);
+ }
+ if((argc == 18)&&IS_A_FLOAT(argv,17))
+ steady = (int)atom_getintarg(17, argc, argv);
+
+ x->x_gui.x_draw = (t_iemfunptr)hslider_draw;
+ iinit &= IEM_INIT_ARGS_ALL;
+ ifstyle &= IEM_FSTYLE_FLAGS_ALL;
+
+ fstyle->x_snd_able = 1;
+ fstyle->x_rcv_able = 1;
+
+ x->x_gui.x_glist = (t_glist *)canvas_getcurrent();
+ x->x_gui.x_isa = *init;
+ if(x->x_gui.x_isa.x_loadinit)
+ x->x_val = v;
+ else
+ x->x_val = 0;
+ x->x_pos = x->x_val;
+ if(lilo != 0) lilo = 1;
+ x->x_lin0_log1 = lilo;
+ if(steady != 0) steady = 1;
+ x->x_steady = steady;
+ if(!strcmp(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0;
+ if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0;
+ x->x_gui.x_unique_num = 0;
+ if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica");
+ else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times");
+ else { fstyle->x_font_style = 0;
+ strcpy(x->x_gui.x_font, "courier"); }
+ x->x_gui.x_fsf = *fstyle;
+ iemgui_first_dollararg2sym(&x->x_gui, srl);
+ if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]);
+ x->x_gui.x_snd = srl[0];
+ x->x_gui.x_rcv = srl[1];
+ x->x_gui.x_lab = srl[2];
+ x->x_gui.x_ldx = ldx;
+ x->x_gui.x_ldy = ldy;
+ if(fs < 4)
+ fs = 4;
+ x->x_gui.x_fontsize = fs;
+ x->x_gui.x_h = iemgui_clip_size(h);
+ hslider_check_width(x, w);
+ hslider_check_minmax(x, min, max);
+ iemgui_all_colfromload(&x->x_gui, bflcol);
+ x->x_thick = 0;
+ iemgui_verify_snd_ne_rcv(&x->x_gui);
+ outlet_new(&x->x_gui.x_obj, &s_float);
+ return (x);
+}
+
+static void hslider_free(t_hslider *x)
+{
+ if(x->x_gui.x_fsf.x_selected)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ if(x->x_gui.x_fsf.x_rcv_able)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);
+ gfxstub_deleteforkey(x);
+}
+
+void g_hslider_setup(void)
+{
+ hslider_class = class_new(gensym("hsl"), (t_newmethod)hslider_new,
+ (t_method)hslider_free, sizeof(t_hslider), 0, A_GIMME, 0);
+#ifndef GGEE_HSLIDER_COMPATIBLE
+ class_addcreator((t_newmethod)hslider_new, gensym("hslider"), A_GIMME, 0);
+#endif
+ class_addbang(hslider_class,hslider_bang);
+ class_addfloat(hslider_class,hslider_float);
+ class_addlist(hslider_class, hslider_list);
+ class_addmethod(hslider_class, (t_method)hslider_click, gensym("click"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(hslider_class, (t_method)hslider_motion, gensym("motion"),
+ A_FLOAT, A_FLOAT, 0);
+ class_addmethod(hslider_class, (t_method)hslider_dialog, gensym("dialog"), A_GIMME, 0);
+ class_addmethod(hslider_class, (t_method)hslider_loadbang, gensym("loadbang"), 0);
+ class_addmethod(hslider_class, (t_method)hslider_set, gensym("set"), A_FLOAT, 0);
+ class_addmethod(hslider_class, (t_method)hslider_size, gensym("size"), A_GIMME, 0);
+ class_addmethod(hslider_class, (t_method)hslider_delta, gensym("delta"), A_GIMME, 0);
+ class_addmethod(hslider_class, (t_method)hslider_pos, gensym("pos"), A_GIMME, 0);
+ class_addmethod(hslider_class, (t_method)hslider_range, gensym("range"), A_GIMME, 0);
+ class_addmethod(hslider_class, (t_method)hslider_color, gensym("color"), A_GIMME, 0);
+ class_addmethod(hslider_class, (t_method)hslider_send, gensym("send"), A_DEFSYM, 0);
+ class_addmethod(hslider_class, (t_method)hslider_receive, gensym("receive"), A_DEFSYM, 0);
+ class_addmethod(hslider_class, (t_method)hslider_label, gensym("label"), A_DEFSYM, 0);
+ class_addmethod(hslider_class, (t_method)hslider_label_pos, gensym("label_pos"), A_GIMME, 0);
+ class_addmethod(hslider_class, (t_method)hslider_label_font, gensym("label_font"), A_GIMME, 0);
+ class_addmethod(hslider_class, (t_method)hslider_log, gensym("log"), 0);
+ class_addmethod(hslider_class, (t_method)hslider_lin, gensym("lin"), 0);
+ class_addmethod(hslider_class, (t_method)hslider_init, gensym("init"), A_FLOAT, 0);
+ class_addmethod(hslider_class, (t_method)hslider_steady, gensym("steady"), A_FLOAT, 0);
+ if(!iemgui_key_sym)
+ iemgui_key_sym = gensym("#keyname");
+ hslider_widgetbehavior.w_getrectfn = hslider_getrect;
+ hslider_widgetbehavior.w_displacefn = iemgui_displace;
+ hslider_widgetbehavior.w_selectfn = iemgui_select;
+ hslider_widgetbehavior.w_activatefn = NULL;
+ hslider_widgetbehavior.w_deletefn = iemgui_delete;
+ hslider_widgetbehavior.w_visfn = iemgui_vis;
+ hslider_widgetbehavior.w_clickfn = hslider_newclick;
+ hslider_widgetbehavior.w_propertiesfn = hslider_properties;
+ hslider_widgetbehavior.w_savefn = hslider_save;
+ class_setwidget(hslider_class, &hslider_widgetbehavior);
+ class_sethelpsymbol(hslider_class, gensym("hslider"));
+}
diff --git a/pd/src/g_io.c b/pd/src/g_io.c
new file mode 100644
index 00000000..487be350
--- /dev/null
+++ b/pd/src/g_io.c
@@ -0,0 +1,612 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* graphical inlets and outlets, both for control and signals. */
+
+/* This code is highly inefficient; messages actually have to be forwarded
+by inlets and outlets. The outlet is in even worse shape than the inlet;
+in order to avoid having a "signal" method in the class, the oulet actually
+sprouts an inlet, which forwards the message to the "outlet" object, which
+sends it on to the outlet proper. Another way to do it would be to have
+separate classes for "signal" and "control" outlets, but this would complicate
+life elsewhere. */
+
+
+/* hacked to run subpatches with different samplerates
+ *
+ * mfg.gfd.uil
+ * IOhannes
+ *
+ * edited lines are marked with "IOhannes"
+ *
+ */
+
+#include "m_pd.h"
+#include "g_canvas.h"
+#include <string.h>
+void signal_setborrowed(t_signal *sig, t_signal *sig2);
+void signal_makereusable(t_signal *sig);
+
+/* ------------------------- vinlet -------------------------- */
+t_class *vinlet_class;
+
+typedef struct _vinlet
+{
+ t_object x_obj;
+ t_canvas *x_canvas;
+ t_inlet *x_inlet;
+ int x_bufsize;
+ t_float *x_buf; /* signal buffer; zero if not a signal */
+ t_float *x_endbuf;
+ t_float *x_fill;
+ t_float *x_read;
+ int x_hop;
+ /* if not reblocking, the next slot communicates the parent's inlet
+ signal from the prolog to the DSP routine: */
+ t_signal *x_directsignal;
+
+ t_resample x_updown; /* IOhannes */
+} t_vinlet;
+
+static void *vinlet_new(t_symbol *s)
+{
+ t_vinlet *x = (t_vinlet *)pd_new(vinlet_class);
+ x->x_canvas = canvas_getcurrent();
+ x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, 0);
+ x->x_bufsize = 0;
+ x->x_buf = 0;
+ outlet_new(&x->x_obj, 0);
+ return (x);
+}
+
+static void vinlet_bang(t_vinlet *x)
+{
+ outlet_bang(x->x_obj.ob_outlet);
+}
+
+static void vinlet_pointer(t_vinlet *x, t_gpointer *gp)
+{
+ outlet_pointer(x->x_obj.ob_outlet, gp);
+}
+
+static void vinlet_float(t_vinlet *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, f);
+}
+
+static void vinlet_symbol(t_vinlet *x, t_symbol *s)
+{
+ outlet_symbol(x->x_obj.ob_outlet, s);
+}
+
+static void vinlet_list(t_vinlet *x, t_symbol *s, int argc, t_atom *argv)
+{
+ outlet_list(x->x_obj.ob_outlet, s, argc, argv);
+}
+
+static void vinlet_anything(t_vinlet *x, t_symbol *s, int argc, t_atom *argv)
+{
+ outlet_anything(x->x_obj.ob_outlet, s, argc, argv);
+}
+
+static void vinlet_free(t_vinlet *x)
+{
+ canvas_rminlet(x->x_canvas, x->x_inlet);
+ resample_free(&x->x_updown);
+}
+
+t_inlet *vinlet_getit(t_pd *x)
+{
+ if (pd_class(x) != vinlet_class) bug("vinlet_getit");
+ return (((t_vinlet *)x)->x_inlet);
+}
+
+/* ------------------------- signal inlet -------------------------- */
+int vinlet_issignal(t_vinlet *x)
+{
+ return (x->x_buf != 0);
+}
+
+static int tot;
+
+t_int *vinlet_perform(t_int *w)
+{
+ t_vinlet *x = (t_vinlet *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ t_float *in = x->x_read;
+#if 0
+ if (tot < 5) post("-in %x out %x n %d", in, out, n);
+ if (tot < 5) post("-buf %x endbuf %x", x->x_buf, x->x_endbuf);
+ if (tot < 5) post("in[0] %f in[1] %f in[2] %f", in[0], in[1], in[2]);
+#endif
+ while (n--) *out++ = *in++;
+ if (in == x->x_endbuf) in = x->x_buf;
+ x->x_read = in;
+ return (w+4);
+}
+
+static void vinlet_dsp(t_vinlet *x, t_signal **sp)
+{
+ t_signal *outsig;
+ /* no buffer means we're not a signal inlet */
+ if (!x->x_buf)
+ return;
+ outsig = sp[0];
+ if (x->x_directsignal)
+ {
+ signal_setborrowed(sp[0], x->x_directsignal);
+ }
+ else
+ {
+ dsp_add(vinlet_perform, 3, x, outsig->s_vec, outsig->s_n);
+ x->x_read = x->x_buf;
+ }
+}
+
+ /* prolog code: loads buffer from parent patch */
+t_int *vinlet_doprolog(t_int *w)
+{
+ t_vinlet *x = (t_vinlet *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ t_float *out = x->x_fill;
+ if (out == x->x_endbuf)
+ {
+ t_float *f1 = x->x_buf, *f2 = x->x_buf + x->x_hop;
+ int nshift = x->x_bufsize - x->x_hop;
+ out -= x->x_hop;
+ while (nshift--) *f1++ = *f2++;
+ }
+#if 0
+ if (tot < 5) post("in %x out %x n %x", in, out, n), tot++;
+ if (tot < 5) post("in[0] %f in[1] %f in[2] %f", in[0], in[1], in[2]);
+#endif
+
+ while (n--) *out++ = *in++;
+ x->x_fill = out;
+ return (w+4);
+}
+
+int inlet_getsignalindex(t_inlet *x);
+
+ /* set up prolog DSP code */
+void vinlet_dspprolog(t_vinlet *x, t_signal **parentsigs,
+ int myvecsize, int phase, int period, int frequency, int downsample, int upsample/* IOhannes */, int reblock,
+ int switched)
+{
+ t_signal *insig, *outsig;
+ x->x_updown.downsample = downsample;
+ x->x_updown.upsample = upsample;
+
+ /* if the "reblock" flag is set, arrange to copy data in from the
+ parent. */
+ if (reblock)
+ {
+ int parentvecsize, bufsize, oldbufsize, prologphase;
+ int re_parentvecsize; /* resampled parentvectorsize: IOhannes */
+ /* this should never happen: */
+ if (!x->x_buf) return;
+
+ /* the prolog code counts from 0 to period-1; the
+ phase is backed up by one so that AFTER the prolog code
+ runs, the "x_fill" phase is in sync with the "x_read" phase. */
+ prologphase = (phase - 1) & (period - 1);
+ if (parentsigs)
+ {
+ insig = parentsigs[inlet_getsignalindex(x->x_inlet)];
+ parentvecsize = insig->s_n;
+ re_parentvecsize = parentvecsize * upsample / downsample;
+ }
+ else
+ {
+ insig = 0;
+ parentvecsize = 1;
+ re_parentvecsize = 1;
+ }
+
+ bufsize = re_parentvecsize;
+ if (bufsize < myvecsize) bufsize = myvecsize;
+ if (bufsize != (oldbufsize = x->x_bufsize))
+ {
+ t_float *buf = x->x_buf;
+ t_freebytes(buf, oldbufsize * sizeof(*buf));
+ buf = (t_float *)t_getbytes(bufsize * sizeof(*buf));
+ memset((char *)buf, 0, bufsize * sizeof(*buf));
+ x->x_bufsize = bufsize;
+ x->x_endbuf = buf + bufsize;
+ x->x_buf = buf;
+ }
+ if (parentsigs)
+ {
+ /* IOhannes { */
+ x->x_hop = period * re_parentvecsize;
+
+ x->x_fill = x->x_endbuf -
+ (x->x_hop - prologphase * re_parentvecsize);
+
+ if (upsample * downsample == 1)
+ dsp_add(vinlet_doprolog, 3, x, insig->s_vec, re_parentvecsize);
+ else {
+ resamplefrom_dsp(&x->x_updown, insig->s_vec, parentvecsize, re_parentvecsize, x->x_updown.method);
+ dsp_add(vinlet_doprolog, 3, x, x->x_updown.s_vec, re_parentvecsize);
+ }
+
+ /* } IOhannes */
+ /* if the input signal's reference count is zero, we have
+ to free it here because we didn't in ugen_doit(). */
+ if (!insig->s_refcount)
+ signal_makereusable(insig);
+ }
+ else memset((char *)(x->x_buf), 0, bufsize * sizeof(*x->x_buf));
+ x->x_directsignal = 0;
+ }
+ else
+ {
+ /* no reblocking; in this case our output signal is "borrowed"
+ and merely needs to be pointed to the real one. */
+ x->x_directsignal = parentsigs[inlet_getsignalindex(x->x_inlet)];
+ }
+}
+
+//static void *vinlet_newsig(void)
+static void *vinlet_newsig(t_symbol *s)
+{
+ t_vinlet *x = (t_vinlet *)pd_new(vinlet_class);
+ x->x_canvas = canvas_getcurrent();
+ x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, &s_signal);
+ x->x_endbuf = x->x_buf = (t_float *)getbytes(0);
+ x->x_bufsize = 0;
+ x->x_directsignal = 0;
+ outlet_new(&x->x_obj, &s_signal);
+
+ resample_init(&x->x_updown);
+
+ /* this should be though over:
+ * it might prove hard to provide consistency between labeled up- & downsampling methods
+ * maybe indeces would be better...
+ *
+ * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !)
+ */
+ if (s == gensym("hold"))x->x_updown.method=1; /* up: sample and hold */
+ else if (s == gensym("lin"))x->x_updown.method=2; /* up: linear interpolation */
+ else x->x_updown.method=0; /* up: zero-padding */
+
+ return (x);
+}
+
+static void vinlet_setup(void)
+{
+ vinlet_class = class_new(gensym("inlet"), (t_newmethod)vinlet_new,
+ (t_method)vinlet_free, sizeof(t_vinlet), CLASS_NOINLET, A_DEFSYM, 0);
+ class_addcreator((t_newmethod)vinlet_newsig, gensym("inlet~"), A_DEFSYM, 0);
+ class_addbang(vinlet_class, vinlet_bang);
+ class_addpointer(vinlet_class, vinlet_pointer);
+ class_addfloat(vinlet_class, vinlet_float);
+ class_addsymbol(vinlet_class, vinlet_symbol);
+ class_addlist(vinlet_class, vinlet_list);
+ class_addanything(vinlet_class, vinlet_anything);
+ class_addmethod(vinlet_class, (t_method)vinlet_dsp, gensym("dsp"), 0);
+ class_sethelpsymbol(vinlet_class, gensym("pd"));
+}
+
+/* ------------------------- voutlet -------------------------- */
+
+t_class *voutlet_class;
+
+typedef struct _voutlet
+{
+ t_object x_obj;
+ t_canvas *x_canvas;
+ t_outlet *x_parentoutlet;
+ int x_bufsize;
+ t_float *x_buf; /* signal buffer; zero if not a signal */
+ t_float *x_endbuf;
+ t_float *x_empty; /* next to read out of buffer in epilog code */
+ t_float *x_write; /* next to write in to buffer */
+ int x_hop; /* hopsize */
+ /* vice versa from the inlet, if we don't block, this holds the
+ parent's outlet signal, valid between the prolog and the dsp setup
+ routines. */
+ t_signal *x_directsignal;
+ /* and here's a flag indicating that we aren't blocked but have to
+ do a copy (because we're switched). */
+ char x_justcopyout;
+ t_resample x_updown; /* IOhannes */
+} t_voutlet;
+
+static void *voutlet_new(t_symbol *s)
+{
+ t_voutlet *x = (t_voutlet *)pd_new(voutlet_class);
+ x->x_canvas = canvas_getcurrent();
+ x->x_parentoutlet = canvas_addoutlet(x->x_canvas, &x->x_obj.ob_pd, 0);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, 0, 0);
+ x->x_bufsize = 0;
+ x->x_buf = 0;
+ return (x);
+}
+
+static void voutlet_bang(t_voutlet *x)
+{
+ outlet_bang(x->x_parentoutlet);
+}
+
+static void voutlet_pointer(t_voutlet *x, t_gpointer *gp)
+{
+ outlet_pointer(x->x_parentoutlet, gp);
+}
+
+static void voutlet_float(t_voutlet *x, t_float f)
+{
+ outlet_float(x->x_parentoutlet, f);
+}
+
+static void voutlet_symbol(t_voutlet *x, t_symbol *s)
+{
+ outlet_symbol(x->x_parentoutlet, s);
+}
+
+static void voutlet_list(t_voutlet *x, t_symbol *s, int argc, t_atom *argv)
+{
+ outlet_list(x->x_parentoutlet, s, argc, argv);
+}
+
+static void voutlet_anything(t_voutlet *x, t_symbol *s, int argc, t_atom *argv)
+{
+ outlet_anything(x->x_parentoutlet, s, argc, argv);
+}
+
+static void voutlet_free(t_voutlet *x)
+{
+ canvas_rmoutlet(x->x_canvas, x->x_parentoutlet);
+ resample_free(&x->x_updown);
+}
+
+t_outlet *voutlet_getit(t_pd *x)
+{
+ if (pd_class(x) != voutlet_class) bug("voutlet_getit");
+ return (((t_voutlet *)x)->x_parentoutlet);
+}
+
+/* ------------------------- signal outlet -------------------------- */
+
+int voutlet_issignal(t_voutlet *x)
+{
+ return (x->x_buf != 0);
+}
+
+ /* LATER optimize for non-overlapped case where the "+=" isn't needed */
+t_int *voutlet_perform(t_int *w)
+{
+ t_voutlet *x = (t_voutlet *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ t_float *out = x->x_write, *outwas = out;
+#if 0
+ if (tot < 5) post("-in %x out %x n %d", in, out, n);
+ if (tot < 5) post("-buf %x endbuf %x", x->x_buf, x->x_endbuf);
+#endif
+ while (n--)
+ {
+ *out++ += *in++;
+ if (out == x->x_endbuf) out = x->x_buf;
+ }
+ outwas += x->x_hop;
+ if (outwas >= x->x_endbuf) outwas = x->x_buf;
+ x->x_write = outwas;
+ return (w+4);
+}
+
+ /* epilog code for blocking: write buffer to parent patch */
+static t_int *voutlet_doepilog(t_int *w)
+{
+ t_voutlet *x = (t_voutlet *)(w[1]);
+ t_float *out = (t_float *)(w[2]); /* IOhannes */
+
+ int n = (int)(w[3]);
+ t_float *in = x->x_empty;
+ if (x->x_updown.downsample != x->x_updown.upsample) out = x->x_updown.s_vec; /* IOhannes */
+
+#if 0
+ if (tot < 5) post("outlet in %x out %x n %x", in, out, n), tot++;
+#endif
+ for (; n--; in++) *out++ = *in, *in = 0;
+ if (in == x->x_endbuf) in = x->x_buf;
+ x->x_empty = in;
+ return (w+4);
+}
+
+/* IOhannes { */
+static t_int *voutlet_doepilog_resampling(t_int *w)
+{
+ t_voutlet *x = (t_voutlet *)(w[1]);
+ // t_float *dummy = (t_float *)(w[2]);
+ int n = (int)(w[2]);
+ t_float *in = x->x_empty;
+ t_float *out = x->x_updown.s_vec; /* IOhannes */
+
+#if 0
+ if (tot < 5) post("outlet in %x out %x n %x", in, out, n), tot++;
+#endif
+ for (; n--; in++) *out++ = *in, *in = 0;
+ if (in == x->x_endbuf) in = x->x_buf;
+ x->x_empty = in;
+ return (w+3);
+}
+/* } IOhannes */
+int outlet_getsignalindex(t_outlet *x);
+
+ /* prolog for outlets -- store pointer to the outlet on the
+ parent, which, if "reblock" is false, will want to refer
+ back to whatever we see on our input during the "dsp" method
+ called later. */
+void voutlet_dspprolog(t_voutlet *x, t_signal **parentsigs,
+ int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock,
+ int switched)
+{
+ x->x_updown.downsample=downsample; x->x_updown.upsample=upsample; /* IOhannes */
+ x->x_justcopyout = (switched && !reblock);
+ if (reblock)
+ {
+ x->x_directsignal = 0;
+ }
+ else
+ {
+ if (!parentsigs) bug("voutlet_dspprolog");
+ x->x_directsignal =
+ parentsigs[outlet_getsignalindex(x->x_parentoutlet)];
+ }
+}
+
+static void voutlet_dsp(t_voutlet *x, t_signal **sp)
+{
+ t_signal *insig;
+ if (!x->x_buf) return;
+ insig = sp[0];
+ if (x->x_justcopyout)
+ dsp_add_copy(insig->s_vec, x->x_directsignal->s_vec, insig->s_n);
+ else if (x->x_directsignal)
+ {
+ /* if we're just going to make the signal available on the
+ parent patch, hand it off to the parent signal. */
+ /* this is done elsewhere--> sp[0]->s_refcount++; */
+ signal_setborrowed(x->x_directsignal, sp[0]);
+ }
+ else
+ dsp_add(voutlet_perform, 3, x, insig->s_vec, insig->s_n);
+}
+
+ /* set up epilog DSP code. If we're reblocking, this is the
+ time to copy the samples out to the containing object's outlets.
+ If we aren't reblocking, there's nothing to do here. */
+void voutlet_dspepilog(t_voutlet *x, t_signal **parentsigs,
+ int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock,
+ int switched)
+{
+ if (!x->x_buf) return; /* this shouldn't be necesssary... */
+ x->x_updown.downsample=downsample; x->x_updown.upsample=upsample; /* IOhannes */
+ if (reblock)
+ {
+ t_signal *insig, *outsig;
+ int parentvecsize, bufsize, oldbufsize;
+ int re_parentvecsize; /* IOhannes */
+ int bigperiod, epilogphase, blockphase;
+ if (parentsigs)
+ {
+ outsig = parentsigs[outlet_getsignalindex(x->x_parentoutlet)];
+ parentvecsize = outsig->s_n;
+ re_parentvecsize = parentvecsize * upsample / downsample;
+ }
+ else
+ {
+ outsig = 0;
+ parentvecsize = 1;
+ re_parentvecsize = 1;
+ }
+ // bigperiod = (downsample * myvecsize)/(upsample * parentvecsize); /* IOhannes */
+ bigperiod = myvecsize/re_parentvecsize; /* IOhannes */
+ if (!bigperiod) bigperiod = 1;
+ epilogphase = phase & (bigperiod - 1);
+ blockphase = (phase + period - 1) & (bigperiod - 1) & (- period);
+ // bufsize = parentvecsize * upsample; /* IOhannes */
+ bufsize = re_parentvecsize; /* IOhannes */
+ if (bufsize < myvecsize) bufsize = myvecsize;
+ if (bufsize != (oldbufsize = x->x_bufsize))
+ {
+ t_float *buf = x->x_buf;
+ t_freebytes(buf, oldbufsize * sizeof(*buf));
+ buf = (t_float *)t_getbytes(bufsize * sizeof(*buf));
+ memset((char *)buf, 0, bufsize * sizeof(*buf));
+ x->x_bufsize = bufsize;
+ x->x_endbuf = buf + bufsize;
+ x->x_buf = buf;
+ }
+ /* IOhannes: { */
+ if (re_parentvecsize * period > bufsize) bug("voutlet_dspepilog");
+ x->x_write = x->x_buf + re_parentvecsize * blockphase;
+ if (x->x_write == x->x_endbuf) x->x_write = x->x_buf;
+ if (period == 1 && frequency > 1)
+ x->x_hop = re_parentvecsize / frequency;
+ else x->x_hop = period * re_parentvecsize;
+ /* } IOhannes */
+ /* post("phase %d, block %d, parent %d", phase & 63,
+ parentvecsize * blockphase, parentvecsize * epilogphase); */
+ if (parentsigs)
+ {
+ /* set epilog pointer and schedule it */
+ /* IOhannes { */
+ x->x_empty = x->x_buf + re_parentvecsize * epilogphase;
+ if (upsample * downsample == 1)
+ dsp_add(voutlet_doepilog, 3, x, outsig->s_vec, re_parentvecsize);
+ else {
+ dsp_add(voutlet_doepilog_resampling, 2, x, re_parentvecsize);
+ resampleto_dsp(&x->x_updown, outsig->s_vec, re_parentvecsize, parentvecsize, x->x_updown.method);
+ }
+ /* } IOhannes */
+ }
+ }
+ /* if we aren't blocked but we are switched, the epilog code just
+ copies zeros to the output. In this case the blocking code actually
+ jumps over the epilog if the block is running. */
+ else if (switched)
+ {
+ if (parentsigs)
+ {
+ t_signal *outsig =
+ parentsigs[outlet_getsignalindex(x->x_parentoutlet)];
+ dsp_add_zero(outsig->s_vec, outsig->s_n);
+ }
+ }
+}
+
+static void *voutlet_newsig(t_symbol *s)
+{
+ t_voutlet *x = (t_voutlet *)pd_new(voutlet_class);
+ x->x_canvas = canvas_getcurrent();
+ x->x_parentoutlet = canvas_addoutlet(x->x_canvas,
+ &x->x_obj.ob_pd, &s_signal);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ x->x_endbuf = x->x_buf = (t_float *)getbytes(0);
+ x->x_bufsize = 0;
+
+ resample_init(&x->x_updown);
+
+ /* this should be though over:
+ * it might prove hard to provide consistency between labeled up- & downsampling methods
+ * maybe indeces would be better...
+ *
+ * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !)
+ */
+ if (s == gensym("hold"))x->x_updown.method=1; /* up: sample and hold */
+ else if (s == gensym("lin"))x->x_updown.method=2; /* up: linear interpolation */
+ else if (s == gensym("linear"))x->x_updown.method=2; /* up: linear interpolation */
+ else x->x_updown.method=0; /* up: zero-padding; down: ignore samples inbetween */
+
+ return (x);
+}
+
+
+static void voutlet_setup(void)
+{
+ voutlet_class = class_new(gensym("outlet"), (t_newmethod)voutlet_new,
+ (t_method)voutlet_free, sizeof(t_voutlet), CLASS_NOINLET, A_DEFSYM, 0);
+ class_addcreator((t_newmethod)voutlet_newsig, gensym("outlet~"), A_DEFSYM, 0);
+ class_addbang(voutlet_class, voutlet_bang);
+ class_addpointer(voutlet_class, voutlet_pointer);
+ class_addfloat(voutlet_class, (t_method)voutlet_float);
+ class_addsymbol(voutlet_class, voutlet_symbol);
+ class_addlist(voutlet_class, voutlet_list);
+ class_addanything(voutlet_class, voutlet_anything);
+ class_addmethod(voutlet_class, (t_method)voutlet_dsp, gensym("dsp"), 0);
+ class_sethelpsymbol(voutlet_class, gensym("pd"));
+}
+
+
+/* ---------------------------- overall setup ----------------------------- */
+
+void g_io_setup(void)
+{
+ vinlet_setup();
+ voutlet_setup();
+}
diff --git a/pd/src/g_mycanvas.c b/pd/src/g_mycanvas.c
new file mode 100644
index 00000000..edddb568
--- /dev/null
+++ b/pd/src/g_mycanvas.c
@@ -0,0 +1,439 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */
+/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include "t_tk.h"
+#include "g_all_guis.h"
+#include <math.h>
+
+#ifdef NT
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+/* ---------- cnv my gui-canvas for a window ---------------- */
+
+t_widgetbehavior my_canvas_widgetbehavior;
+static t_class *my_canvas_class;
+
+/* widget helper functions */
+
+void my_canvas_draw_new(t_my_canvas *x, t_glist *glist)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -outline #%6.6x -tags %xRECT\n",
+ canvas, xpos, ypos,
+ xpos + x->x_vis_w, ypos + x->x_vis_h,
+ x->x_gui.x_bcol, x->x_gui.x_bcol, x);
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -outline #%6.6x -tags %xBASE\n",
+ canvas, xpos, ypos,
+ xpos + x->x_gui.x_w, ypos + x->x_gui.x_h,
+ x->x_gui.x_bcol, x);
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n",
+ canvas, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"",
+ x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x);
+}
+
+void my_canvas_draw_move(t_my_canvas *x, t_glist *glist)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c coords %xRECT %d %d %d %d\n",
+ canvas, x, xpos, ypos, xpos + x->x_vis_w,
+ ypos + x->x_vis_h);
+ sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n",
+ canvas, x, xpos, ypos,
+ xpos + x->x_gui.x_w, ypos + x->x_gui.x_h);
+ sys_vgui(".x%x.c coords %xLABEL %d %d\n",
+ canvas, x, xpos+x->x_gui.x_ldx,
+ ypos+x->x_gui.x_ldy);
+}
+
+void my_canvas_draw_erase(t_my_canvas* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c delete %xBASE\n", canvas, x);
+ sys_vgui(".x%x.c delete %xRECT\n", canvas, x);
+ sys_vgui(".x%x.c delete %xLABEL\n", canvas, x);
+}
+
+void my_canvas_draw_config(t_my_canvas* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c itemconfigure %xRECT -fill #%6.6x -outline #%6.6x\n", canvas, x,
+ x->x_gui.x_bcol, x->x_gui.x_bcol);
+ sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x,
+ x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_bcol);
+ sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n",
+ canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"");
+}
+
+void my_canvas_draw_select(t_my_canvas* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if(x->x_gui.x_fsf.x_selected)
+ {
+ pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ }
+ else
+ {
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, x->x_gui.x_bcol);
+ }
+}
+
+void my_canvas_draw(t_my_canvas *x, t_glist *glist, int mode)
+{
+ if(mode == IEM_GUI_DRAW_MODE_MOVE)
+ my_canvas_draw_move(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_NEW)
+ my_canvas_draw_new(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_SELECT)
+ my_canvas_draw_select(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_ERASE)
+ my_canvas_draw_erase(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_CONFIG)
+ my_canvas_draw_config(x, glist);
+}
+
+/* ------------------------ cnv widgetbehaviour----------------------------- */
+
+static void my_canvas_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_my_canvas *x = (t_my_canvas *)z;
+
+ *xp1 = text_xpix(&x->x_gui.x_obj, glist);
+ *yp1 = text_ypix(&x->x_gui.x_obj, glist);
+ *xp2 = *xp1 + x->x_gui.x_w;
+ *yp2 = *yp1 + x->x_gui.x_h;
+}
+
+static void my_canvas_save(t_gobj *z, t_binbuf *b)
+{
+ t_my_canvas *x = (t_my_canvas *)z;
+ int bflcol[3], *ip1, *ip2;
+ t_symbol *srl[3];
+
+ iemgui_save(&x->x_gui, srl, bflcol);
+ ip1 = (int *)(&x->x_gui.x_isa);
+ ip2 = (int *)(&x->x_gui.x_fsf);
+ binbuf_addv(b, "ssiisiiisssiiiiiii", gensym("#X"),gensym("obj"),
+ (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix,
+ gensym("cnv"), x->x_gui.x_w, x->x_vis_w, x->x_vis_h,
+ srl[0], srl[1], srl[2], x->x_gui.x_ldx, x->x_gui.x_ldy,
+ (*ip2)&IEM_FSTYLE_FLAGS_ALL, x->x_gui.x_fontsize,
+ bflcol[0], bflcol[2], (*ip1)&IEM_INIT_ARGS_ALL);
+ binbuf_addv(b, ";");
+}
+
+static void my_canvas_properties(t_gobj *z, t_glist *owner)
+{
+ t_my_canvas *x = (t_my_canvas *)z;
+ char buf[800];
+ t_symbol *srl[3];
+
+ iemgui_properties(&x->x_gui, srl);
+ sprintf(buf, "pdtk_iemgui_dialog %%s MY_CANVAS \
+ ------selectable_dimensions(pix):------ %d %d size: 0.0 0.0 empty \
+ ------visible_rectangle(pix)(pix):------ %d width: %d height: %d \
+ %d empty empty %d %d empty %d \
+ %s %s \
+ %s %d %d \
+ %d %d \
+ %d %d %d\n",
+ x->x_gui.x_w, 1,
+ x->x_vis_w, x->x_vis_h, 0,/*no_schedule*/
+ -1, -1, -1, -1,/*no linlog, no init, no multi*/
+ srl[0]->s_name, srl[1]->s_name,
+ srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy,
+ x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize,
+ 0xffffff & x->x_gui.x_bcol, -1/*no frontcolor*/, 0xffffff & x->x_gui.x_lcol);
+ gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf);
+}
+
+static void my_canvas_get_pos(t_my_canvas *x)
+{
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ {
+ x->x_at[0].a_w.w_float = text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist);
+ x->x_at[1].a_w.w_float = text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist);
+ pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);
+ }
+}
+
+static void my_canvas_dialog(t_my_canvas *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *srl[3];
+ int a = (int)atom_getintarg(0, argc, argv);
+ int w = (int)atom_getintarg(2, argc, argv);
+ int h = (int)atom_getintarg(3, argc, argv);
+ int sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);
+
+ x->x_gui.x_isa.x_loadinit = 0;
+ if(a < 1)
+ a = 1;
+ x->x_gui.x_w = a;
+ x->x_gui.x_h = x->x_gui.x_w;
+ if(w < 1)
+ w = 1;
+ x->x_vis_w = w;
+ if(h < 1)
+ h = 1;
+ x->x_vis_h = h;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+}
+
+static void my_canvas_size(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)
+{
+ int i = (int)atom_getintarg(0, ac, av);
+
+ if(i < 1)
+ i = 1;
+ x->x_gui.x_w = i;
+ x->x_gui.x_h = i;
+ iemgui_size((void *)x, &x->x_gui);
+}
+
+static void my_canvas_delta(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}
+
+static void my_canvas_pos(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void my_canvas_vis_size(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)
+{
+ int i;
+
+ i = (int)atom_getintarg(0, ac, av);
+ if(i < 1)
+ i = 1;
+ x->x_vis_w = i;
+ if(ac > 1)
+ {
+ i = (int)atom_getintarg(1, ac, av);
+ if(i < 1)
+ i = 1;
+ }
+ x->x_vis_h = i;
+ if(glist_isvisible(x->x_gui.x_glist))
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+}
+
+static void my_canvas_color(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_color((void *)x, &x->x_gui, s, ac, av);}
+
+static void my_canvas_send(t_my_canvas *x, t_symbol *s)
+{iemgui_send(x, &x->x_gui, s);}
+
+static void my_canvas_receive(t_my_canvas *x, t_symbol *s)
+{iemgui_receive(x, &x->x_gui, s);}
+
+static void my_canvas_label(t_my_canvas *x, t_symbol *s)
+{iemgui_label((void *)x, &x->x_gui, s);}
+
+static void my_canvas_label_pos(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void my_canvas_label_font(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);}
+
+static void my_canvas_list(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)
+{
+ int l=iemgui_list((void *)x, &x->x_gui, s, ac, av);
+
+ /*if(l < 0)
+ {
+ post("error: my_canvas: no method for 'list'");
+ }
+ else */if(l > 0)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+ }
+}
+
+static void *my_canvas_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_my_canvas *x = (t_my_canvas *)pd_new(my_canvas_class);
+ int bflcol[]={-233017, -1, -66577};
+ t_symbol *srl[3];
+ int a=IEM_GUI_DEFAULTSIZE, w=100, h=60;
+ int ldx=20, ldy=12, f=2, i=0;
+ int fs=14, iinit=0, ifstyle=0;
+ t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit);
+ t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle);
+ char str[144];
+
+ srl[0] = gensym("empty");
+ srl[1] = gensym("empty");
+ srl[2] = gensym("empty");
+
+ if(((argc >= 10)&&(argc <= 13))
+ &&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2))
+ {
+ a = (int)atom_getintarg(0, argc, argv);
+ w = (int)atom_getintarg(1, argc, argv);
+ h = (int)atom_getintarg(2, argc, argv);
+ }
+ if((argc >= 12)&&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3))&&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4)))
+ {
+ i = 2;
+ if(IS_A_SYMBOL(argv,3))
+ srl[0] = atom_getsymbolarg(3, argc, argv);
+ else if(IS_A_FLOAT(argv,3))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(3, argc, argv));
+ srl[0] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,4))
+ srl[1] = atom_getsymbolarg(4, argc, argv);
+ else if(IS_A_FLOAT(argv,4))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(4, argc, argv));
+ srl[1] = gensym(str);
+ }
+ }
+ else if((argc == 11)&&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3)))
+ {
+ i = 1;
+ if(IS_A_SYMBOL(argv,3))
+ srl[1] = atom_getsymbolarg(3, argc, argv);
+ else if(IS_A_FLOAT(argv,3))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(3, argc, argv));
+ srl[1] = gensym(str);
+ }
+ }
+
+ if(((argc >= 10)&&(argc <= 13))
+ &&(IS_A_SYMBOL(argv,i+3)||IS_A_FLOAT(argv,i+3))&&IS_A_FLOAT(argv,i+4)
+ &&IS_A_FLOAT(argv,i+5)&&IS_A_FLOAT(argv,i+6)
+ &&IS_A_FLOAT(argv,i+7)&&IS_A_FLOAT(argv,i+8)
+ &&IS_A_FLOAT(argv,i+9))
+ {
+ if(IS_A_SYMBOL(argv,i+3))
+ srl[2] = atom_getsymbolarg(i+3, argc, argv);
+ else if(IS_A_FLOAT(argv,i+3))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(i+3, argc, argv));
+ srl[2] = gensym(str);
+ }
+ ldx = (int)atom_getintarg(i+4, argc, argv);
+ ldy = (int)atom_getintarg(i+5, argc, argv);
+ ifstyle = (int)atom_getintarg(i+6, argc, argv);
+ fs = (int)atom_getintarg(i+7, argc, argv);
+ bflcol[0] = (int)atom_getintarg(i+8, argc, argv);
+ bflcol[2] = (int)atom_getintarg(i+9, argc, argv);
+ }
+ if((argc == 13)&&IS_A_FLOAT(argv,i+10))
+ {
+ iinit = (int)(atom_getintarg(i+10, argc, argv));
+ }
+ x->x_gui.x_draw = (t_iemfunptr)my_canvas_draw;
+ iinit &= IEM_INIT_ARGS_ALL;
+ ifstyle &= IEM_FSTYLE_FLAGS_ALL;
+ fstyle->x_snd_able = 1;
+ fstyle->x_rcv_able = 1;
+ x->x_gui.x_glist = (t_glist *)canvas_getcurrent();
+ x->x_gui.x_isa = *init;
+ if(!strcmp(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0;
+ if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0;
+ x->x_gui.x_unique_num = 0;
+ if(a < 1)
+ a = 1;
+ x->x_gui.x_w = a;
+ x->x_gui.x_h = x->x_gui.x_w;
+ if(w < 1)
+ w = 1;
+ x->x_vis_w = w;
+ if(h < 1)
+ h = 1;
+ x->x_vis_h = h;
+ if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica");
+ else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times");
+ else { fstyle->x_font_style = 0;
+ strcpy(x->x_gui.x_font, "courier"); }
+ x->x_gui.x_fsf = *fstyle;
+ iemgui_first_dollararg2sym(&x->x_gui, srl);
+ if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]);
+ x->x_gui.x_snd = srl[0];
+ x->x_gui.x_rcv = srl[1];
+ x->x_gui.x_lab = srl[2];
+ x->x_gui.x_ldx = ldx;
+ x->x_gui.x_ldy = ldy;
+ if(fs < 4)
+ fs = 4;
+ x->x_gui.x_fontsize = fs;
+ iemgui_all_colfromload(&x->x_gui, bflcol);
+ x->x_at[0].a_type = A_FLOAT;
+ x->x_at[1].a_type = A_FLOAT;
+ iemgui_verify_snd_ne_rcv(&x->x_gui);
+ return (x);
+}
+
+static void my_canvas_ff(t_my_canvas *x)
+{
+ if(x->x_gui.x_fsf.x_selected)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ if(x->x_gui.x_fsf.x_rcv_able)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);
+ gfxstub_deleteforkey(x);
+}
+
+void g_mycanvas_setup(void)
+{
+ my_canvas_class = class_new(gensym("cnv"), (t_newmethod)my_canvas_new,
+ (t_method)my_canvas_ff, sizeof(t_my_canvas), CLASS_NOINLET, A_GIMME, 0);
+ class_addcreator((t_newmethod)my_canvas_new, gensym("my_canvas"), A_GIMME, 0);
+ class_addlist(my_canvas_class, my_canvas_list);
+ class_addmethod(my_canvas_class, (t_method)my_canvas_dialog, gensym("dialog"), A_GIMME, 0);
+ class_addmethod(my_canvas_class, (t_method)my_canvas_size, gensym("size"), A_GIMME, 0);
+ class_addmethod(my_canvas_class, (t_method)my_canvas_delta, gensym("delta"), A_GIMME, 0);
+ class_addmethod(my_canvas_class, (t_method)my_canvas_pos, gensym("pos"), A_GIMME, 0);
+ class_addmethod(my_canvas_class, (t_method)my_canvas_vis_size, gensym("vis_size"), A_GIMME, 0);
+ class_addmethod(my_canvas_class, (t_method)my_canvas_color, gensym("color"), A_GIMME, 0);
+ class_addmethod(my_canvas_class, (t_method)my_canvas_send, gensym("send"), A_DEFSYM, 0);
+ class_addmethod(my_canvas_class, (t_method)my_canvas_receive, gensym("receive"), A_DEFSYM, 0);
+ class_addmethod(my_canvas_class, (t_method)my_canvas_label, gensym("label"), A_DEFSYM, 0);
+ class_addmethod(my_canvas_class, (t_method)my_canvas_label_pos, gensym("label_pos"), A_GIMME, 0);
+ class_addmethod(my_canvas_class, (t_method)my_canvas_label_font, gensym("label_font"), A_GIMME, 0);
+ class_addmethod(my_canvas_class, (t_method)my_canvas_get_pos, gensym("get_pos"), 0);
+
+ if(!iemgui_key_sym)
+ iemgui_key_sym = gensym("#keyname");
+ my_canvas_widgetbehavior.w_getrectfn = my_canvas_getrect;
+ my_canvas_widgetbehavior.w_displacefn = iemgui_displace;
+ my_canvas_widgetbehavior.w_selectfn = iemgui_select;
+ my_canvas_widgetbehavior.w_activatefn = NULL;
+ my_canvas_widgetbehavior.w_deletefn = iemgui_delete;
+ my_canvas_widgetbehavior.w_visfn = iemgui_vis;
+ my_canvas_widgetbehavior.w_clickfn = NULL;
+ my_canvas_widgetbehavior.w_propertiesfn = my_canvas_properties;
+ my_canvas_widgetbehavior.w_savefn = my_canvas_save;
+ class_setwidget(my_canvas_class, &my_canvas_widgetbehavior);
+ class_sethelpsymbol(my_canvas_class, gensym("my_canvas"));
+}
diff --git a/pd/src/g_numbox.c b/pd/src/g_numbox.c
new file mode 100644
index 00000000..e0967b5b
--- /dev/null
+++ b/pd/src/g_numbox.c
@@ -0,0 +1,979 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* my_numbox.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include "t_tk.h"
+#include "g_all_guis.h"
+#include <math.h>
+
+#ifdef NT
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+/*------------------ global varaibles -------------------------*/
+
+
+/*------------------ global functions -------------------------*/
+
+
+/* ------------ nmx gui-my number box ----------------------- */
+
+t_widgetbehavior my_numbox_widgetbehavior;
+static t_class *my_numbox_class;
+
+/* widget helper functions */
+
+static void my_numbox_tick_reset(t_my_numbox *x)
+{
+ if(x->x_gui.x_fsf.x_change)
+ {
+ x->x_gui.x_fsf.x_change = 0;
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ }
+}
+
+static void my_numbox_tick_wait(t_my_numbox *x)
+{
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+}
+
+void my_numbox_clip(t_my_numbox *x)
+{
+ if(x->x_val < x->x_min)
+ x->x_val = x->x_min;
+ if(x->x_val > x->x_max)
+ x->x_val = x->x_max;
+}
+
+void my_numbox_calc_fontwidth(t_my_numbox *x)
+{
+ int w, f=31;
+
+ if(x->x_gui.x_fsf.x_font_style == 1)
+ f = 27;
+ else if(x->x_gui.x_fsf.x_font_style == 2)
+ f = 25;
+
+ w = x->x_gui.x_fontsize * f * x->x_gui.x_w;
+ w /= 36;
+ x->x_numwidth = w + (x->x_gui.x_h / 2) + 4;
+}
+
+void my_numbox_ftoa(t_my_numbox *x)
+{
+ double f=x->x_val;
+ int bufsize, is_exp=0, i, idecimal;
+
+ sprintf(x->x_buf, "%g", f);
+ bufsize = strlen(x->x_buf);
+ if(bufsize >= 5)/* if it is in exponential mode */
+ {
+ i = bufsize - 4;
+ if((x->x_buf[i] == 'e') || (x->x_buf[i] == 'E'))
+ is_exp = 1;
+ }
+ if(bufsize > x->x_gui.x_w)/* if to reduce */
+ {
+ if(is_exp)
+ {
+ if(x->x_gui.x_w <= 5)
+ {
+ x->x_buf[0] = (f < 0.0 ? '-' : '+');
+ x->x_buf[1] = 0;
+ }
+ i = bufsize - 4;
+ for(idecimal=0; idecimal < i; idecimal++)
+ if(x->x_buf[idecimal] == '.')
+ break;
+ if(idecimal > (x->x_gui.x_w - 4))
+ {
+ x->x_buf[0] = (f < 0.0 ? '-' : '+');
+ x->x_buf[1] = 0;
+ }
+ else
+ {
+ int new_exp_index=x->x_gui.x_w-4, old_exp_index=bufsize-4;
+
+ for(i=0; i < 4; i++, new_exp_index++, old_exp_index++)
+ x->x_buf[new_exp_index] = x->x_buf[old_exp_index];
+ x->x_buf[x->x_gui.x_w] = 0;
+ }
+
+ }
+ else
+ {
+ for(idecimal=0; idecimal < bufsize; idecimal++)
+ if(x->x_buf[idecimal] == '.')
+ break;
+ if(idecimal > x->x_gui.x_w)
+ {
+ x->x_buf[0] = (f < 0.0 ? '-' : '+');
+ x->x_buf[1] = 0;
+ }
+ else
+ x->x_buf[x->x_gui.x_w] = 0;
+ }
+ }
+}
+
+static void my_numbox_draw_update(t_my_numbox *x, t_glist *glist)
+{
+ if (glist_isvisible(glist))
+ {
+ if(x->x_gui.x_fsf.x_change)
+ {
+ if(x->x_buf[0])
+ {
+ char *cp=x->x_buf;
+ int sl = strlen(x->x_buf);
+
+ x->x_buf[sl] = '>';
+ x->x_buf[sl+1] = 0;
+ if(sl >= x->x_gui.x_w)
+ cp += sl - x->x_gui.x_w + 1;
+ sys_vgui(".x%x.c itemconfigure %xNUMBER -fill #%6.6x -text {%s} \n",
+ glist_getcanvas(glist), x, IEM_GUI_COLOR_EDITED, cp);
+ x->x_buf[sl] = 0;
+ }
+ else
+ {
+ my_numbox_ftoa(x);
+ sys_vgui(".x%x.c itemconfigure %xNUMBER -fill #%6.6x -text {%s} \n",
+ glist_getcanvas(glist), x, IEM_GUI_COLOR_EDITED, x->x_buf);
+ x->x_buf[0] = 0;
+ }
+ }
+ else
+ {
+ my_numbox_ftoa(x);
+ sys_vgui(".x%x.c itemconfigure %xNUMBER -fill #%6.6x -text {%s} \n",
+ glist_getcanvas(glist), x,
+ x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_fcol,
+ x->x_buf);
+ x->x_buf[0] = 0;
+ }
+ }
+}
+
+static void my_numbox_draw_new(t_my_numbox *x, t_glist *glist)
+{
+ int half=x->x_gui.x_h/2, d=1+x->x_gui.x_h/34;
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c create polygon %d %d %d %d %d %d %d %d %d %d -outline #%6.6x -fill #%6.6x -tags %xBASE1\n",
+ canvas, xpos, ypos,
+ xpos + x->x_numwidth-4, ypos,
+ xpos + x->x_numwidth, ypos+4,
+ xpos + x->x_numwidth, ypos + x->x_gui.x_h,
+ xpos, ypos + x->x_gui.x_h,
+ IEM_GUI_COLOR_NORMAL, x->x_gui.x_bcol, x);
+ sys_vgui(".x%x.c create line %d %d %d %d %d %d -fill #%6.6x -tags %xBASE2\n",
+ canvas, xpos, ypos,
+ xpos + half, ypos + half,
+ xpos, ypos + x->x_gui.x_h,
+ x->x_gui.x_fcol, x);
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n",
+ canvas, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"",
+ x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x);
+ my_numbox_ftoa(x);
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xNUMBER\n",
+ canvas, xpos+half+2, ypos+half+d,
+ x->x_buf, x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_fcol, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas,
+ xpos, ypos + x->x_gui.x_h-1,
+ xpos+IOWIDTH, ypos + x->x_gui.x_h,
+ x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas,
+ xpos, ypos,
+ xpos+IOWIDTH, ypos+1,
+ x, 0);
+}
+
+static void my_numbox_draw_move(t_my_numbox *x, t_glist *glist)
+{
+ int half = x->x_gui.x_h/2, d=1+x->x_gui.x_h/34;
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c coords %xBASE1 %d %d %d %d %d %d %d %d %d %d\n",
+ canvas, x, xpos, ypos,
+ xpos + x->x_numwidth-4, ypos,
+ xpos + x->x_numwidth, ypos+4,
+ xpos + x->x_numwidth, ypos + x->x_gui.x_h,
+ xpos, ypos + x->x_gui.x_h);
+ sys_vgui(".x%x.c coords %xBASE2 %d %d %d %d %d %d\n",
+ canvas, x, xpos, ypos,
+ xpos + half, ypos + half,
+ xpos, ypos + x->x_gui.x_h);
+ sys_vgui(".x%x.c coords %xLABEL %d %d\n",
+ canvas, x, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy);
+ sys_vgui(".x%x.c coords %xNUMBER %d %d\n",
+ canvas, x, xpos+half+2, ypos+half+d);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n",
+ canvas, x, 0,
+ xpos, ypos + x->x_gui.x_h-1,
+ xpos+IOWIDTH, ypos + x->x_gui.x_h);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n",
+ canvas, x, 0,
+ xpos, ypos,
+ xpos+IOWIDTH, ypos+1);
+}
+
+static void my_numbox_draw_erase(t_my_numbox* x,t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c delete %xBASE1\n", canvas, x);
+ sys_vgui(".x%x.c delete %xBASE2\n", canvas, x);
+ sys_vgui(".x%x.c delete %xLABEL\n", canvas, x);
+ sys_vgui(".x%x.c delete %xNUMBER\n", canvas, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+static void my_numbox_draw_config(t_my_numbox* x,t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n",
+ canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"");
+ sys_vgui(".x%x.c itemconfigure %xNUMBER -font {%s %d bold} -fill #%6.6x \n",
+ canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_fcol);
+ sys_vgui(".x%x.c itemconfigure %xBASE1 -fill #%6.6x\n", canvas,
+ x, x->x_gui.x_bcol);
+ sys_vgui(".x%x.c itemconfigure %xBASE2 -fill #%6.6x\n", canvas,
+ x, x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_fcol);
+}
+
+static void my_numbox_draw_io(t_my_numbox* x,t_glist* glist, int old_snd_rcv_flags)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas,
+ xpos, ypos + x->x_gui.x_h-1,
+ xpos+IOWIDTH, ypos + x->x_gui.x_h,
+ x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas,
+ xpos, ypos,
+ xpos+IOWIDTH, ypos+1,
+ x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+static void my_numbox_draw_select(t_my_numbox *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if(x->x_gui.x_fsf.x_selected)
+ {
+ if(x->x_gui.x_fsf.x_change)
+ {
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ x->x_gui.x_fsf.x_change = 0;
+ clock_unset(x->x_clock_reset);
+ x->x_buf[0] = 0;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ }
+ pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE1 -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ sys_vgui(".x%x.c itemconfigure %xBASE2 -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ sys_vgui(".x%x.c itemconfigure %xNUMBER -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ }
+ else
+ {
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE1 -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL);
+ sys_vgui(".x%x.c itemconfigure %xBASE2 -fill #%6.6x\n", canvas, x, x->x_gui.x_fcol);
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol);
+ sys_vgui(".x%x.c itemconfigure %xNUMBER -fill #%6.6x\n", canvas, x, x->x_gui.x_fcol);
+ }
+}
+
+void my_numbox_draw(t_my_numbox *x, t_glist *glist, int mode)
+{
+ if(mode == IEM_GUI_DRAW_MODE_UPDATE)
+ my_numbox_draw_update(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_MOVE)
+ my_numbox_draw_move(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_NEW)
+ my_numbox_draw_new(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_SELECT)
+ my_numbox_draw_select(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_ERASE)
+ my_numbox_draw_erase(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_CONFIG)
+ my_numbox_draw_config(x, glist);
+ else if(mode >= IEM_GUI_DRAW_MODE_IO)
+ my_numbox_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO);
+}
+
+/* ------------------------ nbx widgetbehaviour----------------------------- */
+
+
+static void my_numbox_getrect(t_gobj *z, t_glist *glist,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_my_numbox* x = (t_my_numbox*)z;
+
+ *xp1 = text_xpix(&x->x_gui.x_obj, glist);
+ *yp1 = text_ypix(&x->x_gui.x_obj, glist);
+ *xp2 = *xp1 + x->x_numwidth;
+ *yp2 = *yp1 + x->x_gui.x_h;
+}
+
+static void my_numbox_save(t_gobj *z, t_binbuf *b)
+{
+ t_my_numbox *x = (t_my_numbox *)z;
+ int bflcol[3], *ip1, *ip2;
+ t_symbol *srl[3];
+
+ iemgui_save(&x->x_gui, srl, bflcol);
+ if(x->x_gui.x_fsf.x_change)
+ {
+ x->x_gui.x_fsf.x_change = 0;
+ clock_unset(x->x_clock_reset);
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+
+ }
+ ip1 = (int *)(&x->x_gui.x_isa);
+ ip2 = (int *)(&x->x_gui.x_fsf);
+ binbuf_addv(b, "ssiisiiffiisssiiiiiiifi", gensym("#X"),gensym("obj"),
+ (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix,
+ gensym("nbx"), x->x_gui.x_w, x->x_gui.x_h,
+ (float)x->x_min, (float)x->x_max,
+ x->x_lin0_log1, (*ip1)&IEM_INIT_ARGS_ALL,
+ srl[0], srl[1], srl[2],
+ x->x_gui.x_ldx, x->x_gui.x_ldy,
+ (*ip2)&IEM_FSTYLE_FLAGS_ALL, x->x_gui.x_fontsize,
+ bflcol[0], bflcol[1], bflcol[2],
+ x->x_val, x->x_log_height);
+ binbuf_addv(b, ";");
+}
+
+int my_numbox_check_minmax(t_my_numbox *x, double min, double max)
+{
+ int ret=0;
+
+ if(x->x_lin0_log1)
+ {
+ if((min == 0.0)&&(max == 0.0))
+ max = 1.0;
+ if(max > 0.0)
+ {
+ if(min <= 0.0)
+ min = 0.01*max;
+ }
+ else
+ {
+ if(min > 0.0)
+ max = 0.01*min;
+ }
+ }
+ x->x_min = min;
+ x->x_max = max;
+ if(x->x_val < x->x_min)
+ {
+ x->x_val = x->x_min;
+ ret = 1;
+ }
+ if(x->x_val > x->x_max)
+ {
+ x->x_val = x->x_max;
+ ret = 1;
+ }
+ if(x->x_lin0_log1)
+ x->x_k = exp(log(x->x_max/x->x_min)/(double)(x->x_log_height));
+ else
+ x->x_k = 1.0;
+ return(ret);
+}
+
+static void my_numbox_properties(t_gobj *z, t_glist *owner)
+{
+ t_my_numbox *x = (t_my_numbox *)z;
+ char buf[800];
+ t_symbol *srl[3];
+
+ iemgui_properties(&x->x_gui, srl);
+ if(x->x_gui.x_fsf.x_change)
+ {
+ x->x_gui.x_fsf.x_change = 0;
+ clock_unset(x->x_clock_reset);
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+
+ }
+ sprintf(buf, "pdtk_iemgui_dialog %%s NUMBERBOX \
+ -------dimensions(digits)(pix):------- %d %d width: %d %d height: \
+ -----------output-range:----------- %g min: %g max: %d \
+ %d lin log %d %d log-height: %d \
+ %s %s \
+ %s %d %d \
+ %d %d \
+ %d %d %d\n",
+ x->x_gui.x_w, 1, x->x_gui.x_h, 8,
+ x->x_min, x->x_max, 0,/*no_schedule*/
+ x->x_lin0_log1, x->x_gui.x_isa.x_loadinit, -1, x->x_log_height,/*no multi, but iem-characteristic*/
+ srl[0]->s_name, srl[1]->s_name,
+ srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy,
+ x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize,
+ 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol);
+ gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf);
+}
+
+static void my_numbox_bang(t_my_numbox *x)
+{
+ outlet_float(x->x_gui.x_obj.ob_outlet, x->x_val);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_float(x->x_gui.x_snd->s_thing, x->x_val);
+}
+
+static void my_numbox_dialog(t_my_numbox *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *srl[3];
+ int w = (int)atom_getintarg(0, argc, argv);
+ int h = (int)atom_getintarg(1, argc, argv);
+ double min = (double)atom_getfloatarg(2, argc, argv);
+ double max = (double)atom_getfloatarg(3, argc, argv);
+ int lilo = (int)atom_getintarg(4, argc, argv);
+ int log_height = (int)atom_getintarg(6, argc, argv);
+ int sr_flags;
+
+ if(lilo != 0) lilo = 1;
+ x->x_lin0_log1 = lilo;
+ sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);
+ if(w < 1)
+ w = 1;
+ x->x_gui.x_w = w;
+ if(h < 8)
+ h = 8;
+ x->x_gui.x_h = h;
+ if(log_height < 10)
+ log_height = 10;
+ x->x_log_height = log_height;
+ my_numbox_calc_fontwidth(x);
+ /*if(my_numbox_check_minmax(x, min, max))
+ my_numbox_bang(x);*/
+ my_numbox_check_minmax(x, min, max);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+}
+
+static void my_numbox_motion(t_my_numbox *x, t_floatarg dx, t_floatarg dy)
+{
+ double k2=1.0;
+
+ if(x->x_gui.x_fsf.x_finemoved)
+ k2 = 0.01;
+ if(x->x_lin0_log1)
+ x->x_val *= pow(x->x_k, -k2*dy);
+ else
+ x->x_val -= k2*dy;
+ my_numbox_clip(x);
+ if(x->x_gui.x_fsf.x_change)
+ {
+ x->x_gui.x_fsf.x_change = 0;
+ clock_unset(x->x_clock_reset);
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+
+ }
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ my_numbox_bang(x);
+}
+
+static void my_numbox_click(t_my_numbox *x, t_floatarg xpos, t_floatarg ypos,
+ t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
+{
+ glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.te_g, (t_glistmotionfn)my_numbox_motion,
+ 0, xpos, ypos);
+}
+
+static int my_numbox_newclick(t_gobj *z, struct _glist *glist,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ t_my_numbox* x = (t_my_numbox *)z;
+
+ if(doit)
+ {
+ my_numbox_click( x, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift,
+ 0, (t_floatarg)alt);
+ if(shift)
+ x->x_gui.x_fsf.x_finemoved = 1;
+ else
+ x->x_gui.x_fsf.x_finemoved = 0;
+ if(!x->x_gui.x_fsf.x_change)
+ {
+ clock_delay(x->x_clock_wait, 50);
+ x->x_gui.x_fsf.x_change = 1;
+ clock_delay(x->x_clock_reset, 3000);
+ pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ x->x_buf[0] = 0;
+ }
+ else
+ {
+ x->x_gui.x_fsf.x_change = 0;
+ clock_unset(x->x_clock_reset);
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ x->x_buf[0] = 0;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ }
+ }
+ return (1);
+}
+
+static void my_numbox_set(t_my_numbox *x, t_floatarg f)
+{
+ x->x_val = f;
+ my_numbox_clip(x);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+}
+
+static void my_numbox_log_height(t_my_numbox *x, t_floatarg lh)
+{
+ if(lh < 10.0)
+ lh = 10.0;
+ x->x_log_height = (int)lh;
+ if(x->x_lin0_log1)
+ x->x_k = exp(log(x->x_max/x->x_min)/(double)(x->x_log_height));
+ else
+ x->x_k = 1.0;
+
+}
+
+static void my_numbox_float(t_my_numbox *x, t_floatarg f)
+{
+ my_numbox_set(x, f);
+ if(x->x_gui.x_fsf.x_put_in2out)
+ my_numbox_bang(x);
+}
+
+static void my_numbox_size(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)
+{
+ int h, w;
+
+ w = (int)atom_getintarg(0, ac, av);
+ if(w < 1)
+ w = 1;
+ x->x_gui.x_w = w;
+ if(ac > 1)
+ {
+ h = (int)atom_getintarg(1, ac, av);
+ if(h < 8)
+ h = 8;
+ x->x_gui.x_h = h;
+ }
+ my_numbox_calc_fontwidth(x);
+ iemgui_size((void *)x, &x->x_gui);
+}
+
+static void my_numbox_delta(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}
+
+static void my_numbox_pos(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void my_numbox_range(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)
+{
+ if(my_numbox_check_minmax(x, (double)atom_getfloatarg(0, ac, av),
+ (double)atom_getfloatarg(1, ac, av)))
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ /*my_numbox_bang(x);*/
+ }
+}
+
+static void my_numbox_color(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_color((void *)x, &x->x_gui, s, ac, av);}
+
+static void my_numbox_send(t_my_numbox *x, t_symbol *s)
+{iemgui_send(x, &x->x_gui, s);}
+
+static void my_numbox_receive(t_my_numbox *x, t_symbol *s)
+{iemgui_receive(x, &x->x_gui, s);}
+
+static void my_numbox_label(t_my_numbox *x, t_symbol *s)
+{iemgui_label((void *)x, &x->x_gui, s);}
+
+static void my_numbox_label_pos(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void my_numbox_label_font(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)
+{
+ int f = (int)atom_getintarg(1, ac, av);
+
+ if(f < 4)
+ f = 4;
+ x->x_gui.x_fontsize = f;
+ f = (int)atom_getintarg(0, ac, av);
+ if((f < 0) || (f > 2))
+ f = 0;
+ x->x_gui.x_fsf.x_font_style = f;
+ my_numbox_calc_fontwidth(x);
+ iemgui_label_font((void *)x, &x->x_gui, s, ac, av);
+}
+
+static void my_numbox_log(t_my_numbox *x)
+{
+ x->x_lin0_log1 = 1;
+ if(my_numbox_check_minmax(x, x->x_min, x->x_max))
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ /*my_numbox_bang(x);*/
+ }
+}
+
+static void my_numbox_lin(t_my_numbox *x)
+{
+ x->x_lin0_log1 = 0;
+}
+
+static void my_numbox_init(t_my_numbox *x, t_floatarg f)
+{
+ x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1;
+}
+
+static void my_numbox_loadbang(t_my_numbox *x)
+{
+ if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ my_numbox_bang(x);
+ }
+}
+
+static void my_numbox_list(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)
+{
+ int l=-1;
+
+ if(x->x_gui.x_fsf.x_selected)
+ {
+ if((ac == 2)&&IS_A_FLOAT(av,0)&&IS_A_SYMBOL(av,1))
+ {
+ t_symbol *key = atom_getsymbolarg(1, ac, av);
+ int keydown = atom_getintarg(0, ac, av);
+
+ if(keydown)
+ {
+ int refresh = 1,i,d=1;
+ static char buf[20];
+
+ buf[0] = 0;
+ if(!strcmp(key->s_name, "Shift_L")||!strcmp(key->s_name, "Shift_R"))
+ x->x_gui.x_fsf.x_shiftdown = 1;
+ else
+ {
+ if(x->x_gui.x_fsf.x_shiftdown)
+ d = 10;
+ if(!strcmp(key->s_name, "Up"))
+ x->x_gui.x_obj.te_ypix -= d;
+ else if(!strcmp(key->s_name, "Down"))
+ x->x_gui.x_obj.te_ypix += d;
+ else if(!strcmp(key->s_name, "Left"))
+ x->x_gui.x_obj.te_xpix -= d;
+ else if(!strcmp(key->s_name, "Right"))
+ x->x_gui.x_obj.te_xpix += d;
+ else
+ refresh = 0;
+ if(refresh)
+ l = 1;
+ }
+ l = 0;
+ }
+ else
+ {
+ if(!strcmp(key->s_name, "Shift_L")||!strcmp(key->s_name, "Shift_R"))
+ x->x_gui.x_fsf.x_shiftdown = 0;
+ l = 0;
+ }
+ }
+ }
+ else
+ {
+ if(x->x_gui.x_fsf.x_change)
+ {
+ if((ac == 2)&&IS_A_FLOAT(av,0)&&IS_A_SYMBOL(av,1))
+ {
+ t_symbol *key = atom_getsymbolarg(1, ac, av);
+ int keydown = atom_getintarg(0, ac, av);
+ char buf[3];
+
+ buf[1] = 0;
+ if(keydown)
+ {
+ char *c=key->s_name;
+
+ l = 0;
+ if(((*c>='0')&&(*c<='9'))||(*c=='.')||(*c=='-')||(*c=='e')||(*c=='+')||(*c=='E'))
+ {
+ if(strlen(x->x_buf) < (IEMGUI_MAX_NUM_LEN-2))
+ {
+ buf[0] = *c;
+ strcat(x->x_buf, buf);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ }
+ }
+ else if((*c=='\b')||(*c==127))
+ {
+ int sl=strlen(x->x_buf)-1;
+
+ if(sl < 0)
+ sl = 0;
+ x->x_buf[sl] = 0;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ }
+ else if((*c=='\n')||(*c==13))
+ {
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ x->x_val = atof(x->x_buf);
+ x->x_buf[0] = 0;
+ x->x_gui.x_fsf.x_change = 0;
+ clock_unset(x->x_clock_reset);
+ my_numbox_clip(x);
+ my_numbox_bang(x);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ }
+ clock_delay(x->x_clock_reset, 3000);
+ }
+ else
+ {
+ l = 0;
+ }
+ }
+ }
+ }
+ if(l < 0)
+ {
+ if(IS_A_FLOAT(av,0))
+ {
+ my_numbox_set(x, atom_getfloatarg(0, ac, av));
+ my_numbox_bang(x);
+ }
+ }
+ else if(l > 0)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+ }
+}
+
+static void *my_numbox_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_my_numbox *x = (t_my_numbox *)pd_new(my_numbox_class);
+ int bflcol[]={-262144, -1, -1};
+ t_symbol *srl[3];
+ int w=5, h=14;
+ int lilo=0, f=0, ldx=0, ldy=-6;
+ int fs=10, iinit=0, ifstyle=0;
+ int log_height=256;
+ double min=-1.0e+37, max=1.0e+37,v=0.0;
+ t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit);
+ t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle);
+ char str[144];
+
+ srl[0] = gensym("empty");
+ srl[1] = gensym("empty");
+ srl[2] = gensym("empty");
+
+
+ if((argc >= 17)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)
+ &&IS_A_FLOAT(argv,2)&&IS_A_FLOAT(argv,3)
+ &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5)
+ &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6))
+ &&(IS_A_SYMBOL(argv,7)||IS_A_FLOAT(argv,7))
+ &&(IS_A_SYMBOL(argv,8)||IS_A_FLOAT(argv,8))
+ &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)
+ &&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13)
+ &&IS_A_FLOAT(argv,14)&&IS_A_FLOAT(argv,15)&&IS_A_FLOAT(argv,16))
+ {
+ w = (int)atom_getintarg(0, argc, argv);
+ h = (int)atom_getintarg(1, argc, argv);
+ min = (double)atom_getfloatarg(2, argc, argv);
+ max = (double)atom_getfloatarg(3, argc, argv);
+ lilo = (int)atom_getintarg(4, argc, argv);
+ iinit = (int)atom_getintarg(5, argc, argv);
+ srl[0] = atom_getsymbolarg(6, argc, argv);
+ srl[1] = atom_getsymbolarg(7, argc, argv);
+ srl[2] = atom_getsymbolarg(8, argc, argv);
+ if(IS_A_SYMBOL(argv,6))
+ srl[0] = atom_getsymbolarg(6, argc, argv);
+ else if(IS_A_FLOAT(argv,6))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(6, argc, argv));
+ srl[0] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,7))
+ srl[1] = atom_getsymbolarg(7, argc, argv);
+ else if(IS_A_FLOAT(argv,7))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(7, argc, argv));
+ srl[1] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,8))
+ srl[2] = atom_getsymbolarg(8, argc, argv);
+ else if(IS_A_FLOAT(argv,8))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(8, argc, argv));
+ srl[2] = gensym(str);
+ }
+ ldx = (int)atom_getintarg(9, argc, argv);
+ ldy = (int)atom_getintarg(10, argc, argv);
+ ifstyle = (int)atom_getintarg(11, argc, argv);
+ fs = (int)atom_getintarg(12, argc, argv);
+ bflcol[0] = (int)atom_getintarg(13, argc, argv);
+ bflcol[1] = (int)atom_getintarg(14, argc, argv);
+ bflcol[2] = (int)atom_getintarg(15, argc, argv);
+ v = atom_getfloatarg(16, argc, argv);
+ }
+ if((argc == 18)&&IS_A_FLOAT(argv,17))
+ {
+ log_height = (int)atom_getintarg(17, argc, argv);
+ }
+ x->x_gui.x_draw = (t_iemfunptr)my_numbox_draw;
+ iinit &= IEM_INIT_ARGS_ALL;
+ ifstyle &= IEM_FSTYLE_FLAGS_ALL;
+ fstyle->x_snd_able = 1;
+ fstyle->x_rcv_able = 1;
+ x->x_gui.x_glist = (t_glist *)canvas_getcurrent();
+ x->x_gui.x_isa = *init;
+ if(x->x_gui.x_isa.x_loadinit)
+ x->x_val = v;
+ else
+ x->x_val = 0.0;
+ if(lilo != 0) lilo = 1;
+ x->x_lin0_log1 = lilo;
+ if(log_height < 10)
+ log_height = 10;
+ x->x_log_height = log_height;
+ if(!strcmp(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0;
+ if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0;
+ x->x_gui.x_unique_num = 0;
+ if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica");
+ else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times");
+ else { fstyle->x_font_style = 0;
+ strcpy(x->x_gui.x_font, "courier"); }
+ x->x_gui.x_fsf = *fstyle;
+ iemgui_first_dollararg2sym(&x->x_gui, srl);
+ if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]);
+ x->x_gui.x_snd = srl[0];
+ x->x_gui.x_rcv = srl[1];
+ x->x_gui.x_lab = srl[2];
+ x->x_gui.x_ldx = ldx;
+ x->x_gui.x_ldy = ldy;
+ if(fs < 4)
+ fs = 4;
+ x->x_gui.x_fontsize = fs;
+ if(w < 1)
+ w = 1;
+ x->x_gui.x_w = w;
+ if(h < 8)
+ h = 8;
+ x->x_gui.x_h = h;
+ x->x_buf[0] = 0;
+ my_numbox_calc_fontwidth(x);
+ my_numbox_check_minmax(x, min, max);
+ iemgui_all_colfromload(&x->x_gui, bflcol);
+ iemgui_verify_snd_ne_rcv(&x->x_gui);
+ x->x_clock_reset = clock_new(x, (t_method)my_numbox_tick_reset);
+ x->x_clock_wait = clock_new(x, (t_method)my_numbox_tick_wait);
+ x->x_gui.x_fsf.x_change = 0;
+ outlet_new(&x->x_gui.x_obj, &s_float);
+ return (x);
+}
+
+static void my_numbox_free(t_my_numbox *x)
+{
+ if(x->x_gui.x_fsf.x_selected)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ if(x->x_gui.x_fsf.x_rcv_able)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);
+ clock_free(x->x_clock_reset);
+ clock_free(x->x_clock_wait);
+ gfxstub_deleteforkey(x);
+}
+
+void g_numbox_setup(void)
+{
+ my_numbox_class = class_new(gensym("nbx"), (t_newmethod)my_numbox_new,
+ (t_method)my_numbox_free, sizeof(t_my_numbox), 0, A_GIMME, 0);
+ class_addcreator((t_newmethod)my_numbox_new, gensym("my_numbox"), A_GIMME, 0);
+ class_addbang(my_numbox_class,my_numbox_bang);
+ class_addfloat(my_numbox_class,my_numbox_float);
+ class_addlist(my_numbox_class, my_numbox_list);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_click, gensym("click"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_motion, gensym("motion"),
+ A_FLOAT, A_FLOAT, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_dialog, gensym("dialog"),
+ A_GIMME, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_loadbang, gensym("loadbang"), 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_set, gensym("set"), A_FLOAT, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_size, gensym("size"), A_GIMME, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_delta, gensym("delta"), A_GIMME, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_pos, gensym("pos"), A_GIMME, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_range, gensym("range"), A_GIMME, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_color, gensym("color"), A_GIMME, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_send, gensym("send"), A_DEFSYM, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_receive, gensym("receive"), A_DEFSYM, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_label, gensym("label"), A_DEFSYM, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_label_pos, gensym("label_pos"), A_GIMME, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_label_font, gensym("label_font"), A_GIMME, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_log, gensym("log"), 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_lin, gensym("lin"), 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_init, gensym("init"), A_FLOAT, 0);
+ class_addmethod(my_numbox_class, (t_method)my_numbox_log_height, gensym("log_height"), A_FLOAT, 0);
+ if(!iemgui_key_sym)
+ iemgui_key_sym = gensym("#keyname");
+ my_numbox_widgetbehavior.w_getrectfn = my_numbox_getrect;
+ my_numbox_widgetbehavior.w_displacefn = iemgui_displace;
+ my_numbox_widgetbehavior.w_selectfn = iemgui_select;
+ my_numbox_widgetbehavior.w_activatefn = NULL;
+ my_numbox_widgetbehavior.w_deletefn = iemgui_delete;
+ my_numbox_widgetbehavior.w_visfn = iemgui_vis;
+ my_numbox_widgetbehavior.w_clickfn = my_numbox_newclick;
+ my_numbox_widgetbehavior.w_propertiesfn = my_numbox_properties;;
+ my_numbox_widgetbehavior.w_savefn = my_numbox_save;
+ class_setwidget(my_numbox_class, &my_numbox_widgetbehavior);
+ class_sethelpsymbol(my_numbox_class, gensym("numbox2"));
+}
diff --git a/pd/src/g_readwrite.c b/pd/src/g_readwrite.c
new file mode 100644
index 00000000..ab380971
--- /dev/null
+++ b/pd/src/g_readwrite.c
@@ -0,0 +1,722 @@
+/* Copyright (c) 1997-2002 Miller Puckette and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* this file reads and writes the "data" portions of a canvas to a file.
+See also canvas_saveto(), etc., in g_editor.c. The data portion is a
+collection of "scalar" objects. Routines here can save collections of
+scalars into a file and reload them; also, support is included here for
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include <string.h>
+
+ /* the following routines read "scalars" from a file into a canvas. */
+
+static int canvas_scanbinbuf(int natoms, t_atom *vec, int *p_indexout,
+ int *p_next)
+{
+ int i, j;
+ int indexwas = *p_next;
+ *p_indexout = indexwas;
+ if (indexwas >= natoms)
+ return (0);
+ for (i = indexwas; i < natoms && vec[i].a_type != A_SEMI; i++)
+ ;
+ if (i >= natoms)
+ *p_next = i;
+ else *p_next = i + 1;
+ return (i - indexwas);
+}
+
+int glist_readscalar(t_glist *x, int natoms, t_atom *vec,
+ int *p_nextmsg, int selectit);
+
+static void canvas_readerror(int natoms, t_atom *vec, int message,
+ int nline, char *s)
+{
+ error(s);
+ startpost("line was:");
+ postatom(nline, vec + message);
+ endpost();
+}
+
+ /* fill in the contents of the scalar into the vector w. */
+
+static void glist_readatoms(t_glist *x, int natoms, t_atom *vec,
+ int *p_nextmsg, t_symbol *templatesym, t_word *w, int argc, t_atom *argv)
+{
+ int message, nline, n, i;
+
+ t_template *template = template_findbyname(templatesym);
+ if (!template)
+ {
+ error("%s: no such template", templatesym->s_name);
+ *p_nextmsg = natoms;
+ return;
+ }
+ word_restore(w, template, argc, argv);
+ n = template->t_n;
+ for (i = 0; i < n; i++)
+ {
+ if (template->t_vec[i].ds_type == DT_ARRAY)
+ {
+ int j;
+ t_array *a = w[i].w_array;
+ int elemsize = a->a_elemsize, nitems = 0;
+ t_symbol *arraytemplatesym = template->t_vec[i].ds_arraytemplate;
+ t_template *arraytemplate =
+ template_findbyname(arraytemplatesym);
+ if (!arraytemplate)
+ {
+ error("%s: no such template", arraytemplatesym->s_name);
+ }
+ else while (1)
+ {
+ t_word *element;
+ int nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg);
+ /* empty line terminates array */
+ if (!nline)
+ break;
+ array_resize(a, arraytemplate, nitems + 1);
+ element = (t_word *)(((char *)a->a_vec) +
+ nitems * elemsize);
+ glist_readatoms(x, natoms, vec, p_nextmsg, arraytemplatesym,
+ element, nline, vec + message);
+ nitems++;
+ }
+ }
+ else if (template->t_vec[i].ds_type == DT_LIST)
+ {
+ while (1)
+ {
+ if (!glist_readscalar(w->w_list, natoms, vec,
+ p_nextmsg, 0))
+ break;
+ }
+ }
+ }
+}
+
+int glist_readscalar(t_glist *x, int natoms, t_atom *vec,
+ int *p_nextmsg, int selectit)
+{
+ int message, i, j, nline;
+ t_template *template;
+ t_symbol *templatesym;
+ t_scalar *sc;
+ int nextmsg = *p_nextmsg;
+ int wasvis = glist_isvisible(x);
+
+ if (nextmsg >= natoms || vec[nextmsg].a_type != A_SYMBOL)
+ {
+ if (nextmsg < natoms)
+ post("stopping early: type %d", vec[nextmsg].a_type);
+ *p_nextmsg = natoms;
+ return (0);
+ }
+ templatesym = canvas_makebindsym(vec[nextmsg].a_w.w_symbol);
+ *p_nextmsg = nextmsg + 1;
+
+ if (!(template = template_findbyname(templatesym)))
+ {
+ error("canvas_read: %s: no such template", templatesym->s_name);
+ *p_nextmsg = natoms;
+ return (0);
+ }
+ sc = scalar_new(x, templatesym);
+ if (!sc)
+ {
+ error("couldn't create scalar \"%s\"", templatesym->s_name);
+ *p_nextmsg = natoms;
+ return (0);
+ }
+ if (wasvis)
+ {
+ /* temporarily lie about vis flag while this is built */
+ glist_getcanvas(x)->gl_mapped = 0;
+ }
+ glist_add(x, &sc->sc_gobj);
+
+ nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg);
+ glist_readatoms(x, natoms, vec, p_nextmsg, templatesym, sc->sc_vec,
+ nline, vec + message);
+ if (wasvis)
+ {
+ /* reset vis flag as before */
+ glist_getcanvas(x)->gl_mapped = 1;
+ gobj_vis(&sc->sc_gobj, x, 1);
+ }
+ if (selectit)
+ {
+ glist_select(x, &sc->sc_gobj);
+ }
+ return (1);
+}
+
+void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename, int selectem)
+{
+ t_canvas *canvas = glist_getcanvas(x);
+ int cr = 0, natoms, nline, message, nextmsg = 0, i, j, nitems;
+ t_atom *vec;
+ t_gobj *gobj;
+
+ natoms = binbuf_getnatom(b);
+ vec = binbuf_getvec(b);
+
+
+ /* check for file type */
+ nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg);
+ if (nline != 1 && vec[message].a_type != A_SYMBOL &&
+ strcmp(vec[message].a_w.w_symbol->s_name, "data"))
+ {
+ pd_error(x, "%s: file apparently of wrong type", filename);
+ binbuf_free(b);
+ return;
+ }
+ /* read in templates and check for consistency */
+ while (1)
+ {
+ t_template *newtemplate, *existtemplate;
+ t_symbol *templatesym;
+ t_atom *templateargs = getbytes(0);
+ int ntemplateargs = 0, newnargs;
+ nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg);
+ if (nline < 2)
+ break;
+ else if (nline > 2)
+ canvas_readerror(natoms, vec, message, nline,
+ "extra items ignored");
+ else if (vec[message].a_type != A_SYMBOL ||
+ strcmp(vec[message].a_w.w_symbol->s_name, "template") ||
+ vec[message + 1].a_type != A_SYMBOL)
+ {
+ canvas_readerror(natoms, vec, message, nline,
+ "bad template header");
+ continue;
+ }
+ templatesym = canvas_makebindsym(vec[message + 1].a_w.w_symbol);
+ while (1)
+ {
+ nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg);
+ if (nline != 2 && nline != 3)
+ break;
+ newnargs = ntemplateargs + nline;
+ templateargs = (t_atom *)t_resizebytes(templateargs,
+ sizeof(*templateargs) * ntemplateargs,
+ sizeof(*templateargs) * newnargs);
+ templateargs[ntemplateargs] = vec[message];
+ templateargs[ntemplateargs + 1] = vec[message + 1];
+ if (nline == 3)
+ templateargs[ntemplateargs + 2] = vec[message + 2];
+ ntemplateargs = newnargs;
+ }
+ newtemplate = template_new(templatesym, ntemplateargs, templateargs);
+ t_freebytes(templateargs, sizeof (*templateargs) * ntemplateargs);
+ if (!(existtemplate = template_findbyname(templatesym)))
+ {
+ error("%s: template not found in current patch",
+ templatesym->s_name);
+ template_free(newtemplate);
+ return;
+ }
+ if (!template_match(existtemplate, newtemplate))
+ {
+ error("%s: template doesn't match current one",
+ templatesym->s_name);
+ template_free(newtemplate);
+ return;
+ }
+ template_free(newtemplate);
+ }
+ while (nextmsg < natoms)
+ {
+ glist_readscalar(x, natoms, vec, &nextmsg, selectem);
+ }
+}
+
+static void glist_doread(t_glist *x, t_symbol *filename, t_symbol *format,
+ int clearme)
+{
+ t_binbuf *b = binbuf_new();
+ t_canvas *canvas = glist_getcanvas(x);
+ int wasvis = glist_isvisible(canvas);
+ int cr = 0, natoms, nline, message, nextmsg = 0, i, j;
+ t_atom *vec;
+
+ if (!strcmp(format->s_name, "cr"))
+ cr = 1;
+ else if (*format->s_name)
+ error("qlist_read: unknown flag: %s", format->s_name);
+
+ if (binbuf_read_via_path(b, filename->s_name,
+ canvas_getdir(canvas)->s_name, cr))
+ {
+ pd_error(x, "read failed");
+ binbuf_free(b);
+ return;
+ }
+ if (wasvis)
+ canvas_vis(canvas, 0);
+ if (clearme)
+ glist_clear(x);
+ glist_readfrombinbuf(x, b, filename->s_name, 0);
+ if (wasvis)
+ canvas_vis(canvas, 1);
+ binbuf_free(b);
+}
+
+void glist_read(t_glist *x, t_symbol *filename, t_symbol *format)
+{
+ glist_doread(x, filename, format, 1);
+}
+
+void glist_mergefile(t_glist *x, t_symbol *filename, t_symbol *format)
+{
+ glist_doread(x, filename, format, 0);
+}
+
+ /* read text from a "properties" window, called from a gfxstub set
+ up in scalar_properties(). We try to restore the object; if successful
+ we delete the scalar and put the new thing in its place on the list. */
+void canvas_dataproperties(t_canvas *x, t_scalar *sc, t_binbuf *b)
+{
+ int ntotal, nnew, scindex;
+ t_gobj *y, *y2 = 0, *newone, *oldone = 0;
+ for (y = x->gl_list, ntotal = 0, scindex = -1; y; y = y->g_next)
+ {
+ if (y == &sc->sc_gobj)
+ scindex = ntotal, oldone = y;
+ ntotal++;
+ }
+
+ if (scindex == -1)
+ bug("data_properties: scalar disappeared");
+ glist_readfrombinbuf(x, b, "properties dialog", 0);
+ newone = 0;
+ if (scindex >= 0)
+ {
+ /* take the new object off the list */
+ if (ntotal)
+ {
+ for (y = x->gl_list, nnew = 1; y2 = y->g_next;
+ y = y2, nnew++)
+ if (nnew == ntotal)
+ {
+ newone = y2;
+ y->g_next = y2->g_next;
+ break;
+ }
+ }
+ else newone = x->gl_list, x->gl_list = newone->g_next;
+ }
+ if (!newone)
+ error("couldn't update properties (perhaps a format problem?)");
+ else if (!oldone)
+ bug("data_properties: couldn't find old element");
+ else
+ {
+ glist_delete(x, oldone);
+ if (scindex > 0)
+ {
+ for (y = x->gl_list, nnew = 1; y;
+ y = y->g_next, nnew++)
+ if (nnew == scindex || !y->g_next)
+ {
+ newone->g_next = y->g_next;
+ y->g_next = newone;
+ goto didit;
+ }
+ bug("data_properties: can't reinsert");
+ }
+ else newone->g_next = x->gl_list, x->gl_list = newone;
+ }
+didit:
+ ;
+}
+
+ /* ----------- routines to write data to a binbuf ----------- */
+
+void canvas_doaddtemplate(t_symbol *templatesym,
+ int *p_ntemplates, t_symbol ***p_templatevec)
+{
+ int n = *p_ntemplates, i;
+ t_symbol **templatevec = *p_templatevec;
+ for (i = 0; i < n; i++)
+ if (templatevec[i] == templatesym)
+ return;
+ templatevec = (t_symbol **)t_resizebytes(templatevec,
+ n * sizeof(*templatevec), (n+1) * sizeof(*templatevec));
+ templatevec[n] = templatesym;
+ *p_templatevec = templatevec;
+ *p_ntemplates = n+1;
+}
+
+static void glist_writelist(t_gobj *y, t_binbuf *b);
+
+void canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b,
+ int amarrayelement)
+{
+ t_dataslot *ds;
+ t_template *template = template_findbyname(templatesym);
+ t_atom *a = (t_atom *)t_getbytes(0);
+ int i, n = template->t_n, natom = 0;
+ if (!amarrayelement)
+ {
+ t_atom templatename;
+ SETSYMBOL(&templatename, gensym(templatesym->s_name + 3));
+ binbuf_add(b, 1, &templatename);
+ }
+ if (!template)
+ bug("canvas_writescalar");
+ /* write the atoms (floats and symbols) */
+ for (i = 0; i < n; i++)
+ {
+ if (template->t_vec[i].ds_type == DT_FLOAT ||
+ template->t_vec[i].ds_type == DT_SYMBOL)
+ {
+ a = (t_atom *)t_resizebytes(a,
+ natom * sizeof(*a), (natom + 1) * sizeof (*a));
+ if (template->t_vec[i].ds_type == DT_FLOAT)
+ SETFLOAT(a + natom, w[i].w_float);
+ else SETSYMBOL(a + natom, w[i].w_symbol);
+ natom++;
+ }
+ }
+ /* array elements have to have at least something */
+ if (natom == 0 && amarrayelement)
+ SETSYMBOL(a + natom, &s_bang), natom++;
+ binbuf_add(b, natom, a);
+ binbuf_addsemi(b);
+ t_freebytes(a, natom * sizeof(*a));
+ for (i = 0; i < n; i++)
+ {
+ if (template->t_vec[i].ds_type == DT_ARRAY)
+ {
+ int j;
+ t_array *a = w[i].w_array;
+ int elemsize = a->a_elemsize, nitems = a->a_n;
+ t_symbol *arraytemplatesym = template->t_vec[i].ds_arraytemplate;
+ for (j = 0; j < nitems; j++)
+ canvas_writescalar(arraytemplatesym,
+ (t_word *)(((char *)a->a_vec) + elemsize * j), b, 1);
+ binbuf_addsemi(b);
+ }
+ else if (template->t_vec[i].ds_type == DT_LIST)
+ {
+ glist_writelist(w->w_list->gl_list, b);
+ binbuf_addsemi(b);
+ }
+ }
+}
+
+static void glist_writelist(t_gobj *y, t_binbuf *b)
+{
+ for (; y; y = y->g_next)
+ {
+ if (pd_class(&y->g_pd) == scalar_class)
+ {
+ canvas_writescalar(((t_scalar *)y)->sc_template,
+ ((t_scalar *)y)->sc_vec, b, 0);
+ }
+ }
+}
+
+ /* ------------ routines to write out templates for data ------- */
+
+static void canvas_addtemplatesforlist(t_gobj *y,
+ int *p_ntemplates, t_symbol ***p_templatevec);
+
+static void canvas_addtemplatesforscalar(t_symbol *templatesym,
+ t_word *w, int *p_ntemplates, t_symbol ***p_templatevec)
+{
+ t_dataslot *ds;
+ int i;
+ t_template *template = template_findbyname(templatesym);
+ canvas_doaddtemplate(templatesym, p_ntemplates, p_templatevec);
+ if (!template)
+ bug("canvas_addtemplatesforscalar");
+ else for (ds = template->t_vec, i = template->t_n; i--; ds++, w++)
+ {
+ if (ds->ds_type == DT_ARRAY)
+ {
+ int j;
+ t_array *a = w->w_array;
+ int elemsize = a->a_elemsize, nitems = a->a_n;
+ t_symbol *arraytemplatesym = ds->ds_arraytemplate;
+ canvas_doaddtemplate(arraytemplatesym, p_ntemplates, p_templatevec);
+ for (j = 0; j < nitems; j++)
+ canvas_addtemplatesforscalar(arraytemplatesym,
+ (t_word *)(((char *)a->a_vec) + elemsize * j),
+ p_ntemplates, p_templatevec);
+ }
+ else if (ds->ds_type == DT_LIST)
+ canvas_addtemplatesforlist(w->w_list->gl_list,
+ p_ntemplates, p_templatevec);
+ }
+}
+
+static void canvas_addtemplatesforlist(t_gobj *y,
+ int *p_ntemplates, t_symbol ***p_templatevec)
+{
+ for (; y; y = y->g_next)
+ {
+ if (pd_class(&y->g_pd) == scalar_class)
+ {
+ canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template,
+ ((t_scalar *)y)->sc_vec, p_ntemplates, p_templatevec);
+ }
+ }
+}
+
+ /* write all "scalars" in a glist to a binbuf. */
+t_binbuf *glist_writetobinbuf(t_glist *x, int wholething)
+{
+ int i;
+ t_symbol **templatevec = getbytes(0);
+ int ntemplates = 0;
+ t_gobj *y;
+ t_binbuf *b = binbuf_new();
+
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ if ((pd_class(&y->g_pd) == scalar_class) &&
+ (wholething || glist_isselected(x, y)))
+ {
+ canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template,
+ ((t_scalar *)y)->sc_vec, &ntemplates, &templatevec);
+ }
+ }
+ binbuf_addv(b, "s;", gensym("data"));
+ for (i = 0; i < ntemplates; i++)
+ {
+ t_template *template = template_findbyname(templatevec[i]);
+ int j, m = template->t_n;
+ /* drop "pd-" prefix from template symbol to print it: */
+ binbuf_addv(b, "ss;", gensym("template"),
+ gensym(templatevec[i]->s_name + 3));
+ for (j = 0; j < m; j++)
+ {
+ t_symbol *type;
+ switch (template->t_vec[j].ds_type)
+ {
+ case DT_FLOAT: type = &s_float; break;
+ case DT_SYMBOL: type = &s_symbol; break;
+ case DT_ARRAY: type = gensym("array"); break;
+ case DT_LIST: type = &s_list; break;
+ default: type = &s_float; bug("canvas_write");
+ }
+ if (template->t_vec[j].ds_type == DT_ARRAY)
+ binbuf_addv(b, "sss;", type, template->t_vec[j].ds_name,
+ gensym(template->t_vec[j].ds_arraytemplate->s_name + 3));
+ else binbuf_addv(b, "ss;", type, template->t_vec[j].ds_name);
+ }
+ binbuf_addsemi(b);
+ }
+ binbuf_addsemi(b);
+ /* now write out the objects themselves */
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ if ((pd_class(&y->g_pd) == scalar_class) &&
+ (wholething || glist_isselected(x, y)))
+ {
+ canvas_writescalar(((t_scalar *)y)->sc_template,
+ ((t_scalar *)y)->sc_vec, b, 0);
+ }
+ }
+ return (b);
+}
+
+static void glist_write(t_glist *x, t_symbol *filename, t_symbol *format)
+{
+ int cr = 0, i;
+ t_binbuf *b;
+ char buf[MAXPDSTRING];
+ t_symbol **templatevec = getbytes(0);
+ int ntemplates = 0;
+ t_gobj *y;
+ t_canvas *canvas = glist_getcanvas(x);
+ canvas_makefilename(canvas, filename->s_name, buf, MAXPDSTRING);
+ if (!strcmp(format->s_name, "cr"))
+ cr = 1;
+ else if (*format->s_name)
+ error("qlist_read: unknown flag: %s", format->s_name);
+
+ b = glist_writetobinbuf(x, 1);
+ if (b)
+ {
+ if (binbuf_write(b, buf, "", cr))
+ error("%s: write failed", filename->s_name);
+ binbuf_free(b);
+ }
+}
+
+/* ------ routines to save and restore canvases (patches) recursively. ----*/
+
+ /* save to a binbuf, called recursively; cf. canvas_savetofile() which
+ saves the document, and is only called on root canvases. */
+static void canvas_saveto(t_canvas *x, t_binbuf *b)
+{
+ t_gobj *y;
+ t_linetraverser t;
+ t_outconnect *oc;
+ /* subpatch */
+ if (x->gl_owner && !x->gl_env)
+ {
+ binbuf_addv(b, "ssiiiisi;", gensym("#N"), gensym("canvas"),
+ (t_int)(x->gl_screenx1),
+ (t_int)(x->gl_screeny1),
+ (t_int)(x->gl_screenx2 - x->gl_screenx1),
+ (t_int)(x->gl_screeny2 - x->gl_screeny1),
+ x->gl_name, x->gl_mapped);
+ }
+ /* root or abstraction */
+ else binbuf_addv(b, "ssiiiii;", gensym("#N"), gensym("canvas"),
+ (t_int)(x->gl_screenx1),
+ (t_int)(x->gl_screeny1),
+ (t_int)(x->gl_screenx2 - x->gl_screenx1),
+ (t_int)(x->gl_screeny2 - x->gl_screeny1),
+ x->gl_font);
+
+ for (y = x->gl_list; y; y = y->g_next)
+ gobj_save(y, b);
+
+ linetraverser_start(&t, x);
+ while (oc = linetraverser_next(&t))
+ {
+ int srcno, sinkno;
+ for (srcno = 0, y = x->gl_list; y && y != &t.tr_ob->ob_g; y = y->g_next)
+ srcno++;
+ for (sinkno = 0, y = x->gl_list; y && y != &t.tr_ob2->ob_g; y = y->g_next)
+ sinkno++;
+ binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"),
+ srcno, t.tr_outno, sinkno, t.tr_inno);
+ }
+ if (x->gl_isgraph)
+ binbuf_addv(b, "ssfffffff;", gensym("#X"), gensym("coords"),
+ x->gl_x1, x->gl_y1,
+ x->gl_x2, x->gl_y2,
+ (float)x->gl_pixwidth, (float)x->gl_pixheight,
+ (float)x->gl_isgraph);
+}
+
+ /* call this recursively to collect all the template names for
+ a canvas or for the selection. */
+static void canvas_collecttemplatesfor(t_canvas *x, int *ntemplatesp,
+ t_symbol ***templatevecp, int wholething)
+{
+ t_gobj *y;
+
+ for (y = x->gl_list; y; y = y->g_next)
+ {
+ if ((pd_class(&y->g_pd) == scalar_class) &&
+ (wholething || glist_isselected(x, y)))
+ canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template,
+ ((t_scalar *)y)->sc_vec, ntemplatesp, templatevecp);
+ else if ((pd_class(&y->g_pd) == canvas_class) &&
+ (wholething || glist_isselected(x, y)))
+ canvas_collecttemplatesfor((t_canvas *)y,
+ ntemplatesp, templatevecp, 1);
+ }
+}
+
+ /* save the templates needed by a canvas to a binbuf. */
+static void canvas_savetemplatesto(t_canvas *x, t_binbuf *b, int wholething)
+{
+ t_symbol **templatevec = getbytes(0);
+ int i, ntemplates = 0;
+ t_gobj *y;
+ canvas_collecttemplatesfor(x, &ntemplates, &templatevec, wholething);
+ for (i = 0; i < ntemplates; i++)
+ {
+ t_template *template = template_findbyname(templatevec[i]);
+ int j, m = template->t_n;
+ if (!template)
+ {
+ bug("canvas_savetemplatesto");
+ continue;
+ }
+ /* drop "pd-" prefix from template symbol to print */
+ binbuf_addv(b, "sss", &s__N, gensym("struct"),
+ gensym(templatevec[i]->s_name + 3));
+ for (j = 0; j < m; j++)
+ {
+ t_symbol *type;
+ switch (template->t_vec[j].ds_type)
+ {
+ case DT_FLOAT: type = &s_float; break;
+ case DT_SYMBOL: type = &s_symbol; break;
+ case DT_ARRAY: type = gensym("array"); break;
+ case DT_LIST: type = &s_list; break;
+ default: type = &s_float; bug("canvas_write");
+ }
+ if (template->t_vec[j].ds_type == DT_ARRAY)
+ binbuf_addv(b, "sss", type, template->t_vec[j].ds_name,
+ gensym(template->t_vec[j].ds_arraytemplate->s_name + 3));
+ else binbuf_addv(b, "ss", type, template->t_vec[j].ds_name);
+ }
+ binbuf_addsemi(b);
+ }
+}
+
+ /* save a "root" canvas to a file; cf. canvas_saveto() which saves the
+ body (and which is called recursively.) */
+static void canvas_savetofile(t_canvas *x, t_symbol *filename, t_symbol *dir)
+{
+ t_binbuf *b = binbuf_new();
+ canvas_savetemplatesto(x, b, 1);
+ canvas_saveto(x, b);
+ if (binbuf_write(b, filename->s_name, dir->s_name, 0)) sys_ouch();
+ else
+ {
+ /* if not an abstraction, reset title bar and directory */
+ if (!x->gl_owner)
+ canvas_rename(x, filename, dir);
+ post("saved to: %s/%s", dir->s_name, filename->s_name);
+ canvas_dirty(x, 0);
+#if 0 /* not yet written */
+ canvas_reload(filename, dir);
+#endif
+ }
+ binbuf_free(b);
+}
+
+static void canvas_menusaveas(t_canvas *x)
+{
+ t_canvas *x2 = canvas_getrootfor(x);
+ sys_vgui("pdtk_canvas_saveas .x%x \"%s\" \"%s\"\n", x2,
+ x2->gl_name->s_name, canvas_getdir(x2)->s_name);
+}
+
+static void canvas_menusave(t_canvas *x)
+{
+ t_canvas *x2 = canvas_getrootfor(x);
+ char *name = x2->gl_name->s_name;
+ if (*name && strncmp(name, "Untitled", 8)
+ && (strlen(name) < 4 || strcmp(name + strlen(name)-4, ".pat")))
+ canvas_savetofile(x2, x2->gl_name, canvas_getdir(x2));
+ else canvas_menusaveas(x2);
+}
+
+
+void g_readwrite_setup(void)
+{
+ class_addmethod(canvas_class, (t_method)glist_write,
+ gensym("write"), A_SYMBOL, A_DEFSYM, A_NULL);
+ class_addmethod(canvas_class, (t_method)glist_read,
+ gensym("read"), A_SYMBOL, A_DEFSYM, A_NULL);
+ class_addmethod(canvas_class, (t_method)glist_mergefile,
+ gensym("mergefile"), A_SYMBOL, A_DEFSYM, A_NULL);
+ class_addmethod(canvas_class, (t_method)canvas_savetofile,
+ gensym("savetofile"), A_SYMBOL, A_SYMBOL, 0);
+ class_addmethod(canvas_class, (t_method)canvas_saveto,
+ gensym("saveto"), A_CANT, 0);
+/* ------------------ from the menu ------------------------- */
+ class_addmethod(canvas_class, (t_method)canvas_menusave,
+ gensym("menusave"), 0);
+ class_addmethod(canvas_class, (t_method)canvas_menusaveas,
+ gensym("menusaveas"), 0);
+}
diff --git a/pd/src/g_rtext.c b/pd/src/g_rtext.c
new file mode 100644
index 00000000..4b48dcc0
--- /dev/null
+++ b/pd/src/g_rtext.c
@@ -0,0 +1,478 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* changes by Thomas Musil IEM KUG Graz Austria 2001 */
+/* have to insert gui-objects into editor-list */
+/* all changes are labeled with iemlib */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include "t_tk.h"
+
+#define LMARGIN 1
+#define RMARGIN 1
+#define TMARGIN 2
+#define BMARGIN 2
+
+#define SEND_FIRST 1
+#define SEND_UPDATE 2
+#define SEND_CHECK 0
+
+static void rtext_senditup(t_rtext *x, int action, int *widthp, int *heightp,
+ int *indexp);
+
+struct _rtext
+{
+ char *x_buf;
+ int x_bufsize;
+ int x_selstart;
+ int x_selend;
+ int x_active;
+ int x_dragfrom;
+ int x_height;
+ int x_drawnwidth;
+ int x_drawnheight;
+ t_text *x_text;
+ t_glist *x_glist;
+ char x_tag[50];
+ struct _rtext *x_next;
+};
+
+t_rtext *rtext_new(t_glist *glist, t_text *who, t_rtext *next, int senditup)
+{
+ t_rtext *x = (t_rtext *)getbytes(sizeof *x);
+ int w = 0, h = 0, indx;
+ x->x_height = -1;
+ x->x_text = who;
+ x->x_glist = glist;
+ x->x_next = glist->gl_editor->e_rtext;
+ x->x_selstart = x->x_selend = x->x_active =
+ x->x_drawnwidth = x->x_drawnheight = 0;
+ binbuf_gettext(who->te_binbuf, &x->x_buf, &x->x_bufsize);
+ glist->gl_editor->e_rtext = x;
+ sprintf(x->x_tag, ".x%x.t%x", (t_int)glist_getcanvas(x->x_glist),
+ (t_int)x);
+ if (senditup)
+ rtext_senditup(x, SEND_FIRST, &w, &h, &indx);
+ return (x);
+}
+
+/* iemlib version (now incorporated into rtext_new() via the "senditup" arg) */
+
+t_rtext *rtext_new_without_senditup(t_glist *glist, t_text *who, t_rtext *next)
+{
+ return (rtext_new(glist, who, next, 0));
+}
+
+static t_rtext *rtext_entered;
+
+void rtext_free(t_rtext *x)
+{
+ sys_vgui(".x%x.c delete %s\n", glist_getcanvas(x->x_glist),
+ x->x_tag);
+ if (x->x_glist->gl_editor->e_textedfor == x)
+ x->x_glist->gl_editor->e_textedfor = 0;
+ if (x->x_glist->gl_editor->e_rtext == x)
+ x->x_glist->gl_editor->e_rtext = x->x_next;
+ else
+ {
+ t_rtext *e2;
+ for (e2 = x->x_glist->gl_editor->e_rtext; e2; e2 = e2->x_next)
+ if (e2->x_next == x)
+ {
+ e2->x_next = x->x_next;
+ break;
+ }
+ }
+ if (rtext_entered == x) rtext_entered = 0;
+ freebytes(x->x_buf, x->x_bufsize);
+ freebytes(x, sizeof *x);
+}
+
+char *rtext_gettag(t_rtext *x)
+{
+ return (x->x_tag);
+}
+
+void rtext_gettext(t_rtext *x, char **buf, int *bufsize)
+{
+ *buf = x->x_buf;
+ *bufsize = x->x_bufsize;
+}
+
+
+/* LATER deal with tcl-significant characters */
+
+static int firstone(char *s, int c, int n)
+{
+ char *s2 = s + n;
+ int i = 0;
+ while (s != s2)
+ {
+ if (*s == c) return (i);
+ i++;
+ s++;
+ }
+ return (-1);
+}
+
+static int lastone(char *s, int c, int n)
+{
+ char *s2 = s + n;
+ while (s2 != s)
+ {
+ s2--;
+ n--;
+ if (*s2 == c) return (n);
+ }
+ return (-1);
+}
+
+ /* the following routine computes line breaks and carries out
+ some action which could be:
+ SEND_FIRST - draw the box for the first time
+ SEND_UPDATE - redraw the updated box
+ otherwise - don't draw, just calculate.
+ Called with *widthp and *heightpas coordinates of
+ a test point, the routine reports the index of the character found
+ there in *indexp. *widthp and *heightp are set to the width and height
+ of the entire text in pixels.
+ */
+
+ /* LATER get this and sys_vgui to work together properly,
+ breaking up messages as needed. As of now, there's
+ a limit of 1950 characters, imposed by sys_vgui(). */
+#define UPBUFSIZE 4000
+#define BOXWIDTH 60
+
+static void rtext_senditup(t_rtext *x, int action, int *widthp, int *heightp,
+ int *indexp)
+{
+ float dispx, dispy;
+ char tempbuf[UPBUFSIZE], *tp = tempbuf, *bp = x->x_buf;
+ int outchars, inchars = x->x_bufsize, nlines = 0, ncolumns = 0,
+ pixwide, pixhigh;
+ int font = glist_getfont(x->x_glist);
+ int fontwidth = sys_fontwidth(font), fontheight = sys_fontheight(font);
+ int findx = (*widthp + (fontwidth/2)) / fontwidth,
+ findy = *heightp / fontheight;
+ int reportedindex = 0;
+ t_canvas *canvas = glist_getcanvas(x->x_glist);
+ int widthspec = x->x_text->te_width;
+ int widthlimit = (widthspec ? widthspec : BOXWIDTH);
+ while (inchars)
+ {
+ int maxindex = (inchars > widthlimit ? widthlimit : inchars);
+ int eatchar = 1;
+ int foundit = firstone(bp, '\n', maxindex);
+ if (foundit < 0)
+ {
+ if (inchars > widthlimit)
+ {
+ foundit = lastone(bp, ' ', maxindex);
+ if (foundit < 0)
+ {
+ foundit = maxindex;
+ eatchar = 0;
+ }
+ }
+ else
+ {
+ foundit = inchars;
+ eatchar = 0;
+ }
+ }
+ if (nlines == findy)
+ {
+ int actualx = (findx < 0 ? 0 :
+ (findx > foundit ? foundit : findx));
+ *indexp = (bp - x->x_buf) + actualx;
+ reportedindex = 1;
+ }
+ strncpy(tp, bp, foundit);
+ tp += foundit;
+ bp += (foundit + eatchar);
+ inchars -= (foundit + eatchar);
+ if (inchars) *tp++ = '\n';
+ if (foundit > ncolumns) ncolumns = foundit;
+ nlines++;
+ }
+ outchars = tp - tempbuf;
+ if (outchars > 1950) outchars = 1950;
+ if (!reportedindex)
+ *indexp = outchars;
+ dispx = text_xpix(x->x_text, x->x_glist);
+ dispy = text_ypix(x->x_text, x->x_glist);
+ if (nlines < 1) nlines = 1;
+ if (!widthspec)
+ {
+ while (ncolumns < 3)
+ {
+ tempbuf[outchars++] = ' ';
+ ncolumns++;
+ }
+ }
+ else ncolumns = widthspec;
+ pixwide = ncolumns * fontwidth + (LMARGIN + RMARGIN);
+ pixhigh = nlines * fontheight + (TMARGIN + BMARGIN);
+
+ if (action == SEND_FIRST)
+ sys_vgui("pdtk_text_new .x%x.c %s %f %f {%.*s} %d %s\n",
+ canvas, x->x_tag,
+ dispx + LMARGIN, dispy + TMARGIN,
+ outchars, tempbuf, sys_hostfontsize(font),
+ (glist_isselected(x->x_glist,
+ &x->x_glist->gl_gobj)? "blue" : "black"));
+ else if (action == SEND_UPDATE)
+ {
+ sys_vgui("pdtk_text_set .x%x.c %s {%.*s}\n",
+ canvas, x->x_tag, outchars, tempbuf);
+ if (pixwide != x->x_drawnwidth || pixhigh != x->x_drawnheight)
+ text_drawborder(x->x_text, x->x_glist, x->x_tag,
+ pixwide, pixhigh, 0);
+ if (x->x_active)
+ {
+ if (x->x_selend > x->x_selstart)
+ {
+ sys_vgui(".x%x.c select from %s %d\n", canvas,
+ x->x_tag, x->x_selstart);
+ sys_vgui(".x%x.c select to %s %d\n", canvas,
+ x->x_tag, x->x_selend);
+ sys_vgui(".x%x.c focus \"\"\n", canvas);
+ }
+ else
+ {
+ sys_vgui(".x%x.c select clear\n", canvas);
+ sys_vgui(".x%x.c icursor %s %d\n", canvas, x->x_tag,
+ x->x_selstart);
+ sys_vgui(".x%x.c focus %s\n", canvas, x->x_tag);
+ }
+ }
+ }
+ x->x_drawnwidth = pixwide;
+ x->x_drawnheight = pixhigh;
+
+ *widthp = pixwide;
+ *heightp = pixhigh;
+}
+
+void rtext_retext(t_rtext *x)
+{
+ int w = 0, h = 0, indx;
+ t_text *text = x->x_text;
+ t_freebytes(x->x_buf, x->x_bufsize);
+ binbuf_gettext(text->te_binbuf, &x->x_buf, &x->x_bufsize);
+ /* special case: for number boxes, try to pare the number down
+ to the specified width of the box. */
+ if (text->te_width > 0 && text->te_type == T_ATOM &&
+ x->x_bufsize > text->te_width)
+ {
+ t_atom *atomp = binbuf_getvec(text->te_binbuf);
+ int natom = binbuf_getnatom(text->te_binbuf);
+ int bufsize = x->x_bufsize;
+ if (natom == 1 && atomp->a_type == A_FLOAT)
+ {
+ /* try to reduce size by dropping decimal digits */
+ int wantreduce = bufsize - text->te_width;
+ char *decimal = 0, *nextchar, *ebuf = x->x_buf + bufsize,
+ *s1, *s2;
+ int ndecimals;
+ for (decimal = x->x_buf; decimal < ebuf; decimal++)
+ if (*decimal == '.')
+ break;
+ if (decimal >= ebuf)
+ goto giveup;
+ for (nextchar = decimal + 1; nextchar < ebuf; nextchar++)
+ if (*nextchar < '0' || *nextchar > '9')
+ break;
+ if (nextchar - decimal - 1 < wantreduce)
+ goto giveup;
+ for (s1 = nextchar - wantreduce, s2 = s1 + wantreduce;
+ s2 < ebuf; s1++, s2++)
+ *s1 = *s2;
+ t_resizebytes(x->x_buf, bufsize, text->te_width);
+ bufsize = text->te_width;
+ goto done;
+ giveup:
+ /* give up and bash it to "+" or "-" */
+ x->x_buf[0] = (atomp->a_w.w_float < 0 ? '-' : '+');
+ t_resizebytes(x->x_buf, bufsize, 1);
+ bufsize = 1;
+ }
+ else if (bufsize > text->te_width)
+ {
+ x->x_buf[text->te_width - 1] = '>';
+ t_resizebytes(x->x_buf, bufsize, text->te_width);
+ bufsize = text->te_width;
+ }
+ done:
+ x->x_bufsize = bufsize;
+ }
+ rtext_senditup(x, SEND_UPDATE, &w, &h, &indx);
+}
+
+/* find the rtext that goes with a text item */
+t_rtext *glist_findrtext(t_glist *gl, t_text *who)
+{
+ t_rtext *x = gl->gl_editor->e_rtext;
+ while (x && x->x_text != who) x = x->x_next;
+ if (!x) bug("glist_findrtext");
+ return (x);
+}
+
+int rtext_width(t_rtext *x)
+{
+ int w = 0, h = 0, indx;
+ rtext_senditup(x, SEND_CHECK, &w, &h, &indx);
+ return (w);
+}
+
+int rtext_height(t_rtext *x)
+{
+ int w = 0, h = 0, indx;
+ rtext_senditup(x, SEND_CHECK, &w, &h, &indx);
+ return (h);
+}
+
+void rtext_displace(t_rtext *x, int dx, int dy)
+{
+ sys_vgui(".x%x.c move %s %d %d\n", glist_getcanvas(x->x_glist),
+ x->x_tag, dx, dy);
+}
+
+void rtext_select(t_rtext *x, int state)
+{
+ t_glist *glist = x->x_glist;
+ t_canvas *canvas = glist_getcanvas(glist);
+ sys_vgui(".x%x.c itemconfigure %s -fill %s\n", canvas,
+ x->x_tag, (state? "blue" : "black"));
+ canvas_editing = canvas;
+}
+
+void rtext_activate(t_rtext *x, int state)
+{
+ int w = 0, h = 0, indx;
+ t_glist *glist = x->x_glist;
+ t_canvas *canvas = glist_getcanvas(glist);
+ if (state)
+ {
+ sys_vgui(".x%x.c focus %s\n", canvas, x->x_tag);
+ glist->gl_editor->e_textedfor = x;
+ glist->gl_editor->e_textdirty = 0;
+ x->x_dragfrom = x->x_selstart = 0;
+ x->x_selend = x->x_bufsize;
+ x->x_active = 1;
+ }
+ else
+ {
+ sys_vgui("selection clear .x%x.c\n", canvas);
+ sys_vgui(".x%x.c focus \"\"\n", canvas);
+ if (glist->gl_editor->e_textedfor == x)
+ glist->gl_editor->e_textedfor = 0;
+ x->x_active = 0;
+ }
+ rtext_senditup(x, SEND_UPDATE, &w, &h, &indx);
+}
+
+void rtext_key(t_rtext *x, int keynum, t_symbol *keysym)
+{
+ int w = 0, h = 0, indx, i, newsize, ndel;
+ char *s1, *s2;
+ if (keynum)
+ {
+ int n = keynum;
+ if (n == '\r') n = '\n';
+ if (n == '\b')
+ {
+ if ((!x->x_selstart) && (x->x_selend == x->x_bufsize))
+ {
+ /* LATER delete the box... this causes reentrancy
+ problems now. */
+ /* glist_delete(x->x_glist, &x->x_text->te_g); */
+ return;
+ }
+ else if (x->x_selstart && (x->x_selstart == x->x_selend))
+ x->x_selstart--;
+ }
+ ndel = x->x_selend - x->x_selstart;
+ for (i = x->x_selend; i < x->x_bufsize; i++)
+ x->x_buf[i- ndel] = x->x_buf[i];
+ newsize = x->x_bufsize - ndel;
+ x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize);
+ x->x_bufsize = newsize;
+
+ if (n == '\n' || isprint(n))
+ {
+ newsize = x->x_bufsize+1;
+ x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize);
+ for (i = x->x_bufsize; i > x->x_selstart; i--)
+ x->x_buf[i] = x->x_buf[i-1];
+ x->x_buf[x->x_selstart] = n;
+ x->x_bufsize = newsize;
+ x->x_selstart = x->x_selstart + 1;
+ }
+ x->x_selend = x->x_selstart;
+ x->x_glist->gl_editor->e_textdirty = 1;
+ }
+ else if (!strcmp(keysym->s_name, "Right"))
+ {
+ if (x->x_selend == x->x_selstart && x->x_selstart < x->x_bufsize)
+ x->x_selend = x->x_selstart = x->x_selstart + 1;
+ else
+ x->x_selstart = x->x_selend;
+ }
+ else if (!strcmp(keysym->s_name, "Left"))
+ {
+ if (x->x_selend == x->x_selstart && x->x_selstart > 0)
+ x->x_selend = x->x_selstart = x->x_selstart - 1;
+ else
+ x->x_selend = x->x_selstart;
+ }
+ /* this should be improved... life's too short */
+ else if (!strcmp(keysym->s_name, "Up"))
+ {
+ if (x->x_selstart)
+ x->x_selstart--;
+ while (x->x_selstart > 0 && x->x_buf[x->x_selstart] != '\n')
+ x->x_selstart--;
+ x->x_selend = x->x_selstart;
+ }
+ else if (!strcmp(keysym->s_name, "Down"))
+ {
+ while (x->x_selend < x->x_bufsize &&
+ x->x_buf[x->x_selend] != '\n')
+ x->x_selend++;
+ if (x->x_selend < x->x_bufsize)
+ x->x_selend++;
+ x->x_selstart = x->x_selend;
+ }
+ rtext_senditup(x, SEND_UPDATE, &w, &h, &indx);
+}
+
+void rtext_mouse(t_rtext *x, int xval, int yval, int flag)
+{
+ int w = xval, h = yval, indx;
+ rtext_senditup(x, SEND_CHECK, &w, &h, &indx);
+ if (flag == RTEXT_DOWN)
+ {
+ x->x_dragfrom = x->x_selstart = x->x_selend = indx;
+ }
+ else if (flag == RTEXT_SHIFT)
+ {
+ if (indx * 2 > x->x_selstart + x->x_selend)
+ x->x_dragfrom = x->x_selstart, x->x_selend = indx;
+ else
+ x->x_dragfrom = x->x_selend, x->x_selstart = indx;
+ }
+ else if (flag == RTEXT_DRAG)
+ {
+ x->x_selstart = (x->x_dragfrom < indx ? x->x_dragfrom : indx);
+ x->x_selend = (x->x_dragfrom > indx ? x->x_dragfrom : indx);
+ }
+ rtext_senditup(x, SEND_UPDATE, &w, &h, &indx);
+}
diff --git a/pd/src/g_scalar.c b/pd/src/g_scalar.c
new file mode 100644
index 00000000..362fb108
--- /dev/null
+++ b/pd/src/g_scalar.c
@@ -0,0 +1,373 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* This file defines the "scalar" object, which is not a text object, just a
+"gobj". Scalars have templates which describe their structures, which
+can contain numbers, sublists, and arrays.
+
+Also, the "tscalar" object, an ordinary text object that owns a single "scalar"
+and draws it on the parent. This is intended as a way that abstractions can
+control their appearances by adding stuff to draw.
+*/
+
+/* IOhannes :
+ * changed the canvas_restore, so that it might accept $args as well (like "pd $0_test")
+ * so you can make multiple & distinguishable templates
+ * 1511:forum::für::umläute:2001
+ * changes marked with IOhannes
+ * added Krzysztof Czajas fix to avoid crashing...
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h> /* for read/write to files */
+#include "m_pd.h"
+#include "g_canvas.h"
+
+t_class *scalar_class;
+
+void word_init(t_word *wp, t_template *template, t_gpointer *gp)
+{
+ int i, nitems = template->t_n;
+ t_dataslot *datatypes = template->t_vec;
+ for (i = 0; i < nitems; i++, datatypes++, wp++)
+ {
+ int type = datatypes->ds_type;
+ if (type == DT_FLOAT)
+ wp->w_float = 0;
+ else if (type == DT_SYMBOL)
+ wp->w_symbol = &s_symbol;
+ else if (type == DT_ARRAY)
+ {
+ wp->w_array = array_new(datatypes->ds_arraytemplate, gp);
+ }
+ else if (type == DT_LIST)
+ {
+ /* LATER test this and get it to work */
+ wp->w_list = canvas_new(0, 0, 0, 0);
+ }
+ }
+}
+
+void word_restore(t_word *wp, t_template *template,
+ int argc, t_atom *argv)
+{
+ int i, nitems = template->t_n;
+ t_dataslot *datatypes = template->t_vec;
+ for (i = 0; i < nitems; i++, datatypes++, wp++)
+ {
+ int type = datatypes->ds_type;
+ if (type == DT_FLOAT)
+ {
+ float f;
+ if (argc)
+ {
+ f = atom_getfloat(argv);
+ argv++, argc--;
+ }
+ else f = 0;
+ wp->w_float = f;
+ }
+ else if (type == DT_SYMBOL)
+ {
+ t_symbol *s;
+ if (argc)
+ {
+ s = atom_getsymbol(argv);
+ argv++, argc--;
+ }
+ else s = &s_;
+ wp->w_symbol = s;
+ }
+ }
+ if (argc)
+ post("warning: word_restore: extra arguments");
+}
+
+ /* make a new scalar and add to the glist. We create a "gp" here which
+ will be used for array items to point back here. This gp doesn't do
+ reference counting or "validation" updates though; the parent won't go away
+ without the contained arrays going away too. The "gp" is copied out
+ by value in the word_init() routine so we can throw our copy away. */
+
+t_scalar *scalar_new(t_glist *owner, t_symbol *templatesym)
+{
+ t_scalar *x;
+ t_template *template;
+ t_gpointer gp;
+ gpointer_init(&gp);
+ template = template_findbyname(templatesym);
+ if (!template)
+ {
+ error("scalar: couldn't find template %s", templatesym->s_name);
+ return (0);
+ }
+ x = (t_scalar *)getbytes(sizeof(t_scalar) +
+ (template->t_n - 1) * sizeof(*x->sc_vec));
+ x->sc_gobj.g_pd = scalar_class;
+ x->sc_template = templatesym;
+ gpointer_setglist(&gp, owner, x);
+ word_init(x->sc_vec, template, &gp);
+ return (x);
+}
+
+ /* Pd method to create a new scalar, add it to a glist, and initialize
+ it from the message arguments. */
+
+int glist_readscalar(t_glist *x, int natoms, t_atom *vec,
+ int *p_nextmsg, int selectit);
+
+void glist_scalar(t_glist *glist,
+ t_symbol *classname, t_int argc, t_atom *argv)
+{
+ t_symbol *templatesym =
+ canvas_makebindsym(atom_getsymbolarg(0, argc, argv));
+ t_binbuf *b;
+ int natoms, nextmsg = 0;
+ t_atom *vec;
+ if (!template_findbyname(templatesym))
+ {
+ pd_error(glist, "%s: no such template",
+ atom_getsymbolarg(0, argc, argv)->s_name);
+ return;
+ }
+
+ b = binbuf_new();
+ binbuf_restore(b, argc, argv);
+ natoms = binbuf_getnatom(b);
+ vec = binbuf_getvec(b);
+
+ glist_readscalar(glist, natoms, vec, &nextmsg, 0);
+ binbuf_free(b);
+}
+
+/* -------------------- widget behavior for scalar ------------ */
+void scalar_getbasexy(t_scalar *x, float *basex, float *basey)
+{
+ t_template *template = template_findbyname(x->sc_template);
+ *basex = template_getfloat(template, gensym("x"), x->sc_vec, 0);
+ *basey = template_getfloat(template, gensym("y"), x->sc_vec, 0);
+}
+
+static void scalar_getrect(t_gobj *z, t_glist *owner,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_scalar *x = (t_scalar *)z;
+ int hit = 0;
+ t_template *template = template_findbyname(x->sc_template);
+ t_canvas *templatecanvas = template_findcanvas(template);
+ int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff;
+ t_gobj *y;
+ float basex, basey;
+ scalar_getbasexy(x, &basex, &basey);
+ for (y = templatecanvas->gl_list; y; y = y->g_next)
+ {
+ t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
+ int nx1, ny1, nx2, ny2;
+ if (!wb) continue;
+ (*wb->w_parentgetrectfn)(y, owner,
+ x->sc_vec, template, basex, basey,
+ &nx1, &ny1, &nx2, &ny2);
+ if (hit)
+ {
+ if (nx1 < x1) x1 = nx1;
+ if (ny1 < y1) y1 = ny1;
+ if (nx2 > x2) x2 = nx2;
+ if (ny2 > y2) y2 = ny2;
+ }
+ else x1 = nx1, y1 = ny1, x2 = nx2, y2 = ny2, hit = 1;
+ }
+ if (!hit) x1 = y1 = x2 = y2 = 0;
+ /* post("scalar x1 %d y1 %d x2 %d y2 %d", x1, y1, x2, y2); */
+ *xp1 = x1;
+ *yp1 = y1;
+ *xp2 = x2;
+ *yp2 = y2;
+}
+
+static void scalar_select(t_gobj *z, t_glist *owner, int state)
+{
+ t_scalar *x = (t_scalar *)z;
+ /* post("scalar_select %d", state); */
+ /* later */
+ if (state)
+ {
+ int x1, y1, x2, y2;
+ scalar_getrect(z, owner, &x1, &y1, &x2, &y2);
+ sys_vgui(".x%x.c create line %d %d %d %d %d %d %d %d %d %d \
+ -width 0 -fill blue -tags select%x\n",
+ glist_getcanvas(owner), x1, y1, x1, y2, x2, y2, x2, y1, x1, y1,
+ x);
+ }
+ else sys_vgui(".x%x.c delete select%x\n", glist_getcanvas(owner), x);
+}
+
+static void scalar_displace(t_gobj *z, t_glist *glist, int dx, int dy)
+{
+ t_scalar *x = (t_scalar *)z;
+ t_symbol *templatesym = x->sc_template;
+ t_template *template = template_findbyname(templatesym);
+ t_symbol *zz;
+ int xonset, yonset, xtype, ytype, gotx, goty;
+ if (!template)
+ {
+ error("scalar: couldn't find template %s", templatesym->s_name);
+ return;
+ }
+ gotx = template_find_field(template, gensym("x"), &xonset, &xtype, &zz);
+ if (gotx && (xtype != DT_FLOAT))
+ gotx = 0;
+ goty = template_find_field(template, gensym("y"), &yonset, &ytype, &zz);
+ if (goty && (ytype != DT_FLOAT))
+ goty = 0;
+ if (gotx)
+ *(t_float *)(((char *)(x->sc_vec)) + xonset) +=
+ dx * (glist_pixelstox(glist, 1) - glist_pixelstox(glist, 0));
+ if (goty)
+ *(t_float *)(((char *)(x->sc_vec)) + yonset) +=
+ dy * (glist_pixelstoy(glist, 1) - glist_pixelstoy(glist, 0));
+ glist_redrawitem(glist, z);
+ if (glist_isselected(glist, z))
+ {
+ scalar_select(z, glist, 0);
+ scalar_select(z, glist, 1);
+ }
+}
+
+static void scalar_activate(t_gobj *z, t_glist *owner, int state)
+{
+ /* post("scalar_activate %d", state); */
+ /* later */
+}
+
+static void scalar_delete(t_gobj *z, t_glist *glist)
+{
+ /* nothing to do */
+}
+
+static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
+{
+ t_scalar *x = (t_scalar *)z;
+ t_template *template = template_findbyname(x->sc_template);
+ t_canvas *templatecanvas = template_findcanvas(template);
+ t_gobj *y;
+ float basex, basey;
+ if (!templatecanvas)
+ {
+ bug("scalar_vis"); /* it's still a bug, have to fix this for real... */
+ return; /* proposed by Krzysztof Czaja to avoid crashing */
+ }
+ scalar_getbasexy(x, &basex, &basey);
+
+ for (y = templatecanvas->gl_list; y; y = y->g_next)
+ {
+ t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
+ if (!wb) continue;
+ (*wb->w_parentvisfn)(y, owner, x->sc_vec, template, basex, basey, vis);
+ }
+}
+
+static int scalar_click(t_gobj *z, struct _glist *owner,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ t_scalar *x = (t_scalar *)z;
+ int hit = 0;
+ t_template *template = template_findbyname(x->sc_template);
+ t_canvas *templatecanvas = template_findcanvas(template);
+ t_gobj *y;
+ float basex, basey;
+ scalar_getbasexy(x, &basex, &basey);
+ for (y = templatecanvas->gl_list; y; y = y->g_next)
+ {
+ t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
+ if (!wb) continue;
+ if (hit = (*wb->w_parentclickfn)(y, owner,
+ x, template, basex, basey,
+ xpix, ypix, shift, alt, dbl, doit))
+ return (hit);
+ }
+ return (0);
+}
+
+void canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b,
+ int amarrayelement);
+
+static void scalar_save(t_gobj *z, t_binbuf *b)
+{
+ t_scalar *x = (t_scalar *)z;
+ t_binbuf *b2 = binbuf_new();
+ t_atom a, *argv;
+ int i, argc;
+ canvas_writescalar(x->sc_template, x->sc_vec, b2, 0);
+ binbuf_addv(b, "ss", &s__X, gensym("scalar"));
+ binbuf_addbinbuf(b, b2);
+ binbuf_addsemi(b);
+ binbuf_free(b2);
+}
+
+static void scalar_properties(t_gobj *z, struct _glist *owner)
+{
+ t_scalar *x = (t_scalar *)z;
+ char *buf, buf2[80];
+ int bufsize;
+ t_binbuf *b;
+ glist_noselect(owner);
+ glist_select(owner, z);
+ b = glist_writetobinbuf(owner, 0);
+ binbuf_gettext(b, &buf, &bufsize);
+ binbuf_free(b);
+ t_resizebytes(buf, bufsize, bufsize+1);
+ buf[bufsize] = 0;
+ sprintf(buf2, "pdtk_data_dialog %%s {");
+ gfxstub_new((t_pd *)owner, x, buf2);
+ sys_gui(buf);
+ sys_gui("}\n");
+ t_freebytes(buf, bufsize+1);
+}
+
+static t_widgetbehavior scalar_widgetbehavior =
+{
+ scalar_getrect,
+ scalar_displace,
+ scalar_select,
+ scalar_activate,
+ scalar_delete,
+ scalar_vis,
+ scalar_click,
+ scalar_save,
+ scalar_properties,
+};
+
+static void scalar_free(t_scalar *x)
+{
+ int i;
+ t_dataslot *datatypes, *dt;
+ t_symbol *templatesym = x->sc_template;
+ t_template *template = template_findbyname(templatesym);
+ if (!template)
+ {
+ error("scalar: couldn't find template %s", templatesym->s_name);
+ return;
+ }
+ for (dt = template->t_vec, i = 0; i < template->t_n; i++, dt++)
+ {
+ if (dt->ds_type == DT_ARRAY)
+ array_free(x->sc_vec[i].w_array);
+ else if (dt->ds_type == DT_LIST)
+ canvas_free(x->sc_vec[i].w_list);
+ }
+ gfxstub_deleteforkey(x);
+ /* the "size" field in the class is zero, so Pd doesn't try to free
+ us automatically (see pd_free()) */
+ freebytes(x, sizeof(t_scalar) + (template->t_n - 1) * sizeof(*x->sc_vec));
+}
+
+/* ----------------- setup function ------------------- */
+
+void g_scalar_setup(void)
+{
+ scalar_class = class_new(gensym("scalar"), 0, (t_method)scalar_free, 0,
+ CLASS_GOBJ, 0);
+ class_setwidget(scalar_class, &scalar_widgetbehavior);
+}
diff --git a/pd/src/g_template.c b/pd/src/g_template.c
new file mode 100644
index 00000000..5e11cc5a
--- /dev/null
+++ b/pd/src/g_template.c
@@ -0,0 +1,1673 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "m_imp.h" /* for sys_hostfontsize */
+#include "g_canvas.h"
+
+/*
+This file contains text objects you would put in a canvas to define a
+template. Templates describe objects of type "array" (g_array.c) and
+"scalar" (g_scalar.c).
+*/
+
+ /* the structure of a "struct" object (also the obsolete "gtemplate"
+ you get when using the name "template" in a box.) */
+
+struct _gtemplate
+{
+ t_object x_obj;
+ t_template *x_template;
+ t_canvas *x_owner;
+ t_symbol *x_sym;
+ struct _gtemplate *x_next;
+ int x_argc;
+ t_atom *x_argv;
+};
+
+/* ---------------- forward definitions ---------------- */
+
+static void template_conformarray(t_template *tfrom, t_template *tto,
+ int *conformaction, t_array *a);
+static void template_conformglist(t_template *tfrom, t_template *tto,
+ t_glist *glist, int *conformaction);
+
+/* ---------------------- storage ------------------------- */
+
+static t_class *gtemplate_class;
+static t_class *template_class;
+
+/* there's a pre-defined "float" template. LATER should we bind this
+to a symbol such as "pd-float"??? */
+
+static t_dataslot template_float_vec =
+{
+ DT_FLOAT,
+ &s_y,
+ &s_
+};
+
+static t_template template_float =
+{
+ 0, /* class -- fill in in setup routine */
+ 0, /* list of "struct"/t_gtemplate objects */
+ &s_float, /* name */
+ 1, /* number of items */
+ &template_float_vec
+};
+
+ /* return true if two dataslot definitions match */
+static int dataslot_matches(t_dataslot *ds1, t_dataslot *ds2,
+ int nametoo)
+{
+ return ((!nametoo || ds1->ds_name == ds2->ds_name) &&
+ ds1->ds_type == ds2->ds_type &&
+ (ds1->ds_type != DT_ARRAY ||
+ ds1->ds_arraytemplate == ds2->ds_arraytemplate));
+}
+
+/* -- templates, the active ingredient in gtemplates defined below. ------- */
+
+t_template *template_new(t_symbol *templatesym, int argc, t_atom *argv)
+{
+ t_template *x = (t_template *)pd_new(template_class);
+ x->t_n = 0;
+ x->t_vec = (t_dataslot *)t_getbytes(0);
+ while (argc > 0)
+ {
+ int newtype, oldn, newn;
+ t_symbol *newname, *newarraytemplate = &s_, *newtypesym;
+ if (argc < 2 || argv[0].a_type != A_SYMBOL ||
+ argv[1].a_type != A_SYMBOL)
+ goto bad;
+ newtypesym = argv[0].a_w.w_symbol;
+ newname = argv[1].a_w.w_symbol;
+ if (newtypesym == &s_float)
+ newtype = DT_FLOAT;
+ else if (newtypesym == &s_symbol)
+ newtype = DT_SYMBOL;
+ else if (newtypesym == &s_list)
+ newtype = DT_LIST;
+ else if (newtypesym == gensym("array"))
+ {
+ if (argc < 3 || argv[2].a_type != A_SYMBOL)
+ {
+ pd_error(x, "array lacks element template or name");
+ goto bad;
+ }
+ newarraytemplate = canvas_makebindsym(argv[2].a_w.w_symbol);
+ newtype = DT_ARRAY;
+ argc--;
+ argv++;
+ }
+ else
+ {
+ pd_error(x, "%s: no such type", newtypesym->s_name);
+ return (0);
+ }
+ newn = (oldn = x->t_n) + 1;
+ x->t_vec = (t_dataslot *)t_resizebytes(x->t_vec,
+ oldn * sizeof(*x->t_vec), newn * sizeof(*x->t_vec));
+ x->t_n = newn;
+ x->t_vec[oldn].ds_type = newtype;
+ x->t_vec[oldn].ds_name = newname;
+ x->t_vec[oldn].ds_arraytemplate = newarraytemplate;
+ bad:
+ argc -= 2; argv += 2;
+ }
+ if (templatesym->s_name)
+ {
+ x->t_sym = templatesym;
+ pd_bind(&x->t_pd, x->t_sym);
+ }
+ else x->t_sym = templatesym;
+ return (x);
+}
+
+int template_size(t_template *x)
+{
+ return (x->t_n * sizeof(t_word));
+}
+
+int template_find_field(t_template *x, t_symbol *name, int *p_onset,
+ int *p_type, t_symbol **p_arraytype)
+{
+ t_template *t;
+ int i, n;
+ if (!x)
+ {
+ bug("template_find_field");
+ return (0);
+ }
+ n = x->t_n;
+ for (i = 0; i < n; i++)
+ if (x->t_vec[i].ds_name == name)
+ {
+ *p_onset = i * sizeof(t_word);
+ *p_type = x->t_vec[i].ds_type;
+ *p_arraytype = x->t_vec[i].ds_arraytemplate;
+ return (1);
+ }
+ return (0);
+}
+
+t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp,
+ int loud)
+{
+ int onset, type;
+ t_symbol *arraytype;
+ float val = 0;
+ if (template_find_field(x, fieldname, &onset, &type, &arraytype))
+ {
+ if (type == DT_FLOAT)
+ val = *(t_float *)(((char *)wp) + onset);
+ else if (loud) error("%s.%s: not a number",
+ x->t_sym->s_name, fieldname->s_name);
+ }
+ else if (loud) error("%s.%s: no such field",
+ x->t_sym->s_name, fieldname->s_name);
+ return (val);
+}
+
+void template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp,
+ t_float f, int loud)
+{
+ int onset, type;
+ t_symbol *arraytype;
+ if (template_find_field(x, fieldname, &onset, &type, &arraytype))
+ {
+ if (type == DT_FLOAT)
+ *(t_float *)(((char *)wp) + onset) = f;
+ else if (loud) error("%s.%s: not a number",
+ x->t_sym->s_name, fieldname->s_name);
+ }
+ else if (loud) error("%s.%s: no such field",
+ x->t_sym->s_name, fieldname->s_name);
+}
+
+t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, t_word *wp,
+ int loud)
+{
+ int onset, type;
+ t_symbol *arraytype;
+ t_symbol *val = &s_;
+ if (template_find_field(x, fieldname, &onset, &type, &arraytype))
+ {
+ if (type == DT_SYMBOL)
+ val = *(t_symbol **)(((char *)wp) + onset);
+ else if (loud) error("%s.%s: not a symbol",
+ x->t_sym->s_name, fieldname->s_name);
+ }
+ else if (loud) error("%s.%s: no such field",
+ x->t_sym->s_name, fieldname->s_name);
+ return (val);
+}
+
+void template_setsymbol(t_template *x, t_symbol *fieldname, t_word *wp,
+ t_symbol *s, int loud)
+{
+ int onset, type;
+ t_symbol *arraytype;
+ if (template_find_field(x, fieldname, &onset, &type, &arraytype))
+ {
+ if (type == DT_SYMBOL)
+ *(t_symbol **)(((char *)wp) + onset) = s;
+ else if (loud) error("%s.%s: not a symbol",
+ x->t_sym->s_name, fieldname->s_name);
+ }
+ else if (loud) error("%s.%s: no such field",
+ x->t_sym->s_name, fieldname->s_name);
+}
+
+ /* stringent check to see if a "saved" template, x2, matches the current
+ one (x1). It's OK if x1 has additional scalar elements but not (yet)
+ arrays or lists. This is used for reading in "data files". */
+int template_match(t_template *x1, t_template *x2)
+{
+ int i;
+ if (x1->t_n < x2->t_n)
+ return (0);
+ for (i = x2->t_n; i < x1->t_n; i++)
+ {
+ if (x1->t_vec[i].ds_type == DT_ARRAY ||
+ x1->t_vec[i].ds_type == DT_LIST)
+ return (0);
+ }
+ if (x2->t_n > x1->t_n)
+ post("add elements...");
+ for (i = 0; i < x2->t_n; i++)
+ if (!dataslot_matches(&x1->t_vec[i], &x2->t_vec[i], 1))
+ return (0);
+ return (1);
+}
+
+/* --------------- CONFORMING TO CHANGES IN A TEMPLATE ------------ */
+
+/* the following routines handle updating scalars to agree with changes
+in their template. The old template is assumed to be the "installed" one
+so we can delete old items; but making new ones we have to avoid scalar_new
+which would make an old one whereas we will want a new one (but whose array
+elements might still be old ones.
+ LATER deal with graphics updates too... */
+
+ /* conform the word vector of a scalar to the new template */
+static void template_conformwords(t_template *tfrom, t_template *tto,
+ int *conformaction, t_word *wfrom, t_word *wto)
+{
+ int nfrom = tfrom->t_n, nto = tto->t_n, i;
+ for (i = 0; i < nto; i++)
+ {
+ if (conformaction[i] >= 0)
+ {
+ /* we swap the two, in case it's an array or list, so that
+ when "wfrom" is deleted the old one gets cleaned up. */
+ t_word wwas = wto[i];
+ wto[i] = wfrom[conformaction[i]];
+ wfrom[conformaction[i]] = wwas;
+ }
+ }
+}
+
+ /* conform a scalar, recursively conforming sublists and arrays */
+static t_scalar *template_conformscalar(t_template *tfrom, t_template *tto,
+ int *conformaction, t_glist *glist, t_scalar *scfrom)
+{
+ t_scalar *x;
+ t_gpointer gp;
+ int nto = tto->t_n, nfrom = tfrom->t_n, i;
+ post("conform scalar");
+ /* possibly replace the scalar */
+ if (scfrom->sc_template == tfrom->t_sym)
+ {
+ post("match");
+ /* see scalar_new() for comment about the gpointer. */
+ gpointer_init(&gp);
+ x = (t_scalar *)getbytes(sizeof(t_scalar) +
+ (tto->t_n - 1) * sizeof(*x->sc_vec));
+ x->sc_gobj.g_pd = scalar_class;
+ x->sc_template = tfrom->t_sym;
+ gpointer_setglist(&gp, glist, x);
+ /* Here we initialize to the new template, but array and list
+ elements will still belong to old template. */
+ word_init(x->sc_vec, tto, &gp);
+
+ template_conformwords(tfrom, tto, conformaction,
+ scfrom->sc_vec, x->sc_vec);
+
+ /* replace the old one with the new one in the list */
+ if (glist->gl_list == &scfrom->sc_gobj)
+ {
+ glist->gl_list = &x->sc_gobj;
+ x->sc_gobj.g_next = scfrom->sc_gobj.g_next;
+ }
+ else
+ {
+ t_gobj *y, *y2;
+ for (y = glist->gl_list; y2 = y->g_next; y = y2)
+ if (y2 == &scfrom->sc_gobj)
+ {
+ x->sc_gobj.g_next = y2->g_next;
+ y->g_next = &x->sc_gobj;
+ goto nobug;
+ }
+ bug("template_conformscalar");
+ nobug: ;
+ }
+ /* burn the old one */
+ pd_free(&scfrom->sc_gobj.g_pd);
+ }
+ else x = scfrom;
+ /* convert all array elements and sublists */
+ for (i = 0; i < nto; i++)
+ {
+ if (tto->t_vec[i].ds_type == DT_LIST)
+ {
+ t_glist *gl2 = x->sc_vec[i].w_list;
+ template_conformglist(tfrom, tto, gl2, conformaction);
+ }
+ else if (tto->t_vec[i].ds_type == DT_ARRAY)
+ {
+ template_conformarray(tfrom, tto, conformaction,
+ x->sc_vec[i].w_array);
+ }
+ }
+ return (x);
+}
+
+ /* conform an array, recursively conforming sublists and arrays */
+static void template_conformarray(t_template *tfrom, t_template *tto,
+ int *conformaction, t_array *a)
+{
+ int i;
+ if (a->a_templatesym == tfrom->t_sym)
+ {
+ /* the array elements must all be conformed */
+ int oldelemsize = sizeof(t_word) * tfrom->t_n,
+ newelemsize = sizeof(t_word) * tto->t_n;
+ char *newarray = getbytes(sizeof(t_word) * tto->t_n * a->a_n);
+ char *oldarray = a->a_vec;
+ if (a->a_elemsize != oldelemsize)
+ bug("template_conformarray");
+ for (i = 0; i < a->a_n; i++)
+ {
+ t_word *wp = (t_word *)(newarray + newelemsize * i);
+ word_init(wp, tto, &a->a_gp);
+ template_conformwords(tfrom, tto, conformaction,
+ (t_word *)(oldarray + oldelemsize * i), wp);
+ }
+ }
+ bug("template_conformarray: this part not written");
+ /* go through item by item conforming subarrays and sublists... */
+}
+
+ /* this routine searches for every scalar in the glist that belongs
+ to the "from" template and makes it belong to the "to" template. Descend
+ glists recursively.
+ We don't handle redrawing here; this is to be filled in LATER... */
+
+static void template_conformglist(t_template *tfrom, t_template *tto,
+ t_glist *glist, int *conformaction)
+{
+ t_gobj *g;
+ post("conform glist %s", glist->gl_name->s_name);
+ for (g = glist->gl_list; g; g = g->g_next)
+ {
+ if (pd_class(&g->g_pd) == scalar_class)
+ g = &template_conformscalar(tfrom, tto, conformaction,
+ glist, (t_scalar *)g)->sc_gobj;
+ else if (pd_class(&g->g_pd) == canvas_class)
+ template_conformglist(tfrom, tto, (t_glist *)g, conformaction);
+ }
+}
+
+ /* globally conform all scalars from one template to another */
+void template_conform(t_template *tfrom, t_template *tto)
+{
+ int nto = tto->t_n, nfrom = tfrom->t_n, i, j,
+ *conformaction = (int *)getbytes(sizeof(int) * nto),
+ *conformedfrom = (int *)getbytes(sizeof(int) * nfrom), doit = 0;
+ for (i = 0; i < nto; i++)
+ conformaction[i] = -1;
+ for (i = 0; i < nfrom; i++)
+ conformedfrom[i] = 0;
+ for (i = 0; i < nto; i++)
+ {
+ t_dataslot *dataslot = &tto->t_vec[i];
+ for (j = 0; j < nfrom; j++)
+ {
+ t_dataslot *dataslot2 = &tfrom->t_vec[j];
+ if (dataslot_matches(dataslot, dataslot2, 1))
+ {
+ conformaction[i] = j;
+ conformedfrom[j] = 1;
+ }
+ }
+ }
+ for (i = 0; i < nto; i++)
+ if (conformaction[i] < 0)
+ {
+ t_dataslot *dataslot = &tto->t_vec[i];
+ for (j = 0; j < nfrom; j++)
+ if (!conformedfrom[j] &&
+ dataslot_matches(dataslot, &tfrom->t_vec[j], 1))
+ {
+ conformaction[i] = j;
+ conformedfrom[j] = 1;
+ }
+ }
+ if (nto != nfrom)
+ doit = 1;
+ else for (i = 0; i < nto; i++)
+ if (conformaction[i] != i)
+ doit = 1;
+
+ if (doit)
+ {
+ t_glist *gl;
+ post("conforming template '%s' to new structure",
+ tfrom->t_sym->s_name);
+ for (i = 0; i < nto; i++)
+ post("... %d", conformaction[i]);
+ for (gl = canvas_list; gl; gl = gl->gl_next)
+ template_conformglist(tfrom, tto, gl, conformaction);
+ }
+ freebytes(conformaction, sizeof(int) * nto);
+ freebytes(conformedfrom, sizeof(int) * nfrom);
+}
+
+t_template *template_findbyname(t_symbol *s)
+{
+ int i;
+ if (s == &s_float)
+ return (&template_float);
+ else return ((t_template *)pd_findbyclass(s, template_class));
+}
+
+t_canvas *template_findcanvas(t_template *template)
+{
+ t_gtemplate *gt;
+ if (!template)
+ bug("template_findcanvas");
+ if (!(gt = template->t_list))
+ return (0);
+ return (gt->x_owner);
+ /* return ((t_canvas *)pd_findbyclass(template->t_sym, canvas_class)); */
+}
+
+ /* call this when reading a patch from a file to declare what templates
+ we'll need. If there's already a template, check if it matches.
+ If it doesn't it's still OK as long as there are no "struct" (gtemplate)
+ objects hanging from it; we just conform everyone to the new template.
+ If there are still struct objects belonging to the other template, we're
+ in trouble. LATER we'll figure out how to conform the new patch's objects
+ to the pre-existing struct. */
+static void *template_usetemplate(void *dummy, t_symbol *s,
+ int argc, t_atom *argv)
+{
+ t_template *x;
+ t_symbol *templatesym =
+ canvas_makebindsym(atom_getsymbolarg(0, argc, argv));
+ if (!argc)
+ return (0);
+ argc--; argv++;
+ /* check if there's already a template by this name. */
+ if ((x = (t_template *)pd_findbyclass(templatesym, template_class)))
+ {
+ t_template *y = template_new(&s_, argc, argv);
+ /* If the new template is the same as the old one,
+ there's nothing to do. */
+ if (!template_match(x, y))
+ {
+ /* Are there "struct" objects upholding this template? */
+ if (x->t_list)
+ {
+ /* don't know what to do here! */
+ error("%s: template mismatch",
+ templatesym->s_name);
+ }
+ else
+ {
+ /* conform everyone to the new template */
+ template_conform(x, y);
+ pd_free(&x->t_pd);
+ template_new(templatesym, argc, argv);
+ }
+ }
+ pd_free(&y->t_pd);
+ }
+ /* otherwise, just make one. */
+ else template_new(templatesym, argc, argv);
+ return (0);
+}
+
+ /* here we assume someone has already cleaned up all instances of this. */
+void template_free(t_template *x)
+{
+ if (*x->t_sym->s_name)
+ pd_unbind(&x->t_pd, x->t_sym);
+ t_freebytes(x->t_vec, x->t_n * sizeof(*x->t_vec));
+}
+
+static void template_setup(void)
+{
+ template_class = class_new(gensym("template"), 0, (t_method)template_free,
+ sizeof(t_template), CLASS_PD, 0);
+ class_addmethod(pd_canvasmaker, (t_method)template_usetemplate,
+ gensym("struct"), A_GIMME, 0);
+
+}
+
+/* ---------------- gtemplates. One per canvas. ----------- */
+
+/* this is a "text" object that searches for, and if necessary creates,
+a "template" (above). Other objects in the canvas then can give drawing
+instructions for the template. The template doesn't go away when the
+gtemplate is deleted, so that you can replace it with
+another one to add new fields, for example. */
+
+static void *gtemplate_donew(t_symbol *sym, int argc, t_atom *argv)
+{
+ t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class);
+ t_template *t = template_findbyname(sym);
+ int i;
+ t_symbol *sx = gensym("x");
+ x->x_owner = canvas_getcurrent();
+ x->x_next = 0;
+ x->x_sym = sym;
+ x->x_argc = argc;
+ x->x_argv = (t_atom *)getbytes(argc * sizeof(t_atom));
+ for (i = 0; i < argc; i++)
+ x->x_argv[i] = argv[i];
+
+ /* already have a template by this name? */
+ if (t)
+ {
+ x->x_template = t;
+ /* if it's already got a "struct" or "gtemplate" object we
+ just tack this one to the end of the list and leave it
+ there. */
+ if (t->t_list)
+ {
+ t_gtemplate *x2, *x3;
+ for (x2 = x->x_template->t_list; x3 = x2->x_next; x2 = x3)
+ ;
+ x2->x_next = x;
+ post("template %s: warning: already exists.", sym->s_name);
+ }
+ else
+ {
+ /* if there's none, we just replace the template with
+ our own and conform it. */
+ t_template *y = template_new(&s_, argc, argv);
+ /* Unless the new template is different from the old one,
+ there's nothing to do. */
+ if (!template_match(t, y))
+ {
+ /* conform everyone to the new template */
+ template_conform(t, y);
+ pd_free(&t->t_pd);
+ t = template_new(sym, argc, argv);
+ }
+ pd_free(&y->t_pd);
+ t->t_list = x;
+ }
+ }
+ else
+ {
+ /* otherwise make a new one and we're the only struct on it. */
+ x->x_template = t = template_new(sym, argc, argv);
+ t->t_list = x;
+ }
+ return (x);
+}
+
+static void *gtemplate_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class);
+ t_symbol *sym = atom_getsymbolarg(0, argc, argv);
+ if (argc >= 1)
+ argc--; argv++;
+ return (gtemplate_donew(canvas_makebindsym(sym), argc, argv));
+}
+
+ /* old version (0.34) -- delete 2003 or so */
+static void *gtemplate_new_old(t_symbol *s, int argc, t_atom *argv)
+{
+ t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class);
+ t_symbol *sym = canvas_makebindsym(canvas_getcurrent()->gl_name);
+ static int warned;
+ if (!warned)
+ {
+ post("warning -- 'template' is obsolete; replace with 'struct'");
+ warned = 1;
+ }
+ post("name: %s", sym->s_name);
+ return (gtemplate_donew(sym, argc, argv));
+}
+
+t_template *gtemplate_get(t_gtemplate *x)
+{
+ return (x->x_template);
+}
+
+static void gtemplate_free(t_gtemplate *x)
+{
+ /* get off the template's list */
+ t_template *t = x->x_template;
+ if (x == t->t_list)
+ {
+ if (x->x_next)
+ {
+ /* if we were first on the list, and there are others on
+ the list, make a new template corresponding to the new
+ first-on-list and replace teh existing template with it. */
+ t_template *z = template_new(&s_, x->x_argc, x->x_argv);
+ template_conform(t, z);
+ pd_free(&t->t_pd);
+ pd_free(&z->t_pd);
+ z = template_new(x->x_sym, x->x_argc, x->x_argv);
+ z->t_list = x->x_next;
+ }
+ else t->t_list = 0;
+ }
+ else
+ {
+ t_gtemplate *x2, *x3;
+ for (x2 = t->t_list; x3 = x2->x_next; x2 = x3)
+ {
+ if (x == x3)
+ {
+ x2->x_next = x3->x_next;
+ break;
+ }
+ }
+ }
+ freebytes(x->x_argv, sizeof(t_atom) * x->x_argc);
+}
+
+static void gtemplate_setup(void)
+{
+ gtemplate_class = class_new(gensym("struct"),
+ (t_newmethod)gtemplate_new, (t_method)gtemplate_free,
+ sizeof(t_gtemplate), CLASS_NOINLET, A_GIMME, 0);
+ class_addcreator((t_newmethod)gtemplate_new_old, gensym("template"),
+ A_GIMME, 0);
+}
+
+/* --------------- FIELD DESCRIPTORS ---------------------- */
+
+/* a field descriptor can hold a constant or a variable; if a variable,
+it's the name of a field in the template we belong to. LATER, we might
+want to cache the offset of the field so we don't have to search for it
+every single time we draw the object.
+*/
+
+typedef struct _fielddesc
+{
+ char fd_type; /* LATER consider removing this? */
+ char fd_var;
+ union
+ {
+ t_float fd_float; /* the field is a constant float */
+ t_symbol *fd_symbol; /* the field is a constant symbol */
+ t_symbol *fd_varsym; /* the field is variable and this is the name */
+ } fd_un;
+} t_fielddesc;
+
+#define FIELDDESC_SETFLOAT(x, f) \
+ ((x)->fd_type = A_FLOAT, (x)->fd_var = 0, (x)->fd_un.fd_float = (f))
+#define FIELDDESC_SETSYMBOL(x, s) \
+ ((x)->fd_type = A_SYMBOL, (x)->fd_var = 0, (x)->fd_un.fd_symbol = (s))
+#define FIELDDESC_SETVAR(x, s, type) \
+ ((x)->fd_type = type, (x)->fd_var = 1, (x)->fd_un.fd_varsym = (s))
+
+#define CLOSED 1
+#define BEZ 2
+#define A_ARRAY 55 /* LATER decide whether to enshrine this in m_pd.h */
+
+static void fielddesc_setfloatarg(t_fielddesc *fd, int argc, t_atom *argv)
+{
+ if (argc <= 0) FIELDDESC_SETFLOAT(fd, 0);
+ else if (argv->a_type == A_SYMBOL)
+ FIELDDESC_SETVAR(fd, argv->a_w.w_symbol, A_FLOAT);
+ else FIELDDESC_SETFLOAT(fd, argv->a_w.w_float);
+}
+
+static void fielddesc_setarrayarg(t_fielddesc *fd, int argc, t_atom *argv)
+{
+ if (argc <= 0) FIELDDESC_SETFLOAT(fd, 0);
+ else if (argv->a_type == A_SYMBOL)
+ FIELDDESC_SETVAR(fd, argv->a_w.w_symbol, A_ARRAY);
+ else FIELDDESC_SETFLOAT(fd, argv->a_w.w_float);
+}
+
+static t_float fielddesc_getfloat(t_fielddesc *f, t_template *template,
+ t_word *wp, int loud)
+{
+ if (f->fd_type == A_FLOAT)
+ {
+ if (f->fd_var)
+ return (template_getfloat(template, f->fd_un.fd_varsym, wp, loud));
+ else return (f->fd_un.fd_float);
+ }
+ else
+ {
+ if (loud)
+ error("symbolic data field used as number");
+ return (0);
+ }
+}
+
+static t_symbol *fielddesc_getsymbol(t_fielddesc *f, t_template *template,
+ t_word *wp, int loud)
+{
+ if (f->fd_type == A_SYMBOL)
+ {
+ if (f->fd_var)
+ return(template_getsymbol(template, f->fd_un.fd_varsym, wp, loud));
+ else return (f->fd_un.fd_symbol);
+ }
+ else
+ {
+ if (loud)
+ error("numeric data field used as symbol");
+ return (&s_);
+ }
+}
+
+/* ---------------- curves and polygons (joined segments) ---------------- */
+
+/*
+curves belong to templates and describe how the data in the template are to
+be drawn. The coordinates of the curve (and other display features) can
+be attached to fields in the template.
+*/
+
+t_class *curve_class;
+
+typedef struct _curve
+{
+ t_object x_obj;
+ int x_flags; /* CLOSED and/or BEZ */
+ t_fielddesc x_fillcolor;
+ t_fielddesc x_outlinecolor;
+ t_fielddesc x_width;
+ int x_npoints;
+ t_fielddesc *x_vec;
+} t_curve;
+
+static void *curve_new(t_symbol *classsym, t_int argc, t_atom *argv)
+{
+ t_curve *x = (t_curve *)pd_new(curve_class);
+ char *classname = classsym->s_name;
+ int flags = 0;
+ int nxy, i;
+ t_fielddesc *fd;
+ if (classname[0] == 'f')
+ {
+ classname += 6;
+ flags |= CLOSED;
+ if (argc) fielddesc_setfloatarg(&x->x_fillcolor, argc--, argv++);
+ else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0);
+ }
+ else classname += 4;
+ if (classname[0] == 'c') flags |= BEZ;
+ x->x_flags = flags;
+ if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++);
+ else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0);
+ if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++);
+ else FIELDDESC_SETFLOAT(&x->x_width, 1);
+ if (argc < 0) argc = 0;
+ nxy = (argc + (argc & 1));
+ x->x_npoints = (nxy>>1);
+ x->x_vec = (t_fielddesc *)t_getbytes(nxy * sizeof(t_fielddesc));
+ for (i = 0, fd = x->x_vec; i < argc; i++, fd++, argv++)
+ fielddesc_setfloatarg(fd, 1, argv);
+ if (argc & 1) FIELDDESC_SETFLOAT(fd, 0);
+
+ return (x);
+}
+
+/* -------------------- widget behavior for curve ------------ */
+
+static void curve_getrect(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_curve *x = (t_curve *)z;
+ int i, n = x->x_npoints;
+ t_fielddesc *f = x->x_vec;
+ int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff;
+ for (i = 0, f = x->x_vec; i < n; i++, f += 2)
+ {
+ int xloc = glist_xtopixels(glist,
+ basex + fielddesc_getfloat(f, template, data, 0));
+ int yloc = glist_ytopixels(glist,
+ basey + fielddesc_getfloat(f+1, template, data, 0));
+ if (xloc < x1) x1 = xloc;
+ if (xloc > x2) x2 = xloc;
+ if (yloc < y1) y1 = yloc;
+ if (yloc > y2) y2 = yloc;
+ }
+ *xp1 = x1;
+ *yp1 = y1;
+ *xp2 = x2;
+ *yp2 = y2;
+}
+
+static void curve_displace(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int dx, int dy)
+{
+ /* refuse */
+}
+
+static void curve_select(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int state)
+{
+ /* fill in later */
+}
+
+static void curve_activate(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int state)
+{
+ /* fill in later */
+}
+
+static int rangecolor(int n) /* 0 to 9 in 5 steps */
+{
+ int n2 = n/2; /* 0 to 4 */
+ int ret = (n << 6); /* 0 to 256 in 5 steps */
+ if (ret > 255) ret = 255;
+ return (ret);
+}
+
+static void numbertocolor(int n, char *s)
+{
+ int red, blue, green;
+ if (n < 0) n = 0;
+ red = n / 100;
+ blue = ((n / 10) % 10);
+ green = n % 10;
+ sprintf(s, "#%2.2x%2.2x%2.2x", rangecolor(red), rangecolor(blue),
+ rangecolor(green));
+}
+
+static void curve_vis(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int vis)
+{
+ t_curve *x = (t_curve *)z;
+ int i, n = x->x_npoints;
+ t_fielddesc *f = x->x_vec;
+
+ if (vis)
+ {
+ if (n > 1)
+ {
+ int flags = x->x_flags, closed = (flags & CLOSED);
+ float width = fielddesc_getfloat(&x->x_width, template, data, 1);
+ char outline[20], fill[20];
+ if (width < 1) width = 1;
+ numbertocolor(
+ fielddesc_getfloat(&x->x_outlinecolor, template, data, 1),
+ outline);
+ if (flags & CLOSED)
+ {
+ numbertocolor(
+ fielddesc_getfloat(&x->x_fillcolor, template, data, 1),
+ fill);
+ sys_vgui(".x%x.c create polygon\\\n",
+ glist_getcanvas(glist));
+ }
+ else sys_vgui(".x%x.c create line\\\n",
+ glist_getcanvas(glist));
+ for (i = 0, f = x->x_vec; i < n; i++, f += 2)
+ {
+ float xloc = glist_xtopixels(glist,
+ basex + fielddesc_getfloat(f, template, data, 1));
+ float yloc = glist_ytopixels(glist,
+ basey + fielddesc_getfloat(f+1, template, data, 1));
+ sys_vgui("%d %d\\\n", (int)xloc, (int)yloc);
+ }
+ sys_vgui("-width %f\\\n",
+ fielddesc_getfloat(&x->x_width, template, data, 1));
+ if (flags & CLOSED) sys_vgui("-fill %s -outline %s\\\n",
+ fill, outline);
+ else sys_vgui("-fill %s\\\n", outline);
+ if (flags & BEZ) sys_vgui("-smooth 1\\\n");
+ sys_vgui("-tags curve%x\n", data);
+ }
+ else post("warning: curves need at least two points to be graphed");
+ }
+ else
+ {
+ if (n > 1) sys_vgui(".x%x.c delete curve%x\n",
+ glist_getcanvas(glist), data);
+ }
+}
+
+static int curve_motion_field;
+static float curve_motion_xcumulative;
+static float curve_motion_xbase;
+static float curve_motion_xper;
+static float curve_motion_ycumulative;
+static float curve_motion_ybase;
+static float curve_motion_yper;
+static t_glist *curve_motion_glist;
+static t_gobj *curve_motion_gobj;
+static t_word *curve_motion_wp;
+static t_template *curve_motion_template;
+
+ /* LATER protect against the template changing or the scalar disappearing
+ probably by attaching a gpointer here ... */
+
+static void curve_motion(void *z, t_floatarg dx, t_floatarg dy)
+{
+ t_curve *x = (t_curve *)z;
+ t_fielddesc *f = x->x_vec + curve_motion_field;
+ curve_motion_xcumulative += dx;
+ curve_motion_ycumulative += dy;
+ if (f->fd_var)
+ {
+ template_setfloat(curve_motion_template,
+ f->fd_un.fd_varsym,
+ curve_motion_wp,
+ curve_motion_xbase + curve_motion_xcumulative * curve_motion_xper,
+ 1);
+ }
+ if ((f+1)->fd_var)
+ {
+ template_setfloat(curve_motion_template,
+ (f+1)->fd_un.fd_varsym,
+ curve_motion_wp,
+ curve_motion_ybase + curve_motion_ycumulative * curve_motion_yper,
+ 1);
+ }
+ glist_redrawitem(curve_motion_glist, curve_motion_gobj);
+}
+
+static int curve_click(t_gobj *z, t_glist *glist,
+ t_scalar *sc, t_template *template, float basex, float basey,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ t_curve *x = (t_curve *)z;
+ int i, n = x->x_npoints;
+ int bestn = -1;
+ int besterror = 0x7fffffff;
+ t_fielddesc *f = x->x_vec;
+ t_word *data = sc->sc_vec;
+ for (i = 0, f = x->x_vec; i < n; i++, f += 2)
+ {
+ int xloc = glist_xtopixels(glist,
+ basex + fielddesc_getfloat(f, template, data, 0));
+ int yloc = glist_ytopixels(glist,
+ basey + fielddesc_getfloat(f+1, template, data, 0));
+ int xerr = xloc - xpix, yerr = yloc - ypix;
+ if (!f->fd_var && !(f+1)->fd_var)
+ continue;
+ if (xerr < 0)
+ xerr = -xerr;
+ if (yerr < 0)
+ yerr = -yerr;
+ if (yerr > xerr)
+ xerr = yerr;
+ if (xerr < besterror)
+ {
+ besterror = xerr;
+ bestn = i;
+ curve_motion_xbase = fielddesc_getfloat(f, template, data, 0);
+ curve_motion_ybase = fielddesc_getfloat(f+1, template, data, 0);
+ }
+ }
+ if (besterror > 10)
+ return (0);
+ if (doit)
+ {
+ curve_motion_xper = glist_pixelstox(glist, 1)
+ - glist_pixelstox(glist, 0);
+ curve_motion_yper = glist_pixelstoy(glist, 1)
+ - glist_pixelstoy(glist, 0);
+ curve_motion_xcumulative = curve_motion_ycumulative = 0;
+ curve_motion_glist = glist;
+ curve_motion_gobj = &sc->sc_gobj;
+ curve_motion_wp = data;
+ curve_motion_field = 2*bestn;
+ curve_motion_template = template;
+ glist_grab(glist, z, curve_motion, 0, xpix, ypix);
+ }
+ return (1);
+}
+
+t_parentwidgetbehavior curve_widgetbehavior =
+{
+ curve_getrect,
+ curve_displace,
+ curve_select,
+ curve_activate,
+ curve_vis,
+ curve_click,
+};
+
+static void curve_free(t_curve *x)
+{
+ t_freebytes(x->x_vec, 2 * x->x_npoints * sizeof(*x->x_vec));
+}
+
+static void curve_setup(void)
+{
+ curve_class = class_new(gensym("drawpolygon"), (t_newmethod)curve_new,
+ (t_method)curve_free, sizeof(t_curve), CLASS_NOINLET, A_GIMME, 0);
+ class_setdrawcommand(curve_class);
+ class_addcreator((t_newmethod)curve_new, gensym("drawcurve"),
+ A_GIMME, 0);
+ class_addcreator((t_newmethod)curve_new, gensym("filledpolygon"),
+ A_GIMME, 0);
+ class_addcreator((t_newmethod)curve_new, gensym("filledcurve"),
+ A_GIMME, 0);
+ class_setparentwidget(curve_class, &curve_widgetbehavior);
+}
+
+/* --------- plots for showing arrays --------------- */
+
+t_class *plot_class;
+
+typedef struct _plot
+{
+ t_object x_obj;
+ int x_flags;
+ t_fielddesc x_outlinecolor;
+ t_fielddesc x_width;
+ t_fielddesc x_xloc;
+ t_fielddesc x_yloc;
+ t_fielddesc x_xinc;
+ t_fielddesc x_data;
+} t_plot;
+
+static void *plot_new(t_symbol *classsym, t_int argc, t_atom *argv)
+{
+ t_plot *x = (t_plot *)pd_new(plot_class);
+ int flags = 0;
+ int nxy, i;
+ t_fielddesc *fd;
+ t_symbol *firstarg = atom_getsymbolarg(0, argc, argv);
+ if (!strcmp(firstarg->s_name, "curve"))
+ {
+ flags |= BEZ;
+ argc--, argv++;
+ }
+ if (argc) fielddesc_setarrayarg(&x->x_data, argc--, argv++);
+ else FIELDDESC_SETFLOAT(&x->x_data, 1);
+ if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++);
+ else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0);
+ if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++);
+ else FIELDDESC_SETFLOAT(&x->x_width, 1);
+ if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++);
+ else FIELDDESC_SETFLOAT(&x->x_xloc, 1);
+ if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++);
+ else FIELDDESC_SETFLOAT(&x->x_yloc, 1);
+ if (argc) fielddesc_setfloatarg(&x->x_xinc, argc--, argv++);
+ else FIELDDESC_SETFLOAT(&x->x_xinc, 1);
+ x->x_flags = flags;
+ return (x);
+}
+
+/* -------------------- widget behavior for plot ------------ */
+
+
+ /* get everything we'll need from the owner template of the array being
+ plotted. Not used for garrays, but see below */
+static int plot_readownertemplate(t_plot *x,
+ t_word *data, t_template *ownertemplate,
+ t_symbol **elemtemplatesymp, t_array **arrayp,
+ float *linewidthp, float *xlocp, float *xincp, float *ylocp)
+{
+ int arrayonset, type;
+ t_symbol *elemtemplatesym;
+ t_array *array;
+
+ /* find the data and verify it's an array */
+ if (x->x_data.fd_type != A_ARRAY || !x->x_data.fd_var)
+ {
+ error("plot: needs an array field");
+ return (-1);
+ }
+ if (!template_find_field(ownertemplate, x->x_data.fd_un.fd_varsym,
+ &arrayonset, &type, &elemtemplatesym))
+ {
+ error("plot: %s: no such field", x->x_data.fd_un.fd_varsym->s_name);
+ return (-1);
+ }
+ if (type != DT_ARRAY)
+ {
+ error("plot: %s: not an array", x->x_data.fd_un.fd_varsym->s_name);
+ return (-1);
+ }
+ array = *(t_array **)(((char *)data) + arrayonset);
+ *linewidthp = fielddesc_getfloat(&x->x_width, ownertemplate, data, 1);
+ *xlocp = fielddesc_getfloat(&x->x_xloc, ownertemplate, data, 1);
+ *xincp = fielddesc_getfloat(&x->x_xinc, ownertemplate, data, 1);
+ *ylocp = fielddesc_getfloat(&x->x_yloc, ownertemplate, data, 1);
+ *elemtemplatesymp = elemtemplatesym;
+ *arrayp = array;
+ return (0);
+}
+
+ /* get everything else you could possibly need about a plot,
+ either for plot's own purposes or for plotting a "garray" */
+int array_getfields(t_symbol *elemtemplatesym,
+ t_canvas **elemtemplatecanvasp,
+ t_template **elemtemplatep, int *elemsizep,
+ int *xonsetp, int *yonsetp, int *wonsetp)
+{
+ int arrayonset, elemsize, yonset, wonset, xonset, type;
+ t_template *elemtemplate;
+ t_symbol *dummy;
+ t_canvas *elemtemplatecanvas = 0;
+
+ /* the "float" template is special in not having to have a canvas;
+ template_findbyname is hardwired to return a predefined
+ template. */
+
+ if (!(elemtemplate = template_findbyname(elemtemplatesym)))
+ {
+ error("plot: %s: no such template", elemtemplatesym->s_name);
+ return (-1);
+ }
+ if (!((elemtemplatesym == &s_float) ||
+ (elemtemplatecanvas = template_findcanvas(elemtemplate))))
+ {
+ error("plot: %s: no canvas for this template", elemtemplatesym->s_name);
+ return (-1);
+ }
+ elemsize = elemtemplate->t_n * sizeof(t_word);
+ if (!template_find_field(elemtemplate, gensym("y"), &yonset, &type, &dummy)
+ || type != DT_FLOAT)
+ yonset = -1;
+ if (!template_find_field(elemtemplate, gensym("x"), &xonset, &type, &dummy)
+ || type != DT_FLOAT)
+ xonset = -1;
+ if (!template_find_field(elemtemplate, gensym("w"), &wonset, &type, &dummy)
+ || type != DT_FLOAT)
+ wonset = -1;
+
+ /* fill in slots for return values */
+ *elemtemplatecanvasp = elemtemplatecanvas;
+ *elemtemplatep = elemtemplate;
+ *elemsizep = elemsize;
+ *xonsetp = xonset;
+ *yonsetp = yonset;
+ *wonsetp = wonset;
+ return (0);
+}
+
+static void plot_getrect(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_plot *x = (t_plot *)z;
+ int elemsize, yonset, wonset, xonset;
+ t_canvas *elemtemplatecanvas;
+ t_template *elemtemplate;
+ t_symbol *elemtemplatesym;
+ float linewidth, xloc, xinc, yloc;
+ t_array *array;
+ float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff;
+ int i;
+ float xpix, ypix, wpix;
+
+ if (!plot_readownertemplate(x, data, template,
+ &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc) &&
+ !array_getfields(elemtemplatesym, &elemtemplatecanvas,
+ &elemtemplate, &elemsize, &xonset, &yonset, &wonset))
+ {
+ for (i = 0; i < array->a_n; i++)
+ {
+ array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize,
+ xonset, yonset, wonset, i, basex + xloc, basey + yloc, xinc,
+ &xpix, &ypix, &wpix);
+ if (xpix < x1)
+ x1 = xpix;
+ if (xpix > x2)
+ x2 = xpix;
+ if (ypix - wpix < y1)
+ y1 = ypix - wpix;
+ if (ypix + wpix > y2)
+ y2 = ypix + wpix;
+ }
+ }
+
+ *xp1 = x1;
+ *yp1 = y1;
+ *xp2 = x2;
+ *yp2 = y2;
+}
+
+static void plot_displace(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int dx, int dy)
+{
+ /* not yet */
+}
+
+static void plot_select(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int state)
+{
+ /* not yet */
+}
+
+static void plot_activate(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int state)
+{
+ /* not yet */
+}
+
+static void plot_vis(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int vis)
+{
+ t_plot *x = (t_plot *)z;
+ int elemsize, yonset, wonset, xonset;
+ t_canvas *elemtemplatecanvas;
+ t_template *elemtemplate;
+ t_symbol *elemtemplatesym;
+ float linewidth, xloc, xinc, yloc;
+ t_array *array;
+ int nelem;
+ char *elem;
+ if (plot_readownertemplate(x, data, template,
+ &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc) ||
+ array_getfields(elemtemplatesym, &elemtemplatecanvas,
+ &elemtemplate, &elemsize, &xonset, &yonset, &wonset))
+ return;
+ nelem = array->a_n;
+ elem = (char *)array->a_vec;
+ if (vis)
+ {
+ char outline[20];
+ int lastpixel = -1, ndrawn = 0;
+ float xsum, yval = 0, wval = 0, xpix;
+ int ixpix = 0, i;
+
+ /* draw the trace */
+ numbertocolor(fielddesc_getfloat(&x->x_outlinecolor, template, data, 1),
+ outline);
+ if (wonset >= 0)
+ {
+ /* found "w" field which controls linewidth. The trace is
+ a filled polygon with 2n points. */
+ sys_vgui(".x%x.c create polygon \\\n",
+ glist_getcanvas(glist));
+
+ for (i = 0, xsum = xloc; i < nelem; i++)
+ {
+ float usexloc;
+ if (xonset >= 0)
+ usexloc = xloc + *(float *)((elem + elemsize * i) + xonset);
+ else usexloc = xsum, xsum += xinc;
+ if (yonset >= 0)
+ yval = *(float *)((elem + elemsize * i) + yonset);
+ else yval = 0;
+ wval = *(float *)((elem + elemsize * i) + wonset);
+ xpix = glist_xtopixels(glist, basex + usexloc);
+ ixpix = xpix + 0.5;
+ if (xonset >= 0 || ixpix != lastpixel)
+ {
+ sys_vgui("%d %f \\\n", ixpix,
+ glist_ytopixels(glist,
+ basey + yloc + yval - wval));
+ ndrawn++;
+ }
+ lastpixel = ixpix;
+ if (ndrawn >= 1000) goto ouch;
+ }
+ lastpixel = -1;
+ for (i = nelem-1; i >= 0; i--)
+ {
+ float usexloc;
+ if (xonset >= 0)
+ usexloc = xloc + *(float *)((elem + elemsize * i) + xonset);
+ else xsum -= xinc, usexloc = xsum;
+ if (yonset >= 0)
+ yval = *(float *)((elem + elemsize * i) + yonset);
+ else yval = 0;
+ wval = *(float *)((elem + elemsize * i) + wonset);
+ xpix = glist_xtopixels(glist, basex + usexloc);
+ ixpix = xpix + 0.5;
+ if (xonset >= 0 || ixpix != lastpixel)
+ {
+ sys_vgui("%d %f \\\n", ixpix, glist_ytopixels(glist,
+ basey + yloc + yval + wval));
+ ndrawn++;
+ }
+ lastpixel = ixpix;
+ if (ndrawn >= 1000) goto ouch;
+ }
+ /* TK will complain if there aren't at least 3 points. There
+ should be at least two already. */
+ if (ndrawn < 4)
+ {
+ sys_vgui("%d %f \\\n", ixpix + 10, glist_ytopixels(glist,
+ basey + yloc + yval + wval));
+ sys_vgui("%d %f \\\n", ixpix + 10, glist_ytopixels(glist,
+ basey + yloc + yval - wval));
+ }
+ ouch:
+ sys_vgui(" -width 1 -fill %s -outline %s\\\n", outline, outline);
+ if (x->x_flags & BEZ) sys_vgui("-smooth 1\\\n");
+
+ sys_vgui("-tags plot%x\n", data);
+ }
+ else if (linewidth > 0)
+ {
+ /* no "w" field. If the linewidth is positive, draw a
+ segmented line with the requested width; otherwise don't
+ draw the trace at all. */
+ sys_vgui(".x%x.c create line \\\n", glist_getcanvas(glist));
+
+ for (xsum = xloc, i = 0; i < nelem; i++)
+ {
+ float usexloc;
+ if (xonset >= 0)
+ usexloc = xloc + *(float *)((elem + elemsize * i) + xonset);
+ else usexloc = xsum, xsum += xinc;
+ if (yonset >= 0)
+ yval = *(float *)((elem + elemsize * i) + yonset);
+ else yval = 0;
+ xpix = glist_xtopixels(glist, basex + usexloc);
+ ixpix = xpix + 0.5;
+ if (xonset >= 0 || ixpix != lastpixel)
+ {
+ sys_vgui("%d %f \\\n", ixpix,
+ glist_ytopixels(glist, basey + yloc + yval));
+ ndrawn++;
+ }
+ lastpixel = ixpix;
+ if (ndrawn >= 1000) break;
+ }
+ /* TK will complain if there aren't at least 2 points... */
+ if (ndrawn == 0) sys_vgui("0 0 0 0 \\\n");
+ else if (ndrawn == 1) sys_vgui("%d %f \\\n", ixpix + 10,
+ glist_ytopixels(glist, basey + yloc + yval));
+
+ sys_vgui("-width %f\\\n", linewidth);
+ sys_vgui("-fill %s\\\n", outline);
+ if (x->x_flags & BEZ) sys_vgui("-smooth 1\\\n");
+
+ sys_vgui("-tags plot%x\n", data);
+ }
+ /* We're done with the outline; now draw all the points.
+ This code is inefficient since the template has to be
+ searched for drawing instructions for every last point. */
+
+ for (xsum = xloc, i = 0; i < nelem; i++)
+ {
+ float usexloc, useyloc;
+ t_gobj *y;
+ if (xonset >= 0)
+ usexloc = basex + xloc +
+ *(float *)((elem + elemsize * i) + xonset);
+ else usexloc = basex + xsum, xsum += xinc;
+ if (yonset >= 0)
+ yval = *(float *)((elem + elemsize * i) + yonset);
+ else yval = 0;
+ useyloc = basey + yloc + yval;
+ for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)
+ {
+ t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
+ if (!wb) continue;
+ (*wb->w_parentvisfn)(y, glist,
+ (t_word *)(elem + elemsize * i),
+ elemtemplate, usexloc, useyloc, vis);
+ }
+ }
+ }
+ else
+ {
+ /* un-draw the individual points */
+ int i;
+ for (i = 0; i < nelem; i++)
+ {
+ t_gobj *y;
+ for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)
+ {
+ t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
+ if (!wb) continue;
+ (*wb->w_parentvisfn)(y, glist,
+ (t_word *)(elem + elemsize * i), elemtemplate,
+ 0, 0, 0);
+ }
+ }
+ /* and then the trace */
+ sys_vgui(".x%x.c delete plot%x\n",
+ glist_getcanvas(glist), data);
+ }
+}
+
+
+static int plot_click(t_gobj *z, t_glist *glist,
+ t_scalar *sc, t_template *template, float basex, float basey,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ t_plot *x = (t_plot *)z;
+ t_symbol *elemtemplatesym;
+ float linewidth, xloc, xinc, yloc;
+ t_array *array;
+ t_word *data = sc->sc_vec;
+
+ if (!plot_readownertemplate(x, data, template,
+ &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc))
+ {
+ return (array_doclick(array, glist, &sc->sc_gobj,
+ elemtemplatesym,
+ linewidth, basex + xloc, xinc, basey + yloc,
+ xpix, ypix, shift, alt, dbl, doit));
+ }
+ else return (0);
+}
+
+t_parentwidgetbehavior plot_widgetbehavior =
+{
+ plot_getrect,
+ plot_displace,
+ plot_select,
+ plot_activate,
+ plot_vis,
+ plot_click,
+};
+
+static void plot_setup(void)
+{
+ plot_class = class_new(gensym("plot"), (t_newmethod)plot_new, 0,
+ sizeof(t_plot), CLASS_NOINLET, A_GIMME, 0);
+ class_setdrawcommand(plot_class);
+ class_setparentwidget(plot_class, &plot_widgetbehavior);
+}
+
+/* ---------------- drawnumber: draw a number ---------------- */
+
+/*
+ drawnumbers draw numeric fields at controllable locations, with
+ controllable color and label .
+ invocation: (drawnumber|drawsymbol) variable x y color label
+*/
+
+t_class *drawnumber_class;
+
+#define DRAW_SYMBOL 1
+
+typedef struct _drawnumber
+{
+ t_object x_obj;
+ t_fielddesc x_value;
+ t_fielddesc x_xloc;
+ t_fielddesc x_yloc;
+ t_fielddesc x_color;
+ t_symbol *x_label;
+ int x_flags;
+} t_drawnumber;
+
+static void *drawnumber_new(t_symbol *classsym, t_int argc, t_atom *argv)
+{
+ t_drawnumber *x = (t_drawnumber *)pd_new(drawnumber_class);
+ char *classname = classsym->s_name;
+ int flags = 0;
+ if (classname[4] == 's')
+ flags |= DRAW_SYMBOL;
+ x->x_flags = flags;
+ if (argc) fielddesc_setfloatarg(&x->x_value, argc--, argv++);
+ else FIELDDESC_SETFLOAT(&x->x_value, 0);
+ if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++);
+ else FIELDDESC_SETFLOAT(&x->x_xloc, 0);
+ if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++);
+ else FIELDDESC_SETFLOAT(&x->x_yloc, 0);
+ if (argc) fielddesc_setfloatarg(&x->x_color, argc--, argv++);
+ else FIELDDESC_SETFLOAT(&x->x_color, 1);
+ if (argc)
+ x->x_label = atom_getsymbolarg(0, argc, argv);
+ else x->x_label = &s_;
+
+ return (x);
+}
+
+/* -------------------- widget behavior for drawnumber ------------ */
+
+#define DRAWNUMBER_BUFSIZE 80
+static void drawnumber_sprintf(t_drawnumber *x, char *buf, t_atom *ap)
+{
+ int nchars;
+ strncpy(buf, x->x_label->s_name, DRAWNUMBER_BUFSIZE);
+ buf[DRAWNUMBER_BUFSIZE - 1] = 0;
+ nchars = strlen(buf);
+ atom_string(ap, buf + nchars, DRAWNUMBER_BUFSIZE - nchars);
+}
+
+static void drawnumber_getrect(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_drawnumber *x = (t_drawnumber *)z;
+ t_atom at;
+ int xloc = glist_xtopixels(glist,
+ basex + fielddesc_getfloat(&x->x_xloc, template, data, 0));
+ int yloc = glist_ytopixels(glist,
+ basey + fielddesc_getfloat(&x->x_yloc, template, data, 0));
+ int font = glist_getfont(glist);
+ int fontwidth = sys_fontwidth(font), fontheight = sys_fontheight(font);
+ char buf[DRAWNUMBER_BUFSIZE];
+ if (x->x_flags & DRAW_SYMBOL)
+ SETSYMBOL(&at, fielddesc_getsymbol(&x->x_value, template, data, 0));
+ else SETFLOAT(&at, fielddesc_getfloat(&x->x_value, template, data, 0));
+ drawnumber_sprintf(x, buf, &at);
+ *xp1 = xloc;
+ *yp1 = yloc;
+ *xp2 = xloc + fontwidth * strlen(buf);
+ *yp2 = yloc + fontheight;
+}
+
+static void drawnumber_displace(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int dx, int dy)
+{
+ /* refuse */
+}
+
+static void drawnumber_select(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int state)
+{
+ post("drawnumber_select %d", state);
+ /* fill in later */
+}
+
+static void drawnumber_activate(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int state)
+{
+ post("drawnumber_activate %d", state);
+}
+
+static void drawnumber_vis(t_gobj *z, t_glist *glist,
+ t_word *data, t_template *template, float basex, float basey,
+ int vis)
+{
+ t_drawnumber *x = (t_drawnumber *)z;
+
+ if (vis)
+ {
+ t_atom at;
+ int xloc = glist_xtopixels(glist,
+ basex + fielddesc_getfloat(&x->x_xloc, template, data, 0));
+ int yloc = glist_ytopixels(glist,
+ basey + fielddesc_getfloat(&x->x_yloc, template, data, 0));
+ char colorstring[20], buf[DRAWNUMBER_BUFSIZE];
+ numbertocolor(fielddesc_getfloat(&x->x_color, template, data, 1),
+ colorstring);
+ if (x->x_flags & DRAW_SYMBOL)
+ SETSYMBOL(&at, fielddesc_getsymbol(&x->x_value, template, data, 0));
+ else SETFLOAT(&at, fielddesc_getfloat(&x->x_value, template, data, 0));
+ drawnumber_sprintf(x, buf, &at);
+ sys_vgui(".x%x.c create text %d %d -anchor nw -fill %s -text {%s}",
+ glist_getcanvas(glist), xloc, yloc, colorstring, buf);
+ sys_vgui(" -font -*-courier-bold--normal--%d-*",
+ sys_hostfontsize(glist_getfont(glist)));
+ sys_vgui(" -tags drawnumber%x\n", data);
+ }
+ else sys_vgui(".x%x.c delete drawnumber%x\n", glist_getcanvas(glist), data);
+}
+
+static float drawnumber_motion_ycumulative;
+static t_glist *drawnumber_motion_glist;
+static t_gobj *drawnumber_motion_gobj;
+static t_word *drawnumber_motion_wp;
+static t_template *drawnumber_motion_template;
+
+ /* LATER protect against the template changing or the scalar disappearing
+ probably by attaching a gpointer here ... */
+
+static void drawnumber_motion(void *z, t_floatarg dx, t_floatarg dy)
+{
+ t_drawnumber *x = (t_drawnumber *)z;
+ t_fielddesc *f = &x->x_value;
+ drawnumber_motion_ycumulative -= dy;
+ template_setfloat(drawnumber_motion_template,
+ f->fd_un.fd_varsym,
+ drawnumber_motion_wp,
+ drawnumber_motion_ycumulative,
+ 1);
+ glist_redrawitem(drawnumber_motion_glist, drawnumber_motion_gobj);
+}
+
+static int drawnumber_click(t_gobj *z, t_glist *glist,
+ t_scalar *sc, t_template *template, float basex, float basey,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ t_drawnumber *x = (t_drawnumber *)z;
+ int x1, y1, x2, y2;
+ t_word *data = sc->sc_vec;
+ drawnumber_getrect(z, glist,
+ sc->sc_vec, template, basex, basey,
+ &x1, &y1, &x2, &y2);
+ if (xpix >= x1 && xpix <= x2 && ypix >= y1 && ypix <= y2
+ && x->x_value.fd_var)
+ {
+ if (doit)
+ {
+ drawnumber_motion_glist = glist;
+ drawnumber_motion_gobj = &sc->sc_gobj;
+ drawnumber_motion_wp = data;
+ drawnumber_motion_template = template;
+ drawnumber_motion_ycumulative =
+ fielddesc_getfloat(&x->x_value, template, data, 0);
+ glist_grab(glist, z, drawnumber_motion, 0, xpix, ypix);
+ }
+ return (1);
+ }
+ else return (0);
+}
+
+t_parentwidgetbehavior drawnumber_widgetbehavior =
+{
+ drawnumber_getrect,
+ drawnumber_displace,
+ drawnumber_select,
+ drawnumber_activate,
+ drawnumber_vis,
+ drawnumber_click,
+};
+
+static void drawnumber_free(t_drawnumber *x)
+{
+}
+
+static void drawnumber_setup(void)
+{
+ drawnumber_class = class_new(gensym("drawnumber"),
+ (t_newmethod)drawnumber_new, (t_method)drawnumber_free,
+ sizeof(t_drawnumber), CLASS_NOINLET, A_GIMME, 0);
+ class_setdrawcommand(drawnumber_class);
+ class_addcreator((t_newmethod)drawnumber_new, gensym("drawsymbol"),
+ A_GIMME, 0);
+ class_setparentwidget(drawnumber_class, &drawnumber_widgetbehavior);
+}
+
+/* ---------------------- setup function ---------------------------- */
+
+void g_template_setup(void)
+{
+ template_setup();
+ gtemplate_setup();
+ template_float.t_pd = template_class;
+ curve_setup();
+ plot_setup();
+ drawnumber_setup();
+}
+
diff --git a/pd/src/g_text.c b/pd/src/g_text.c
new file mode 100644
index 00000000..226ddc7a
--- /dev/null
+++ b/pd/src/g_text.c
@@ -0,0 +1,1070 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* changes by Thomas Musil IEM KUG Graz Austria 2001 */
+/* the methods for calling the gui-objects from menu are implemented */
+/* all changes are labeled with iemlib */
+
+#include <stdlib.h>
+#include "m_imp.h"
+#include "t_tk.h"
+#include "g_canvas.h"
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+static t_class *text_class;
+static t_class *message_class;
+static t_class *gatom_class;
+void canvas_startmotion(t_canvas *x);
+t_widgetbehavior text_widgetbehavior;
+
+/* ----------------- the "text" object. ------------------ */
+
+
+ /* add a "text" object (comment) to a glist. While this one goes for any glist,
+ the other 3 below are for canvases only. (why?) This is called
+ without args if invoked from the GUI; otherwise at least x and y
+ are provided. */
+
+void glist_text(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ t_text *x = (t_text *)pd_new(text_class);
+ t_atom at;
+ x->te_width = 0; /* don't know it yet. */
+ x->te_type = T_TEXT;
+ x->te_binbuf = binbuf_new();
+ if (argc > 1)
+ {
+ x->te_xpix = atom_getfloatarg(0, argc, argv);
+ x->te_ypix = atom_getfloatarg(1, argc, argv);
+ if (argc > 2) binbuf_restore(x->te_binbuf, argc-2, argv+2);
+ else
+ {
+ SETSYMBOL(&at, gensym("comment"));
+ binbuf_restore(x->te_binbuf, 1, &at);
+ }
+ glist_add(gl, &x->te_g);
+ }
+ else
+ {
+ int xpix, ypix;
+ pd_vmess((t_pd *)glist_getcanvas(gl), gensym("editmode"), "i", 1);
+ SETSYMBOL(&at, gensym("comment"));
+ glist_noselect(gl);
+ glist_getnextxy(gl, &xpix, &ypix);
+ x->te_xpix = glist_pixelstox(gl, xpix-3);
+ x->te_ypix = glist_pixelstoy(gl, ypix-3);
+ binbuf_restore(x->te_binbuf, 1, &at);
+ glist_add(gl, &x->te_g);
+ glist_noselect(gl);
+ glist_select(gl, &x->te_g);
+ canvas_startmotion(glist_getcanvas(gl));
+ }
+}
+
+/* ----------------- the "object" object. ------------------ */
+
+extern t_pd *newest;
+void canvas_getargs(int *argcp, t_atom **argvp);
+
+static void canvas_objtext(t_glist *gl, int xpix, int ypix, int selected,
+ t_binbuf *b)
+{
+ t_text *x;
+ int argc;
+ t_atom *argv;
+ newest = 0;
+ canvas_setcurrent((t_canvas *)gl);
+ canvas_getargs(&argc, &argv);
+ binbuf_eval(b, &pd_objectmaker, argc, argv);
+ if (binbuf_getnatom(b))
+ {
+ if (!newest)
+ {
+ binbuf_print(b);
+ post("... couldn't create");
+ x = 0;
+ }
+ else if (!(x = pd_checkobject(newest)))
+ {
+ binbuf_print(b);
+ post("... didn't return a patchable object");
+ }
+ }
+ else x = 0;
+ if (!x)
+ {
+
+ /* LATER make the color reflect this */
+ x = (t_text *)pd_new(text_class);
+ }
+ x->te_binbuf = b;
+ x->te_xpix = xpix;
+ x->te_ypix = ypix;
+ x->te_width = 0;
+ x->te_type = T_OBJECT;
+ glist_add(gl, &x->te_g);
+ if (selected)
+ {
+ /* this is called if we've been created from the menu. */
+ glist_select(gl, &x->te_g);
+ gobj_activate(&x->te_g, gl, 1);
+ }
+ if (pd_class(&x->ob_pd) == vinlet_class)
+ canvas_resortinlets(glist_getcanvas(gl));
+ if (pd_class(&x->ob_pd) == voutlet_class)
+ canvas_resortoutlets(glist_getcanvas(gl));
+ canvas_unsetcurrent((t_canvas *)gl);
+}
+
+ /* object creation routine. These are called without any arguments if
+ they're invoked from the
+ gui; when pasting or restoring from a file, we get at least x and y. */
+
+void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ t_text *x;
+ if (argc >= 2)
+ {
+ t_binbuf *b = binbuf_new();
+ binbuf_restore(b, argc-2, argv+2);
+ canvas_objtext(gl, atom_getintarg(0, argc, argv),
+ atom_getintarg(1, argc, argv), 0, b);
+ }
+ else
+ {
+ t_binbuf *b = binbuf_new();
+ int xpix, ypix;
+ pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1);
+ glist_noselect(gl);
+ glist_getnextxy(gl, &xpix, &ypix);
+ canvas_objtext(gl, xpix, ypix, 1, b);
+ canvas_startmotion(glist_getcanvas(gl));
+ }
+}
+
+/* make an object box for an object that's already there. */
+
+/* iemlib */
+void canvas_iemguis(t_glist *gl, t_symbol *guiobjname)
+{
+ t_atom at;
+ t_binbuf *b = binbuf_new();
+ int xpix, ypix;
+
+ pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1);
+ glist_noselect(gl);
+ SETSYMBOL(&at, guiobjname);
+ binbuf_restore(b, 1, &at);
+ glist_getnextxy(gl, &xpix, &ypix);
+ canvas_objtext(gl, xpix, ypix, 1, b);
+ canvas_startmotion(glist_getcanvas(gl));
+}
+
+void canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ canvas_iemguis(gl, gensym("bng"));
+}
+
+void canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ canvas_iemguis(gl, gensym("tgl"));
+}
+
+void canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ canvas_iemguis(gl, gensym("vsl"));
+}
+
+void canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ canvas_iemguis(gl, gensym("hsl"));
+}
+
+void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ canvas_iemguis(gl, gensym("hdl"));
+}
+
+void canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ canvas_iemguis(gl, gensym("vdl"));
+}
+
+void canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ canvas_iemguis(gl, gensym("vu"));
+}
+
+void canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ canvas_iemguis(gl, gensym("cnv"));
+}
+
+void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ canvas_iemguis(gl, gensym("nbx"));
+}
+
+/* iemlib */
+
+void canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv)
+{
+ x->te_width = 0; /* don't know it yet. */
+ x->te_type = T_OBJECT;
+ x->te_binbuf = binbuf_new();
+ x->te_xpix = atom_getfloatarg(0, argc, argv);
+ x->te_ypix = atom_getfloatarg(1, argc, argv);
+ if (argc > 2) binbuf_restore(x->te_binbuf, argc-2, argv+2);
+ glist_add(gl, &x->te_g);
+}
+
+/* ---------------------- the "message" text item ------------------------ */
+
+typedef struct _messresponder
+{
+ t_pd mr_pd;
+ t_outlet *mr_outlet;
+} t_messresponder;
+
+typedef struct _message
+{
+ t_text m_text;
+ t_messresponder m_messresponder;
+ t_glist *m_glist;
+ t_clock *m_clock;
+} t_message;
+
+static t_class *message_class, *messresponder_class;
+
+static void messresponder_bang(t_messresponder *x)
+{
+ outlet_bang(x->mr_outlet);
+}
+
+static void messresponder_float(t_messresponder *x, t_float f)
+{
+ outlet_float(x->mr_outlet, f);
+}
+
+static void messresponder_symbol(t_messresponder *x, t_symbol *s)
+{
+ outlet_symbol(x->mr_outlet, s);
+}
+
+static void messresponder_list(t_messresponder *x,
+ t_symbol *s, int argc, t_atom *argv)
+{
+ outlet_list(x->mr_outlet, s, argc, argv);
+}
+
+static void messresponder_anything(t_messresponder *x,
+ t_symbol *s, int argc, t_atom *argv)
+{
+ outlet_anything(x->mr_outlet, s, argc, argv);
+}
+
+static void message_bang(t_message *x)
+{
+ binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 0, 0);
+}
+
+static void message_float(t_message *x, t_float f)
+{
+ t_atom at;
+ SETFLOAT(&at, f);
+ binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at);
+}
+
+static void message_symbol(t_message *x, t_symbol *s)
+{
+ t_atom at;
+ SETSYMBOL(&at, s);
+ binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at);
+}
+
+static void message_list(t_message *x, t_symbol *s, int argc, t_atom *argv)
+{
+ binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, argc, argv);
+}
+
+static void message_set(t_message *x, t_symbol *s, int argc, t_atom *argv)
+{
+ binbuf_clear(x->m_text.te_binbuf);
+ binbuf_add(x->m_text.te_binbuf, argc, argv);
+ glist_retext(x->m_glist, &x->m_text);
+}
+
+static void message_add2(t_message *x, t_symbol *s, int argc, t_atom *argv)
+{
+ binbuf_add(x->m_text.te_binbuf, argc, argv);
+ glist_retext(x->m_glist, &x->m_text);
+}
+
+static void message_add(t_message *x, t_symbol *s, int argc, t_atom *argv)
+{
+ binbuf_add(x->m_text.te_binbuf, argc, argv);
+ binbuf_addsemi(x->m_text.te_binbuf);
+ glist_retext(x->m_glist, &x->m_text);
+}
+
+static void message_click(t_message *x,
+ t_floatarg xpos, t_floatarg ypos, t_floatarg shift,
+ t_floatarg ctrl, t_floatarg alt)
+{
+ message_float(x, 0);
+ if (glist_isvisible(x->m_glist))
+ {
+ t_rtext *y = glist_findrtext(x->m_glist, &x->m_text);
+ sys_vgui(".x%x.c itemconfigure %sR -width 5\n",
+ glist_getcanvas(x->m_glist), rtext_gettag(y));
+ clock_delay(x->m_clock, 120);
+ }
+}
+
+static void message_tick(t_message *x)
+{
+ if (glist_isvisible(x->m_glist))
+ {
+ t_rtext *y = glist_findrtext(x->m_glist, &x->m_text);
+ sys_vgui(".x%x.c itemconfigure %sR -width 1\n",
+ glist_getcanvas(x->m_glist), rtext_gettag(y));
+ }
+}
+
+static void message_free(t_message *x)
+{
+ clock_free(x->m_clock);
+}
+
+void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ t_message *x = (t_message *)pd_new(message_class);
+ x->m_messresponder.mr_pd = messresponder_class;
+ x->m_messresponder.mr_outlet = outlet_new(&x->m_text, &s_float);
+ x->m_text.te_width = 0; /* don't know it yet. */
+ x->m_text.te_type = T_MESSAGE;
+ x->m_text.te_binbuf = binbuf_new();
+ x->m_glist = gl;
+ x->m_clock = clock_new(x, (t_method)message_tick);
+ if (argc > 1)
+ {
+ x->m_text.te_xpix = atom_getfloatarg(0, argc, argv);
+ x->m_text.te_ypix = atom_getfloatarg(1, argc, argv);
+ if (argc > 2) binbuf_restore(x->m_text.te_binbuf, argc-2, argv+2);
+ glist_add(gl, &x->m_text.te_g);
+ }
+ else
+ {
+ int xpix, ypix;
+ pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1);
+ glist_noselect(gl);
+ glist_getnextxy(gl, &xpix, &ypix);
+ x->m_text.te_xpix = xpix-3;
+ x->m_text.te_ypix = ypix-3;
+ glist_add(gl, &x->m_text.te_g);
+ glist_noselect(gl);
+ glist_select(gl, &x->m_text.te_g);
+ canvas_startmotion(glist_getcanvas(gl));
+ }
+}
+
+/* ---------------------- the "atom" text item ------------------------ */
+
+#define ATOMBUFSIZE 40
+
+typedef struct _gatom
+{
+ t_text a_text;
+ t_atom a_atom; /* this holds the value and the type */
+ t_glist *a_glist; /* owning glist */
+ t_float a_toggle; /* value to toggle to */
+ t_float a_draghi; /* high end of drag range */
+ t_float a_draglo; /* low end of drag range */
+ char a_buf[ATOMBUFSIZE];
+ char a_shift;
+} t_gatom;
+
+static void gatom_set(t_gatom *x, t_symbol *s, int argc, t_atom *argv)
+{
+ if (!argc) return;
+ if (x->a_atom.a_type == A_FLOAT)
+ x->a_atom.a_w.w_float = atom_getfloat(argv);
+ else if (x->a_atom.a_type == A_SYMBOL)
+ x->a_atom.a_w.w_symbol = atom_getsymbol(argv);
+ binbuf_clear(x->a_text.te_binbuf);
+ binbuf_add(x->a_text.te_binbuf, 1, &x->a_atom);
+ glist_retext(x->a_glist, &x->a_text);
+ x->a_buf[0] = 0;
+}
+
+static void gatom_bang(t_gatom *x)
+{
+ if (x->a_atom.a_type == A_FLOAT)
+ outlet_float(x->a_text.te_outlet, x->a_atom.a_w.w_float);
+ else if (x->a_atom.a_type == A_SYMBOL)
+ outlet_symbol(x->a_text.te_outlet, x->a_atom.a_w.w_symbol);
+}
+
+static void gatom_float(t_gatom *x, t_float f)
+{
+ t_atom at;
+ SETFLOAT(&at, f);
+ gatom_set(x, 0, 1, &at);
+ gatom_bang(x);
+}
+
+static void gatom_clipfloat(t_gatom *x, t_float f)
+{
+ if (x->a_draglo != 0 || x->a_draghi != 0)
+ {
+ if (f < x->a_draglo)
+ f = x->a_draglo;
+ if (f > x->a_draghi)
+ f = x->a_draghi;
+ }
+ gatom_float(x, f);
+}
+
+static void gatom_symbol(t_gatom *x, t_symbol *s)
+{
+ t_atom at;
+ SETSYMBOL(&at, s);
+ gatom_set(x, 0, 1, &at);
+ gatom_bang(x);
+}
+
+static void gatom_motion(void *z, t_floatarg dx, t_floatarg dy)
+{
+ t_gatom *x = (t_gatom *)z;
+ if (dy == 0) return;
+ if (x->a_atom.a_type == A_FLOAT)
+ {
+ if (x->a_shift)
+ {
+ double nval = x->a_atom.a_w.w_float - 0.01 * dy;
+ double trunc = 0.01 * (floor(100. * nval + 0.5));
+ if (trunc < nval + 0.0001 && trunc > nval - 0.0001) nval = trunc;
+ gatom_clipfloat(x, nval);
+ }
+ else
+ {
+ double nval = x->a_atom.a_w.w_float - dy;
+ double trunc = 0.01 * (floor(100. * nval + 0.5));
+ if (trunc < nval + 0.0001 && trunc > nval - 0.0001) nval = trunc;
+ trunc = floor(nval + 0.5);
+ if (trunc < nval + 0.001 && trunc > nval - 0.001) nval = trunc;
+ gatom_clipfloat(x, nval);
+ }
+ }
+}
+
+static void gatom_key(void *z, t_floatarg f)
+{
+ t_gatom *x = (t_gatom *)z;
+ int c = f;
+ int l = strlen(x->a_buf);
+ t_atom at;
+ char sbuf[ATOMBUFSIZE + 4];
+ if (c == ' ') return;
+ else if (c == '\b')
+ {
+ if (l > 0)
+ {
+ x->a_buf[l-1] = 0;
+ goto redraw;
+ }
+ }
+ else if (c == '\n')
+ {
+ if (x->a_atom.a_type == A_FLOAT)
+ gatom_float(x, atof(x->a_buf));
+ else if (x->a_atom.a_type == A_SYMBOL)
+ gatom_symbol(x, gensym(x->a_buf));
+ else bug("gatom_key");
+ }
+ else if (l < (ATOMBUFSIZE-1))
+ {
+ x->a_buf[l] = c;
+ x->a_buf[l+1] = 0;
+ goto redraw;
+ }
+ return;
+redraw:
+ /* LATER figure out how to avoid creating all these symbols! */
+ sprintf(sbuf, "%s...", x->a_buf);
+ SETSYMBOL(&at, gensym(sbuf));
+ binbuf_clear(x->a_text.te_binbuf);
+ binbuf_add(x->a_text.te_binbuf, 1, &at);
+ glist_retext(x->a_glist, &x->a_text);
+}
+
+static void gatom_click(t_gatom *x,
+ t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl,
+ t_floatarg alt)
+{
+ if (x->a_text.te_width == 1)
+ {
+ if (x->a_atom.a_type == A_FLOAT)
+ gatom_float(x, (x->a_atom.a_w.w_float == 0));
+ }
+ else
+ {
+ if (alt)
+ {
+ if (x->a_atom.a_type != A_FLOAT) return;
+ if (x->a_atom.a_w.w_float != 0)
+ {
+ x->a_toggle = x->a_atom.a_w.w_float;
+ gatom_float(x, 0);
+ return;
+ }
+ else gatom_float(x, x->a_toggle);
+ }
+ x->a_shift = shift;
+ x->a_buf[0] = 0;
+ glist_grab(x->a_glist, &x->a_text.te_g, gatom_motion, gatom_key,
+ xpos, ypos);
+ }
+}
+
+static void gatom_param(t_gatom *x, t_floatarg width, t_floatarg draglo,
+ t_floatarg draghi)
+{
+ if (draglo >= draghi)
+ draglo = draghi = 0;
+ x->a_draglo = draglo;
+ x->a_draghi = draghi;
+ if (width < 0)
+ width = 4;
+ else if (width > 80)
+ width = 80;
+ x->a_text.te_width = width;
+ glist_retext(x->a_glist, &x->a_text);
+}
+
+void canvas_atom(t_glist *gl, t_atomtype type,
+ t_symbol *s, int argc, t_atom *argv)
+{
+ t_gatom *x = (t_gatom *)pd_new(gatom_class);
+ t_atom at;
+ x->a_text.te_width = 0; /* don't know it yet. */
+ x->a_text.te_type = T_ATOM;
+ x->a_text.te_binbuf = binbuf_new();
+ x->a_glist = gl;
+ x->a_atom.a_type = type;
+ x->a_toggle = 1;
+ x->a_draglo = 0;
+ x->a_draghi = 0;
+ if (type == A_FLOAT)
+ {
+ x->a_atom.a_w.w_float = 0;
+ x->a_text.te_width = 5;
+ outlet_new(&x->a_text, &s_float);
+ SETFLOAT(&at, 0);
+ }
+ else
+ {
+ x->a_atom.a_w.w_symbol = &s_symbol;
+ x->a_text.te_width = 10;
+ outlet_new(&x->a_text, &s_symbol);
+ SETSYMBOL(&at, &s_symbol);
+ }
+ binbuf_add(x->a_text.te_binbuf, 1, &at);
+ if (argc > 1)
+ {
+ x->a_text.te_xpix = atom_getfloatarg(0, argc, argv);
+ x->a_text.te_ypix = atom_getfloatarg(1, argc, argv);
+ x->a_text.te_width = atom_getintarg(2, argc, argv);
+ /* sanity check because some very old patches have trash in this
+ field... remove this in 2003 or so: */
+ if (x->a_text.te_width < 0 || x->a_text.te_width > 500)
+ x->a_text.te_width = 4;
+ x->a_draglo = atom_getfloatarg(3, argc, argv);
+ x->a_draghi = atom_getfloatarg(4, argc, argv);
+ glist_add(gl, &x->a_text.te_g);
+ }
+ else
+ {
+ int xpix, ypix;
+ pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1);
+ glist_noselect(gl);
+ glist_getnextxy(gl, &xpix, &ypix);
+ x->a_text.te_xpix = xpix;
+ x->a_text.te_ypix = ypix;
+ glist_add(gl, &x->a_text.te_g);
+ glist_noselect(gl);
+ glist_select(gl, &x->a_text.te_g);
+ canvas_startmotion(glist_getcanvas(gl));
+ }
+}
+
+void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ canvas_atom(gl, A_FLOAT, s, argc, argv);
+}
+
+void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
+{
+ canvas_atom(gl, A_SYMBOL, s, argc, argv);
+}
+
+static void gatom_free(t_gatom *x)
+{
+ gfxstub_deleteforkey(x);
+}
+
+static void gatom_properties(t_gobj *z, t_glist *owner)
+{
+ t_gatom *x = (t_gatom *)z;
+ char buf[200];
+ sprintf(buf, "pdtk_gatom_dialog %%s %d %g %g\n",
+ x->a_text.te_width, x->a_draglo, x->a_draghi);
+ gfxstub_new(&x->a_text.te_pd, x, buf);
+}
+
+
+/* -------------------- widget behavior for text objects ------------ */
+
+static void text_getrect(t_gobj *z, t_glist *glist,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_text *x = (t_text *)z;
+ int width, height, iscomment = (x->te_type == T_TEXT);
+ float x1, y1, x2, y2;
+
+ /* for number boxes, we know width and height a priori, and should
+ report them here so that graphs can get swelled to fit. */
+
+ if (x->te_type == T_ATOM && x->te_width > 0)
+ {
+ int font = glist_getfont(glist);
+ int fontwidth = sys_fontwidth(font), fontheight = sys_fontheight(font);
+ width = (x->te_width > 0 ? x->te_width : 6) * fontwidth + 2;
+ height = fontheight + 1; /* borrowed from TMARGIN, etc, in g_rtext.c */
+ }
+ /* if we're invisible we don't know our size so we just lie about
+ it. This is called on invisible boxes to establish order of inlets
+ and possibly other reasons.
+ To find out if the box is visible we can't just check the "vis"
+ flag because we might be within the vis() routine and not have set
+ that yet. So we check directly whether the "rtext" list has been
+ built. LATER reconsider when "vis" flag should be on and off? */
+
+ else if (glist->gl_editor && glist->gl_editor->e_rtext)
+ {
+ t_rtext *y = glist_findrtext(glist, x);
+ width = rtext_width(y);
+ height = rtext_height(y) - (iscomment << 1);
+ }
+ else width = height = 10;
+ x1 = text_xpix(x, glist);
+ y1 = text_ypix(x, glist);
+ x2 = x1 + width;
+ y2 = y1 + height;
+ y1 += iscomment;
+ *xp1 = x1;
+ *yp1 = y1;
+ *xp2 = x2;
+ *yp2 = y2;
+}
+
+static void text_displace(t_gobj *z, t_glist *glist,
+ int dx, int dy)
+{
+ t_text *x = (t_text *)z;
+ x->te_xpix += dx;
+ x->te_ypix += dy;
+ if (glist_isvisible(glist))
+ {
+ t_rtext *y = glist_findrtext(glist, x);
+ rtext_displace(y, dx, dy);
+ text_drawborder(x, glist, rtext_gettag(y),
+ rtext_width(y), rtext_height(y), 0);
+ canvas_fixlinesfor(glist_getcanvas(glist), x);
+ }
+}
+
+static void text_select(t_gobj *z, t_glist *glist, int state)
+{
+ t_text *x = (t_text *)z;
+ t_rtext *y = glist_findrtext(glist, x);
+ rtext_select(y, state);
+ sys_vgui(".x%x.c itemconfigure %sR -fill %s\n", glist,
+ rtext_gettag(y), (state? "blue" : "black"));
+}
+
+static void text_activate(t_gobj *z, t_glist *glist, int state)
+{
+ t_text *x = (t_text *)z;
+ t_rtext *y = glist_findrtext(glist, x);
+ if (z->g_pd != gatom_class) rtext_activate(y, state);
+}
+
+static void text_delete(t_gobj *z, t_glist *glist)
+{
+ t_text *x = (t_text *)z;
+ canvas_deletelinesfor(glist, x);
+}
+
+ /* return true if the text box should be drawn.
+ We don't show object boxes inside graphs. */
+int text_shouldvis(t_text *x, t_glist *glist)
+{
+ return (glist->gl_havewindow ||
+ (x->te_pd != canvas_class && x->te_pd->c_wb != &text_widgetbehavior) ||
+ (x->te_pd == canvas_class && (((t_glist *)x)->gl_isgraph)));
+}
+
+static void text_vis(t_gobj *z, t_glist *glist, int vis)
+{
+ t_text *x = (t_text *)z;
+ if (vis)
+ {
+ if (text_shouldvis(x, glist))
+ {
+ t_rtext *y = rtext_new(glist, x, glist->gl_editor->e_rtext, 1);
+ if (x->te_type == T_ATOM)
+ glist_retext(glist, x);
+ text_drawborder(x, glist, rtext_gettag(y),
+ rtext_width(y), rtext_height(y), 1);
+ }
+ else rtext_new(glist, x, glist->gl_editor->e_rtext, 0);
+ }
+ else
+ {
+ t_rtext *y = glist_findrtext(glist, x);
+ if (text_shouldvis(x, glist))
+ text_eraseborder(x, glist, rtext_gettag(y));
+ rtext_free(y);
+ }
+}
+
+static int text_click(t_gobj *z, struct _glist *glist,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ t_text *x = (t_text *)z;
+ if (x->te_type == T_OBJECT)
+ {
+ t_symbol *clicksym = gensym("click");
+ if (zgetfn(&x->te_pd, clicksym))
+ {
+ if (doit)
+ pd_vmess(&x->te_pd, clicksym, "fffff",
+ (double)xpix, (double)ypix,
+ (double)shift, 0, (double)alt);
+ return (1);
+ }
+ else return (0);
+ }
+ else if (x->te_type == T_ATOM)
+ {
+ if (doit)
+ gatom_click((t_gatom *)x, (t_floatarg)xpix, (t_floatarg)ypix,
+ (t_floatarg)shift, 0, (t_floatarg)alt);
+ return (1);
+ }
+ else if (x->te_type == T_MESSAGE)
+ {
+ if (doit)
+ message_click((t_message *)x, (t_floatarg)xpix, (t_floatarg)ypix,
+ (t_floatarg)shift, 0, (t_floatarg)alt);
+ return (1);
+ }
+ else return (0);
+}
+
+static void text_save(t_gobj *z, t_binbuf *b)
+{
+ t_text *x = (t_text *)z;
+ if (x->te_type == T_OBJECT)
+ {
+ /* if we have a "saveto" method, and if we don't happen to be
+ a canvas that's an abstraction, the saveto method does the work */
+ if (zgetfn(&x->te_pd, gensym("saveto")) &&
+ !((pd_class(&x->te_pd) == canvas_class) &&
+ (canvas_isabstraction((t_canvas *)x)
+ || canvas_istable((t_canvas *)x))))
+ {
+ mess1(&x->te_pd, gensym("saveto"), b);
+ binbuf_addv(b, "ssii", gensym("#X"), gensym("restore"),
+ (t_int)x->te_xpix, (t_int)x->te_ypix);
+ }
+ else /* otherwise just save the text */
+ {
+ binbuf_addv(b, "ssii", gensym("#X"), gensym("obj"),
+ (t_int)x->te_xpix, (t_int)x->te_ypix);
+ }
+ binbuf_addbinbuf(b, x->te_binbuf);
+ binbuf_addv(b, ";");
+ }
+ else if (x->te_type == T_MESSAGE)
+ {
+ binbuf_addv(b, "ssii", gensym("#X"), gensym("msg"),
+ (t_int)x->te_xpix, (t_int)x->te_ypix);
+ binbuf_addbinbuf(b, x->te_binbuf);
+ binbuf_addv(b, ";");
+ }
+ else if (x->te_type == T_ATOM)
+ {
+ t_atomtype t = ((t_gatom *)x)->a_atom.a_type;
+ t_symbol *sel = (t == A_SYMBOL ? gensym("symbolatom") :
+ (t == A_FLOAT ? gensym("floatatom") : gensym("intatom")));
+ binbuf_addv(b, "ssiiiff", gensym("#X"), sel,
+ (t_int)x->te_xpix, (t_int)x->te_ypix, (t_int)x->te_width,
+ (double)((t_gatom *)x)->a_draglo, (double)((t_gatom *)x)->a_draghi);
+ binbuf_addv(b, ";");
+ }
+ else
+ {
+ binbuf_addv(b, "ssii", gensym("#X"), gensym("text"),
+ (t_int)x->te_xpix, (t_int)x->te_ypix);
+ binbuf_addbinbuf(b, x->te_binbuf);
+ binbuf_addv(b, ";");
+ }
+}
+
+ /* this one is for everyone but "gatoms"; it's imposed in m_class.c */
+t_widgetbehavior text_widgetbehavior =
+{
+ text_getrect,
+ text_displace,
+ text_select,
+ text_activate,
+ text_delete,
+ text_vis,
+ text_click,
+ text_save,
+ 0,
+};
+
+static t_widgetbehavior gatom_widgetbehavior =
+{
+ text_getrect,
+ text_displace,
+ text_select,
+ text_activate,
+ text_delete,
+ text_vis,
+ text_click,
+ text_save,
+ gatom_properties,
+};
+
+/* -------------------- the "text" class ------------ */
+
+#ifdef MACOSX
+#define EXTRAPIX 2
+#else
+#define EXTRAPIX 1
+#endif
+
+ /* draw inlets and outlets for a text object or for a graph. */
+void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime,
+ char *tag, int x1, int y1, int x2, int y2)
+{
+ int n = obj_noutlets(ob), nplus = (n == 1 ? 1 : n-1), i;
+ int width = x2 - x1;
+ for (i = 0; i < n; i++)
+ {
+ int onset = x1 + (width - IOWIDTH) * i / nplus;
+ if (firsttime)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %so%d\n",
+ glist_getcanvas(glist),
+ onset, y2 - 1,
+ onset + IOWIDTH, y2,
+ tag, i);
+ else
+ sys_vgui(".x%x.c coords %so%d %d %d %d %d\n",
+ glist_getcanvas(glist), tag, i,
+ onset, y2 - 1,
+ onset + IOWIDTH, y2);
+ }
+ n = obj_ninlets(ob);
+ nplus = (n == 1 ? 1 : n-1);
+ for (i = 0; i < n; i++)
+ {
+ int onset = x1 + (width - IOWIDTH) * i / nplus;
+ if (firsttime)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %si%d\n",
+ glist_getcanvas(glist),
+ onset, y1,
+ onset + IOWIDTH, y1 + EXTRAPIX,
+ tag, i);
+ else
+ sys_vgui(".x%x.c coords %si%d %d %d %d %d\n",
+ glist_getcanvas(glist), tag, i,
+ onset, y1,
+ onset + IOWIDTH, y1 + EXTRAPIX);
+ }
+}
+
+void text_drawborder(t_text *x, t_glist *glist,
+ char *tag, int width2, int height2, int firsttime)
+{
+ t_object *ob;
+ int x1, y1, x2, y2, width, height;
+ text_getrect(&x->te_g, glist, &x1, &y1, &x2, &y2);
+ width = x2 - x1;
+ height = y2 - y1;
+ if (x->te_type == T_OBJECT)
+ {
+ if (firsttime)
+ sys_vgui(".x%x.c create line\
+ %d %d %d %d %d %d %d %d %d %d -tags %sR\n",
+ glist_getcanvas(glist),
+ x1, y1, x2, y1, x2, y2, x1, y2, x1, y1, tag);
+ else
+ sys_vgui(".x%x.c coords %sR\
+ %d %d %d %d %d %d %d %d %d %d\n",
+ glist_getcanvas(glist), tag,
+ x1, y1, x2, y1, x2, y2, x1, y2, x1, y1);
+ }
+ else if (x->te_type == T_MESSAGE)
+ {
+ if (firsttime)
+ sys_vgui(".x%x.c create line\
+ %d %d %d %d %d %d %d %d %d %d %d %d %d %d -tags %sR\n",
+ glist_getcanvas(glist),
+ x1, y1, x2+4, y1, x2, y1+4, x2, y2-4, x2+4, y2,
+ x1, y2, x1, y1,
+ tag);
+ else
+ sys_vgui(".x%x.c coords %sR\
+ %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ glist_getcanvas(glist), tag,
+ x1, y1, x2+4, y1, x2, y1+4, x2, y2-4, x2+4, y2,
+ x1, y2, x1, y1);
+ }
+ else if (x->te_type == T_ATOM)
+ {
+ if (firsttime)
+ sys_vgui(".x%x.c create line\
+ %d %d %d %d %d %d %d %d %d %d %d %d -tags %sR\n",
+ glist_getcanvas(glist),
+ x1, y1, x2-4, y1, x2, y1+4, x2, y2, x1, y2, x1, y1,
+ tag);
+ else
+ sys_vgui(".x%x.c coords %sR\
+ %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ glist_getcanvas(glist), tag,
+ x1, y1, x2, y1, x2+4, y1+4, x2+4, y2, x1, y2, x1, y1);
+ }
+ /* draw inlets/outlets */
+
+ if (ob = pd_checkobject(&x->te_pd))
+ glist_drawiofor(glist, ob, firsttime, tag, x1, y1, x2, y2);
+}
+
+void glist_eraseiofor(t_glist *glist, t_object *ob, char *tag)
+{
+ int i, n;
+ n = obj_noutlets(ob);
+ for (i = 0; i < n; i++)
+ sys_vgui(".x%x.c delete %so%d\n",
+ glist_getcanvas(glist), tag, i);
+ n = obj_ninlets(ob);
+ for (i = 0; i < n; i++)
+ sys_vgui(".x%x.c delete %si%d\n",
+ glist_getcanvas(glist), tag, i);
+}
+
+void text_eraseborder(t_text *x, t_glist *glist, char *tag)
+{
+ if (x->te_type == T_TEXT) return;
+ sys_vgui(".x%x.c delete %sR\n",
+ glist_getcanvas(glist), tag);
+ glist_eraseiofor(glist, x, tag);
+}
+
+ /* change text; if T_OBJECT, remake it. LATER we'll have an undo buffer
+ which should be filled in here before making the change. */
+
+void text_setto(t_text *x, t_glist *glist, char *buf, int bufsize)
+{
+ if (x->te_type == T_OBJECT)
+ {
+ t_binbuf *b = binbuf_new();
+ int natom1, natom2;
+ t_atom *vec1, *vec2;
+ binbuf_text(b, buf, bufsize);
+ natom1 = binbuf_getnatom(x->te_binbuf);
+ vec1 = binbuf_getvec(x->te_binbuf);
+ natom2 = binbuf_getnatom(b);
+ vec2 = binbuf_getvec(b);
+ /* special case: if pd args change just pass the message on. */
+ if (natom1 >= 1 && natom2 >= 1 && vec1[0].a_type == A_SYMBOL
+ && !strcmp(vec1[0].a_w.w_symbol->s_name, "pd") &&
+ vec2[0].a_type == A_SYMBOL
+ && !strcmp(vec2[0].a_w.w_symbol->s_name, "pd"))
+ {
+ typedmess(&x->te_pd, gensym("rename"), natom2-1, vec2+1);
+ binbuf_free(x->te_binbuf);
+ x->te_binbuf = b;
+ }
+ else /* normally, just destroy the old one and make a new one. */
+ {
+ int xwas = x->te_xpix, ywas = x->te_ypix;
+ glist_delete(glist, &x->te_g);
+ canvas_objtext(glist, xwas, ywas, 0, b);
+ /* if it's an abstraction loadbang it here */
+ if (newest && pd_class(newest) == canvas_class)
+ canvas_loadbang((t_canvas *)newest);
+ canvas_restoreconnections(glist_getcanvas(glist));
+ }
+ /* if we made a new "pd" or changed a window name,
+ update window list */
+ if (natom2 >= 1 && vec2[0].a_type == A_SYMBOL
+ && !strcmp(vec2[0].a_w.w_symbol->s_name, "pd"))
+ canvas_updatewindowlist();
+ }
+ else binbuf_text(x->te_binbuf, buf, bufsize);
+}
+
+void g_text_setup(void)
+{
+ text_class = class_new(gensym("text"), 0, 0, sizeof(t_text),
+ CLASS_NOINLET | CLASS_PATCHABLE, 0);
+
+ message_class = class_new(gensym("message"), 0, (t_method)message_free,
+ sizeof(t_message), CLASS_PATCHABLE, 0);
+ class_addbang(message_class, message_bang);
+ class_addfloat(message_class, message_float);
+ class_addsymbol(message_class, message_symbol);
+ class_addlist(message_class, message_list);
+ class_addanything(message_class, message_list);
+
+ class_addmethod(message_class, (t_method)message_click, gensym("click"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(message_class, (t_method)message_set, gensym("set"),
+ A_GIMME, 0);
+ class_addmethod(message_class, (t_method)message_add, gensym("add"),
+ A_GIMME, 0);
+ class_addmethod(message_class, (t_method)message_add2, gensym("add2"),
+ A_GIMME, 0);
+
+ messresponder_class = class_new(gensym("messresponder"), 0, 0,
+ sizeof(t_text), CLASS_PD, 0);
+ class_addbang(messresponder_class, messresponder_bang);
+ class_addfloat(messresponder_class, (t_method) messresponder_float);
+ class_addsymbol(messresponder_class, messresponder_symbol);
+ class_addlist(messresponder_class, messresponder_list);
+ class_addanything(messresponder_class, messresponder_anything);
+
+ gatom_class = class_new(gensym("gatom"), 0, (t_method)gatom_free,
+ sizeof(t_gatom), CLASS_PATCHABLE, 0);
+ class_addbang(gatom_class, gatom_bang);
+ class_addfloat(gatom_class, gatom_float);
+ class_addsymbol(gatom_class, gatom_symbol);
+ class_addmethod(gatom_class, (t_method)gatom_set, gensym("set"),
+ A_GIMME, 0);
+ class_addmethod(gatom_class, (t_method)gatom_click, gensym("click"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(gatom_class, (t_method)gatom_param, gensym("param"),
+ A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_setwidget(gatom_class, &gatom_widgetbehavior);
+}
+
+
diff --git a/pd/src/g_toggle.c b/pd/src/g_toggle.c
new file mode 100644
index 00000000..6eba64d2
--- /dev/null
+++ b/pd/src/g_toggle.c
@@ -0,0 +1,528 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */
+/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include "t_tk.h"
+#include "g_all_guis.h"
+#include <math.h>
+
+#ifdef NT
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+/* --------------- tgl gui-toggle ------------------------- */
+
+t_widgetbehavior toggle_widgetbehavior;
+static t_class *toggle_class;
+
+/* widget helper functions */
+
+void toggle_draw_update(t_toggle *x, t_glist *glist)
+{
+ if(glist_isvisible(glist))
+ {
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c itemconfigure %xX1 -fill #%6.6x\n", canvas, x,
+ (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol);
+ sys_vgui(".x%x.c itemconfigure %xX2 -fill #%6.6x\n", canvas, x,
+ (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol);
+ }
+}
+
+void toggle_draw_new(t_toggle *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int w=1, xx=text_xpix(&x->x_gui.x_obj, glist), yy=text_ypix(&x->x_gui.x_obj, glist);
+
+ if(x->x_gui.x_w >= 30)
+ w = 2;
+ if(x->x_gui.x_w >= 60)
+ w = 3;
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n",
+ canvas, xx, yy, xx + x->x_gui.x_w, yy + x->x_gui.x_h,
+ x->x_gui.x_bcol, x);
+ sys_vgui(".x%x.c create line %d %d %d %d -width %d -fill #%6.6x -tags %xX1\n",
+ canvas, xx+w+1, yy+w+1, xx + x->x_gui.x_w-w, yy + x->x_gui.x_h-w, w,
+ (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol, x);
+ sys_vgui(".x%x.c create line %d %d %d %d -width %d -fill #%6.6x -tags %xX2\n",
+ canvas, xx+w+1, yy + x->x_gui.x_h-w-1, xx + x->x_gui.x_w-w, yy+w, w,
+ (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol, x);
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n",
+ canvas, xx+x->x_gui.x_ldx,
+ yy+x->x_gui.x_ldy,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"",
+ x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas, xx, yy + x->x_gui.x_h-1, xx + IOWIDTH, yy + x->x_gui.x_h, x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas, xx, yy, xx + IOWIDTH, yy+1, x, 0);
+}
+
+void toggle_draw_move(t_toggle *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int w=1, xx=text_xpix(&x->x_gui.x_obj, glist), yy=text_ypix(&x->x_gui.x_obj, glist);
+
+ if(x->x_gui.x_w >= 30)
+ w = 2;
+
+ if(x->x_gui.x_w >= 60)
+ w = 3;
+ sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n",
+ canvas, x, xx, yy, xx + x->x_gui.x_w, yy + x->x_gui.x_h);
+ sys_vgui(".x%x.c itemconfigure %xX1 -width %d\n", canvas, x, w);
+ sys_vgui(".x%x.c coords %xX1 %d %d %d %d\n",
+ canvas, x, xx+w+1, yy+w+1, xx + x->x_gui.x_w-w, yy + x->x_gui.x_h-w);
+ sys_vgui(".x%x.c itemconfigure %xX2 -width %d\n", canvas, x, w);
+ sys_vgui(".x%x.c coords %xX2 %d %d %d %d\n",
+ canvas, x, xx+w+1, yy + x->x_gui.x_h-w-1, xx + x->x_gui.x_w-w, yy+w);
+ sys_vgui(".x%x.c coords %xLABEL %d %d\n",
+ canvas, x, xx+x->x_gui.x_ldx, yy+x->x_gui.x_ldy);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n",
+ canvas, x, 0, xx, yy + x->x_gui.x_h-1, xx + IOWIDTH, yy + x->x_gui.x_h);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n",
+ canvas, x, 0, xx, yy, xx + IOWIDTH, yy+1);
+}
+
+void toggle_draw_erase(t_toggle* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c delete %xBASE\n", canvas, x);
+ sys_vgui(".x%x.c delete %xX1\n", canvas, x);
+ sys_vgui(".x%x.c delete %xX2\n", canvas, x);
+ sys_vgui(".x%x.c delete %xLABEL\n", canvas, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+void toggle_draw_config(t_toggle* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n",
+ canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"");
+ sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, x,
+ x->x_gui.x_bcol);
+ sys_vgui(".x%x.c itemconfigure %xX1 -fill #%6.6x\n", canvas, x,
+ x->x_on?x->x_gui.x_fcol:x->x_gui.x_bcol);
+ sys_vgui(".x%x.c itemconfigure %xX2 -fill #%6.6x\n", canvas, x,
+ x->x_on?x->x_gui.x_fcol:x->x_gui.x_bcol);
+}
+
+void toggle_draw_io(t_toggle* x, t_glist* glist, int old_snd_rcv_flags)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas, xpos,
+ ypos + x->x_gui.x_h-1, xpos + IOWIDTH,
+ ypos + x->x_gui.x_h, x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas, xpos, ypos,
+ xpos + IOWIDTH, ypos+1, x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+void toggle_draw_select(t_toggle* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if(x->x_gui.x_fsf.x_selected)
+ {
+ pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ }
+ else
+ {
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL);
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol);
+ }
+}
+
+void toggle_draw(t_toggle *x, t_glist *glist, int mode)
+{
+ if(mode == IEM_GUI_DRAW_MODE_UPDATE)
+ toggle_draw_update(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_MOVE)
+ toggle_draw_move(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_NEW)
+ toggle_draw_new(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_SELECT)
+ toggle_draw_select(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_ERASE)
+ toggle_draw_erase(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_CONFIG)
+ toggle_draw_config(x, glist);
+ else if(mode >= IEM_GUI_DRAW_MODE_IO)
+ toggle_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO);
+}
+
+/* ------------------------ tgl widgetbehaviour----------------------------- */
+
+static void toggle_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_toggle *x = (t_toggle *)z;
+
+ *xp1 = text_xpix(&x->x_gui.x_obj, glist);
+ *yp1 = text_ypix(&x->x_gui.x_obj, glist);
+ *xp2 = *xp1 + x->x_gui.x_w;
+ *yp2 = *yp1 + x->x_gui.x_h;
+}
+
+static void toggle_save(t_gobj *z, t_binbuf *b)
+{
+ t_toggle *x = (t_toggle *)z;
+ int bflcol[3], *ip1, *ip2;
+ t_symbol *srl[3];
+
+ iemgui_save(&x->x_gui, srl, bflcol);
+ ip1 = (int *)(&x->x_gui.x_isa);
+ ip2 = (int *)(&x->x_gui.x_fsf);
+ binbuf_addv(b, "ssiisiisssiiiiiiiff", gensym("#X"),gensym("obj"),
+ (t_int)x->x_gui.x_obj.te_xpix,
+ (t_int)x->x_gui.x_obj.te_ypix,
+ gensym("tgl"), x->x_gui.x_w,
+ (*ip1)&IEM_INIT_ARGS_ALL,
+ srl[0], srl[1], srl[2],
+ x->x_gui.x_ldx, x->x_gui.x_ldy,
+ (*ip2)&IEM_FSTYLE_FLAGS_ALL, x->x_gui.x_fontsize,
+ bflcol[0], bflcol[1], bflcol[2], x->x_on, x->x_nonzero);
+ binbuf_addv(b, ";");
+}
+
+static void toggle_properties(t_gobj *z, t_glist *owner)
+{
+ t_toggle *x = (t_toggle *)z;
+ char buf[800];
+ t_symbol *srl[3];
+
+ iemgui_properties(&x->x_gui, srl);
+ sprintf(buf, "pdtk_iemgui_dialog %%s TOGGLE \
+ ----------dimensions(pix):----------- %d %d size: 0 0 empty \
+ -----------non-zero-value:----------- %g value: 0.0 empty %g \
+ -1 lin log %d %d empty %d \
+ %s %s \
+ %s %d %d \
+ %d %d \
+ %d %d %d\n",
+ x->x_gui.x_w, IEM_GUI_MINSIZE,
+ x->x_nonzero, 1.0,/*non_zero-schedule*/
+ x->x_gui.x_isa.x_loadinit, -1, -1,/*no multi*/
+ srl[0]->s_name, srl[1]->s_name,
+ srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy,
+ x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize,
+ 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol);
+ gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf);
+}
+
+static void toggle_bang(t_toggle *x)
+{
+ x->x_on = (x->x_on==0.0)?x->x_nonzero:0.0;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_float(x->x_gui.x_snd->s_thing, x->x_on);
+}
+
+static void toggle_dialog(t_toggle *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *srl[3];
+ int a = (int)atom_getintarg(0, argc, argv);
+ float nonzero = (float)atom_getfloatarg(2, argc, argv);
+ int sr_flags;
+
+ if(nonzero == 0.0)
+ nonzero = 1.0;
+ x->x_nonzero = nonzero;
+ if(x->x_on != 0.0)
+ x->x_on = x->x_nonzero;
+ sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);
+ x->x_gui.x_w = iemgui_clip_size(a);
+ x->x_gui.x_h = x->x_gui.x_w;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+}
+
+static void toggle_click(t_toggle *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
+{toggle_bang(x);}
+
+static int toggle_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ if(doit)
+ toggle_click((t_toggle *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt);
+ return (1);
+}
+
+static void toggle_set(t_toggle *x, t_floatarg f)
+{
+ x->x_on = f;
+ if(f != 0.0)
+ x->x_nonzero = f;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+}
+
+static void toggle_float(t_toggle *x, t_floatarg f)
+{
+ toggle_set(x, f);
+ if(x->x_gui.x_fsf.x_put_in2out)
+ {
+ outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_float(x->x_gui.x_snd->s_thing, x->x_on);
+ }
+}
+
+static void toggle_fout(t_toggle *x, t_floatarg f)
+{
+ toggle_set(x, f);
+ outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_float(x->x_gui.x_snd->s_thing, x->x_on);
+}
+
+static void toggle_loadbang(t_toggle *x)
+{
+ if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit)
+ toggle_fout(x, (float)x->x_on);
+}
+
+static void toggle_size(t_toggle *x, t_symbol *s, int ac, t_atom *av)
+{
+ x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av));
+ x->x_gui.x_h = x->x_gui.x_w;
+ iemgui_size((void *)x, &x->x_gui);
+}
+
+static void toggle_delta(t_toggle *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}
+
+static void toggle_pos(t_toggle *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void toggle_color(t_toggle *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_color((void *)x, &x->x_gui, s, ac, av);}
+
+static void toggle_send(t_toggle *x, t_symbol *s)
+{iemgui_send(x, &x->x_gui, s);}
+
+static void toggle_receive(t_toggle *x, t_symbol *s)
+{iemgui_receive(x, &x->x_gui, s);}
+
+static void toggle_label(t_toggle *x, t_symbol *s)
+{iemgui_label((void *)x, &x->x_gui, s);}
+
+static void toggle_label_font(t_toggle *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);}
+
+static void toggle_label_pos(t_toggle *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void toggle_init(t_toggle *x, t_floatarg f)
+{
+ x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1;
+}
+
+static void toggle_nonzero(t_toggle *x, t_floatarg f)
+{
+ if(f != 0.0)
+ x->x_nonzero = f;
+}
+
+static void toggle_list(t_toggle *x, t_symbol *s, int ac, t_atom *av)
+{
+ int l=iemgui_list((void *)x, &x->x_gui, s, ac, av);
+
+ if(l < 0)
+ {
+ if(IS_A_FLOAT(av,0))
+ toggle_float(x, atom_getfloatarg(0, ac, av));
+ }
+ else if(l > 0)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+ }
+}
+
+static void *toggle_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_toggle *x = (t_toggle *)pd_new(toggle_class);
+ int bflcol[]={-262144, -1, -1};
+ t_symbol *srl[3];
+ int a=IEM_GUI_DEFAULTSIZE, f=0;
+ int ldx=0, ldy=-6;
+ int fs=8, iinit=0, ifstyle=0;
+ float on=0.0, nonzero=1.0;
+ t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit);
+ t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle);
+ char str[144];
+
+ srl[0] = gensym("empty");
+ srl[1] = gensym("empty");
+ srl[2] = gensym("empty");
+
+ if(((argc == 13)||(argc == 14))&&IS_A_FLOAT(argv,0)
+ &&IS_A_FLOAT(argv,1)
+ &&(IS_A_SYMBOL(argv,2)||IS_A_FLOAT(argv,2))
+ &&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3))
+ &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4))
+ &&IS_A_FLOAT(argv,5)&&IS_A_FLOAT(argv,6)
+ &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8)&&IS_A_FLOAT(argv,9)
+ &&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12))
+ {
+ a = (int)atom_getintarg(0, argc, argv);
+ iinit = (int)atom_getintarg(1, argc, argv);
+ if(IS_A_SYMBOL(argv,2))
+ srl[0] = atom_getsymbolarg(2, argc, argv);
+ else if(IS_A_FLOAT(argv,2))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(2, argc, argv));
+ srl[0] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,3))
+ srl[1] = atom_getsymbolarg(3, argc, argv);
+ else if(IS_A_FLOAT(argv,3))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(3, argc, argv));
+ srl[1] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,4))
+ srl[2] = atom_getsymbolarg(4, argc, argv);
+ else if(IS_A_FLOAT(argv,4))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(4, argc, argv));
+ srl[2] = gensym(str);
+ }
+ ldx = (int)atom_getintarg(5, argc, argv);
+ ldy = (int)atom_getintarg(6, argc, argv);
+ ifstyle = (int)atom_getintarg(7, argc, argv);
+ fs = (int)atom_getintarg(8, argc, argv);
+ bflcol[0] = (int)atom_getintarg(9, argc, argv);
+ bflcol[1] = (int)atom_getintarg(10, argc, argv);
+ bflcol[2] = (int)atom_getintarg(11, argc, argv);
+ on = (float)atom_getfloatarg(12, argc, argv);
+ }
+ if((argc == 14)&&IS_A_FLOAT(argv,13))
+ nonzero = (float)atom_getfloatarg(13, argc, argv);
+ x->x_gui.x_draw = (t_iemfunptr)toggle_draw;
+ iinit &= IEM_INIT_ARGS_ALL;
+ ifstyle &= IEM_FSTYLE_FLAGS_ALL;
+
+ fstyle->x_snd_able = 1;
+ fstyle->x_rcv_able = 1;
+ x->x_gui.x_glist = (t_glist *)canvas_getcurrent();
+ x->x_gui.x_isa = *init;
+ if(!strcmp(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0;
+ if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0;
+ x->x_gui.x_unique_num = 0;
+ if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica");
+ else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times");
+ else { fstyle->x_font_style = 0;
+ strcpy(x->x_gui.x_font, "courier"); }
+ x->x_gui.x_fsf = *fstyle;
+ x->x_nonzero = (nonzero!=0.0)?nonzero:1.0;
+ if(x->x_gui.x_isa.x_loadinit)
+ x->x_on = (on!=0.0)?nonzero:0.0;
+ else
+ x->x_on = 0.0;
+ iemgui_first_dollararg2sym(&x->x_gui, srl);
+ if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]);
+ x->x_gui.x_snd = srl[0];
+ x->x_gui.x_rcv = srl[1];
+ x->x_gui.x_lab = srl[2];
+ x->x_gui.x_ldx = ldx;
+ x->x_gui.x_ldy = ldy;
+
+ if(fs < 4)
+ fs = 4;
+ x->x_gui.x_fontsize = fs;
+ x->x_gui.x_w = iemgui_clip_size(a);
+ x->x_gui.x_h = x->x_gui.x_w;
+ iemgui_all_colfromload(&x->x_gui, bflcol);
+ iemgui_verify_snd_ne_rcv(&x->x_gui);
+ outlet_new(&x->x_gui.x_obj, &s_float);
+ return (x);
+}
+
+static void toggle_ff(t_toggle *x)
+{
+ if(x->x_gui.x_fsf.x_selected)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ if(x->x_gui.x_fsf.x_rcv_able)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);
+ gfxstub_deleteforkey(x);
+}
+
+void g_toggle_setup(void)
+{
+ toggle_class = class_new(gensym("tgl"), (t_newmethod)toggle_new,
+ (t_method)toggle_ff, sizeof(t_toggle), 0, A_GIMME, 0);
+ class_addcreator((t_newmethod)toggle_new, gensym("toggle"), A_GIMME, 0);
+ class_addbang(toggle_class, toggle_bang);
+ class_addfloat(toggle_class, toggle_float);
+ class_addlist(toggle_class, toggle_list);
+ class_addmethod(toggle_class, (t_method)toggle_click, gensym("click"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(toggle_class, (t_method)toggle_dialog, gensym("dialog"),
+ A_GIMME, 0);
+ class_addmethod(toggle_class, (t_method)toggle_loadbang, gensym("loadbang"), 0);
+ class_addmethod(toggle_class, (t_method)toggle_set, gensym("set"), A_FLOAT, 0);
+ class_addmethod(toggle_class, (t_method)toggle_size, gensym("size"), A_GIMME, 0);
+ class_addmethod(toggle_class, (t_method)toggle_delta, gensym("delta"), A_GIMME, 0);
+ class_addmethod(toggle_class, (t_method)toggle_pos, gensym("pos"), A_GIMME, 0);
+ class_addmethod(toggle_class, (t_method)toggle_color, gensym("color"), A_GIMME, 0);
+ class_addmethod(toggle_class, (t_method)toggle_send, gensym("send"), A_DEFSYM, 0);
+ class_addmethod(toggle_class, (t_method)toggle_receive, gensym("receive"), A_DEFSYM, 0);
+ class_addmethod(toggle_class, (t_method)toggle_label, gensym("label"), A_DEFSYM, 0);
+ class_addmethod(toggle_class, (t_method)toggle_label_pos, gensym("label_pos"), A_GIMME, 0);
+ class_addmethod(toggle_class, (t_method)toggle_label_font, gensym("label_font"), A_GIMME, 0);
+ class_addmethod(toggle_class, (t_method)toggle_init, gensym("init"), A_FLOAT, 0);
+ class_addmethod(toggle_class, (t_method)toggle_nonzero, gensym("nonzero"), A_FLOAT, 0);
+ if(!iemgui_key_sym)
+ iemgui_key_sym = gensym("#keyname");
+ toggle_widgetbehavior.w_getrectfn = toggle_getrect;
+ toggle_widgetbehavior.w_displacefn = iemgui_displace;
+ toggle_widgetbehavior.w_selectfn = iemgui_select;
+ toggle_widgetbehavior.w_activatefn = NULL;
+ toggle_widgetbehavior.w_deletefn = iemgui_delete;
+ toggle_widgetbehavior.w_visfn = iemgui_vis;
+ toggle_widgetbehavior.w_clickfn = toggle_newclick;
+ toggle_widgetbehavior.w_propertiesfn = toggle_properties;
+ toggle_widgetbehavior.w_savefn = toggle_save;
+ class_setwidget(toggle_class, &toggle_widgetbehavior);
+ class_sethelpsymbol(toggle_class, gensym("toggle"));
+}
diff --git a/pd/src/g_traversal.c b/pd/src/g_traversal.c
new file mode 100644
index 00000000..a9e8ce03
--- /dev/null
+++ b/pd/src/g_traversal.c
@@ -0,0 +1,1084 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* This file defines Text objects which traverse data contained in scalars
+and arrays:
+
+pointer - point to an object belonging to a template
+get - get numeric fields
+set - change numeric fields
+element - get an array element
+getsize - get the size of an array
+setsize - change the size of an array
+append - add an element to a list
+sublist - get a pointer into a list which is an element of another scalar
+
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h> /* for read/write to files */
+#include "m_pd.h"
+#include "g_canvas.h"
+
+/* ------------- gstubs and gpointers - safe pointing --------------- */
+
+/* create a gstub which is "owned" by a glist (gl) or an array ("a"). */
+
+t_gstub *gstub_new(t_glist *gl, t_array *a)
+{
+ t_gstub *gs = t_getbytes(sizeof(*gs));
+ if (gl)
+ {
+ gs->gs_which = GP_GLIST;
+ gs->gs_un.gs_glist = gl;
+ }
+ else
+ {
+ gs->gs_which = GP_ARRAY;
+ gs->gs_un.gs_array = a;
+ }
+ gs->gs_refcount = 0;
+ return (gs);
+}
+
+/* when a "gpointer" is set to point to this stub (so we can later chase
+down the owner) we increase a reference count. The following routine is called
+whenever a gpointer is unset from pointing here. If the owner is
+gone and the refcount goes to zero, we can free the gstub safely. */
+
+static void gstub_dis(t_gstub *gs)
+{
+ int refcount = --gs->gs_refcount;
+ if ((!refcount) && gs->gs_which == GP_NONE)
+ t_freebytes(gs, sizeof (*gs));
+ else if (refcount < 0) bug("gstub_dis");
+}
+
+/* this routing is called by the owner to inform the gstub that it is
+being deleted. If no gpointers are pointing here, we can free the gstub;
+otherwise we wait for the last gstub_dis() to free it. */
+
+void gstub_cutoff(t_gstub *gs)
+{
+ gs->gs_which = GP_NONE;
+ if (gs->gs_refcount < 0) bug("gstub_cutoff");
+ if (!gs->gs_refcount) t_freebytes(gs, sizeof (*gs));
+}
+
+/* call this to verify that a pointer is fresh, i.e., that it either
+points to real data or to the head of a list, and that in either case
+the object hasn't disappeared since this pointer was generated.
+Unless "headok" is set, the routine also fails for the head of a list. */
+
+int gpointer_check(const t_gpointer *gp, int headok)
+{
+ t_gstub *gs = gp->gp_stub;
+ if (!gs) return (0);
+ if (gs->gs_which == GP_ARRAY)
+ {
+ if (gs->gs_un.gs_array->a_valid != gp->gp_valid) return (0);
+ else return (1);
+ }
+ else if (gs->gs_which == GP_GLIST)
+ {
+ if (!headok && !gp->gp_un.gp_scalar) return (0);
+ else if (gs->gs_un.gs_glist->gl_valid != gp->gp_valid) return (0);
+ else return (1);
+ }
+ else return (0);
+}
+
+/* call this if you know the pointer is fresh but don't know if we're pointing
+to the head of a list or to real data. Any pointer is known to be fresh
+when it appears as the argument of a message, but if your "pointer" method
+or inlet stores it and you use it later, call gpointer_check above. */
+
+/* LATER reconsider the above... I no longer think it's true! */
+
+static int gpointer_ishead(const t_gpointer *gp)
+{
+ return ((gp->gp_stub->gs_which == GP_GLIST) && !gp->gp_un.gp_scalar);
+}
+
+/* get the template for the object pointer to. Assumes we've already checked
+freshness. Returns 0 if head of list. */
+
+static t_symbol *gpointer_gettemplatesym(const t_gpointer *gp)
+{
+ t_gstub *gs = gp->gp_stub;
+ if (gs->gs_which == GP_GLIST)
+ {
+ t_scalar *sc = gp->gp_un.gp_scalar;
+ if (sc)
+ return (sc->sc_template);
+ else return (0);
+ }
+ else
+ {
+ t_array *a = gs->gs_un.gs_array;
+ return (a->a_templatesym);
+ }
+}
+
+ /* copy a pointer to another, assuming the first one is fresh and
+ the second one hasn't yet been initialized. */
+void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto)
+{
+ *gpto = *gpfrom;
+ if (gpto->gp_stub)
+ gpto->gp_stub->gs_refcount++;
+ else bug("gpointer_copy");
+}
+
+void gpointer_unset(t_gpointer *gp)
+{
+ t_gstub *gs;
+ if (gs = gp->gp_stub)
+ {
+ gstub_dis(gs);
+ gp->gp_stub = 0;
+ }
+}
+
+void gpointer_setglist(t_gpointer *gp, t_glist *glist, t_scalar *x)
+{
+ t_gstub *gs;
+ if (gs = gp->gp_stub) gstub_dis(gs);
+ gp->gp_stub = gs = glist->gl_stub;
+ gp->gp_valid = glist->gl_valid;
+ gp->gp_un.gp_scalar = x;
+ gs->gs_refcount++;
+}
+
+static void gpointer_setarray(t_gpointer *gp, t_array *array, t_word *w)
+{
+ t_gstub *gs;
+ if (gs = gp->gp_stub) gstub_dis(gs);
+ gp->gp_stub = gs = array->a_stub;
+ gp->gp_valid = array->a_valid;
+ gp->gp_un.gp_w = w;
+ gs->gs_refcount++;
+}
+
+void gpointer_init(t_gpointer *gp)
+{
+ gp->gp_stub = 0;
+ gp->gp_valid = 0;
+ gp->gp_un.gp_scalar = 0;
+}
+
+/* ---------------------- pointers ----------------------------- */
+
+static t_class *ptrobj_class;
+
+typedef struct
+{
+ t_symbol *to_type;
+ t_outlet *to_outlet;
+} t_typedout;
+
+typedef struct _ptrobj
+{
+ t_object x_obj;
+ t_gpointer x_gp;
+ t_typedout *x_typedout;
+ int x_ntypedout;
+ t_outlet *x_otherout;
+ t_outlet *x_bangout;
+} t_ptrobj;
+
+static void *ptrobj_new(t_symbol *classname, int argc, t_atom *argv)
+{
+ t_ptrobj *x = (t_ptrobj *)pd_new(ptrobj_class);
+ t_typedout *to;
+ int n;
+ gpointer_init(&x->x_gp);
+ x->x_typedout = to = (t_typedout *)getbytes(argc * sizeof (*to));
+ x->x_ntypedout = n = argc;
+ for (; n--; to++)
+ {
+ to->to_outlet = outlet_new(&x->x_obj, &s_pointer);
+ to->to_type = canvas_makebindsym(atom_getsymbol(argv++));
+ }
+ x->x_otherout = outlet_new(&x->x_obj, &s_pointer);
+ x->x_bangout = outlet_new(&x->x_obj, &s_bang);
+ pointerinlet_new(&x->x_obj, &x->x_gp);
+ return (x);
+}
+
+static void ptrobj_traverse(t_ptrobj *x, t_symbol *s)
+{
+ t_glist *glist = (t_glist *)pd_findbyclass(s, canvas_class);
+ if (glist) gpointer_setglist(&x->x_gp, glist, 0);
+ else pd_error(x, "pointer: list '%s' not found", s->s_name);
+}
+
+static void ptrobj_vnext(t_ptrobj *x, float f)
+{
+ t_gobj *gobj;
+ t_gpointer *gp = &x->x_gp;
+ t_gstub *gs = gp->gp_stub;
+ t_glist *glist;
+ int wantselected = (f != 0);
+
+ if (!gs)
+ {
+ pd_error(x, "ptrobj_next: no current pointer");
+ return;
+ }
+ if (gs->gs_which != GP_GLIST)
+ {
+ pd_error(x, "ptrobj_next: lists only, not arrays");
+ return;
+ }
+ glist = gs->gs_un.gs_glist;
+ if (glist->gl_valid != gp->gp_valid)
+ {
+ pd_error(x, "ptrobj_next: stale pointer");
+ return;
+ }
+ if (wantselected && !glist_isvisible(glist))
+ {
+ pd_error(x,
+ "ptrobj_vnext: next-selected only works for a visible window");
+ return;
+ }
+ gobj = &gp->gp_un.gp_scalar->sc_gobj;
+
+ if (!gobj) gobj = glist->gl_list;
+ else gobj = gobj->g_next;
+ while (gobj && ((pd_class(&gobj->g_pd) != scalar_class) ||
+ (wantselected && !glist_isselected(glist, gobj))))
+ gobj = gobj->g_next;
+
+ if (gobj)
+ {
+ t_typedout *to;
+ int n;
+ t_scalar *sc = (t_scalar *)gobj;
+ t_symbol *templatesym = sc->sc_template;
+
+ gp->gp_un.gp_scalar = sc;
+ for (n = x->x_ntypedout, to = x->x_typedout; n--; to++)
+ {
+ if (to->to_type == templatesym)
+ {
+ outlet_pointer(to->to_outlet, &x->x_gp);
+ return;
+ }
+ }
+ outlet_pointer(x->x_otherout, &x->x_gp);
+ }
+ else
+ {
+ gpointer_unset(gp);
+ outlet_bang(x->x_bangout);
+ }
+}
+
+static void ptrobj_next(t_ptrobj *x)
+{
+ ptrobj_vnext(x, 0);
+}
+
+static void ptrobj_sendwindow(t_ptrobj *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_scalar *sc;
+ t_symbol *templatesym;
+ int n;
+ t_typedout *to;
+ t_glist *glist;
+ t_pd *canvas;
+ t_gstub *gs;
+ if (!gpointer_check(&x->x_gp, 1))
+ {
+ pd_error(x, "ptrobj_bang: empty pointer");
+ return;
+ }
+ gs = x->x_gp.gp_stub;
+ if (gs->gs_which == GP_GLIST)
+ glist = gs->gs_un.gs_glist;
+ else
+ {
+ t_array *owner_array = gs->gs_un.gs_array;
+ while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)
+ owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;
+ glist = owner_array->a_gp.gp_stub->gs_un.gs_glist;
+ }
+ canvas = (t_pd *)glist_getcanvas(glist);
+ if (argc && argv->a_type == A_SYMBOL)
+ pd_typedmess(canvas, argv->a_w.w_symbol, argc-1, argv+1);
+ else pd_error(x, "send-window: no message?");
+}
+
+static void ptrobj_bang(t_ptrobj *x)
+{
+ t_symbol *templatesym;
+ int n;
+ t_typedout *to;
+ if (!gpointer_check(&x->x_gp, 1))
+ {
+ pd_error(x, "ptrobj_bang: empty pointer");
+ return;
+ }
+ templatesym = gpointer_gettemplatesym(&x->x_gp);
+ for (n = x->x_ntypedout, to = x->x_typedout; n--; to++)
+ {
+ if (to->to_type == templatesym)
+ {
+ outlet_pointer(to->to_outlet, &x->x_gp);
+ return;
+ }
+ }
+ outlet_pointer(x->x_otherout, &x->x_gp);
+}
+
+
+static void ptrobj_pointer(t_ptrobj *x, t_gpointer *gp)
+{
+ gpointer_unset(&x->x_gp);
+ gpointer_copy(gp, &x->x_gp);
+ ptrobj_bang(x);
+}
+
+static void ptrobj_free(t_ptrobj *x)
+{
+ freebytes(x->x_typedout, x->x_ntypedout * sizeof (*x->x_typedout));
+ gpointer_unset(&x->x_gp);
+}
+
+static void ptrobj_setup(void)
+{
+ ptrobj_class = class_new(gensym("pointer"), (t_newmethod)ptrobj_new,
+ (t_method)ptrobj_free, sizeof(t_ptrobj), 0, A_GIMME, 0);
+ class_addmethod(ptrobj_class, (t_method)ptrobj_traverse, gensym("traverse"),
+ A_SYMBOL, 0);
+ class_addmethod(ptrobj_class, (t_method)ptrobj_next, gensym("next"), 0);
+ class_addmethod(ptrobj_class, (t_method)ptrobj_vnext, gensym("vnext"),
+ A_DEFFLOAT, 0);
+ class_addmethod(ptrobj_class, (t_method)ptrobj_sendwindow,
+ gensym("send-window"), A_GIMME, 0);
+ class_addpointer(ptrobj_class, ptrobj_pointer);
+ class_addbang(ptrobj_class, ptrobj_bang);
+}
+
+/* ---------------------- get ----------------------------- */
+
+static t_class *get_class;
+
+typedef struct _getvariable
+{
+ t_symbol *gv_sym;
+ t_outlet *gv_outlet;
+} t_getvariable;
+
+typedef struct _get
+{
+ t_object x_obj;
+ t_symbol *x_templatesym;
+ int x_nout;
+ t_getvariable *x_variables;
+} t_get;
+
+static void *get_new(t_symbol *why, int argc, t_atom *argv)
+{
+ t_get *x = (t_get *)pd_new(get_class);
+ int i;
+ t_getvariable *sp;
+ x->x_templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv));
+ if (argc) argc--, argv++;
+ x->x_variables
+ = (t_getvariable *)getbytes(argc * sizeof (*x->x_variables));
+ x->x_nout = argc;
+ for (i = 0, sp = x->x_variables; i < argc; i++, sp++)
+ {
+ sp->gv_sym = atom_getsymbolarg(i, argc, argv);
+ sp->gv_outlet = outlet_new(&x->x_obj, 0);
+ /* LATER connect with the template and set the outlet's type
+ correctly. We can't yet guarantee that the template is there
+ before we hit this routine. */
+ }
+ return (x);
+}
+
+static void get_pointer(t_get *x, t_gpointer *gp)
+{
+ int nitems = x->x_nout, i;
+ t_symbol *templatesym = x->x_templatesym;
+ t_template *template = template_findbyname(templatesym);
+ t_gstub *gs = gp->gp_stub;
+ t_word *vec;
+ t_getvariable *vp;
+ if (!template)
+ {
+ pd_error(x, "get: couldn't find template %s", templatesym->s_name);
+ return;
+ }
+ if (gpointer_ishead(gp))
+ {
+ pd_error(x, "get: empty pointer");
+ return;
+ }
+ if (gs->gs_which == GP_ARRAY) vec = gp->gp_un.gp_w;
+ else vec = gp->gp_un.gp_scalar->sc_vec;
+ for (i = nitems - 1, vp = x->x_variables + i; i >= 0; i--, vp--)
+ {
+ float f = template_getfloat(template, vp->gv_sym, vec, 1);
+ outlet_float(vp->gv_outlet, f);
+ /* LATER deal with other types. */
+ }
+}
+
+static void get_free(t_get *x)
+{
+ freebytes(x->x_variables, x->x_nout * sizeof (*x->x_variables));
+}
+
+static void get_setup(void)
+{
+ get_class = class_new(gensym("get"), (t_newmethod)get_new,
+ (t_method)get_free, sizeof(t_get), 0, A_GIMME, 0);
+ class_addpointer(get_class, get_pointer);
+}
+
+/* ---------------------- set ----------------------------- */
+
+static t_class *set_class;
+
+typedef struct _setvariable
+{
+ t_symbol *gv_sym;
+ t_float gv_f; /* LATER take other types */
+} t_setvariable;
+
+typedef struct _set
+{
+ t_object x_obj;
+ t_gpointer x_gp;
+ t_symbol *x_templatesym;
+ int x_nin;
+ t_setvariable *x_variables;
+} t_set;
+
+static void *set_new(t_symbol *why, int argc, t_atom *argv)
+{
+ t_set *x = (t_set *)pd_new(set_class);
+ int i;
+ t_setvariable *sp;
+ x->x_templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv));
+ if (argc) argc--, argv++;
+ x->x_variables
+ = (t_setvariable *)getbytes(argc * sizeof (*x->x_variables));
+ x->x_nin = argc;
+ if (argc)
+ {
+ for (i = 0, sp = x->x_variables; i < argc; i++, sp++)
+ {
+ sp->gv_sym = atom_getsymbolarg(i, argc, argv);
+ sp->gv_f = 0;
+ if (i) floatinlet_new(&x->x_obj, &sp->gv_f);
+ /* LATER figure out type as in "get" object. */
+ }
+ }
+ pointerinlet_new(&x->x_obj, &x->x_gp);
+ gpointer_init(&x->x_gp);
+ return (x);
+}
+
+static void set_float(t_set *x, t_float f)
+{
+ int nitems = x->x_nin, i;
+ t_symbol *templatesym = x->x_templatesym;
+ t_template *template = template_findbyname(templatesym);
+ t_setvariable *vp;
+ t_gpointer *gp = &x->x_gp;
+ t_gstub *gs = gp->gp_stub;
+ t_word *vec;
+ if (!template)
+ {
+ pd_error(x, "set: couldn't find template %s", templatesym->s_name);
+ return;
+ }
+ if (!gpointer_check(gp, 0))
+ {
+ pd_error(x, "set: empty pointer");
+ return;
+ }
+ if (gpointer_gettemplatesym(gp) != x->x_templatesym)
+ {
+ pd_error(x, "set %s: got wrong template (%s)",
+ x->x_templatesym->s_name, gpointer_gettemplatesym(gp)->s_name);
+ return;
+ }
+ if (!nitems) return;
+ x->x_variables[0].gv_f = f;
+ if (gs->gs_which == GP_ARRAY) vec = gp->gp_un.gp_w;
+ else vec = gp->gp_un.gp_scalar->sc_vec;
+ for (i = 0, vp = x->x_variables; i < nitems; i++, vp++)
+ {
+ template_setfloat(template, vp->gv_sym, vec, vp->gv_f, 1);
+ /* LATER deal with other types ala get_pointer. */
+ }
+ if (gs->gs_which == GP_GLIST)
+ glist_redrawitem(gs->gs_un.gs_glist, (t_gobj *)(gp->gp_un.gp_scalar));
+ else
+ {
+ t_array *owner_array = gs->gs_un.gs_array;
+ while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)
+ owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;
+ glist_redrawitem(owner_array->a_gp.gp_stub->gs_un.gs_glist,
+ (t_gobj *)(owner_array->a_gp.gp_un.gp_scalar));
+ }
+}
+
+static void set_free(t_set *x)
+{
+ freebytes(x->x_variables, x->x_nin * sizeof (*x->x_variables));
+ gpointer_unset(&x->x_gp);
+}
+
+static void set_setup(void)
+{
+ set_class = class_new(gensym("set"), (t_newmethod)set_new,
+ (t_method)set_free, sizeof(t_set), 0, A_GIMME, 0);
+ class_addfloat(set_class, set_float);
+}
+
+/* ---------------------- elem ----------------------------- */
+
+static t_class *elem_class;
+
+typedef struct _elem
+{
+ t_object x_obj;
+ t_symbol *x_templatesym;
+ t_symbol *x_fieldsym;
+ t_gpointer x_gp;
+ t_gpointer x_gparent;
+} t_elem;
+
+static void *elem_new(t_symbol *templatesym, t_symbol *fieldsym)
+{
+ t_elem *x = (t_elem *)pd_new(elem_class);
+ x->x_templatesym = canvas_makebindsym(templatesym);
+ x->x_fieldsym = fieldsym;
+ gpointer_init(&x->x_gp);
+ gpointer_init(&x->x_gparent);
+ pointerinlet_new(&x->x_obj, &x->x_gparent);
+ outlet_new(&x->x_obj, &s_pointer);
+ return (x);
+}
+
+static void elem_float(t_elem *x, t_float f)
+{
+ int indx = f, nitems, onset;
+ t_symbol *templatesym = x->x_templatesym, *fieldsym = x->x_fieldsym,
+ *elemtemplatesym;
+ t_template *template = template_findbyname(templatesym);
+ t_template *elemtemplate;
+ t_gpointer *gparent = &x->x_gparent;
+ t_word *w;
+ t_array *array;
+ int elemsize, type;
+
+ if (!gpointer_check(gparent, 0))
+ {
+ pd_error(x, "element: empty pointer");
+ return;
+ }
+ if (gpointer_gettemplatesym(gparent) != x->x_templatesym)
+ {
+ pd_error(x, "element %s: got wrong template (%s)",
+ x->x_templatesym->s_name, gpointer_gettemplatesym(gparent)->s_name);
+ return;
+ }
+ if (gparent->gp_stub->gs_which == GP_ARRAY) w = gparent->gp_un.gp_w;
+ else w = gparent->gp_un.gp_scalar->sc_vec;
+ if (!template)
+ {
+ pd_error(x, "element: couldn't find template %s", templatesym->s_name);
+ return;
+ }
+ if (!template_find_field(template, fieldsym,
+ &onset, &type, &elemtemplatesym))
+ {
+ pd_error(x, "element: couldn't find array field %s", fieldsym->s_name);
+ return;
+ }
+ if (type != DT_ARRAY)
+ {
+ pd_error(x, "element: field %s not of type array", fieldsym->s_name);
+ return;
+ }
+ if (!(elemtemplate = template_findbyname(elemtemplatesym)))
+ {
+ pd_error(x, "element: couldn't find field template %s",
+ elemtemplatesym->s_name);
+ return;
+ }
+
+ elemsize = elemtemplate->t_n * sizeof(t_word);
+
+ array = *(t_array **)(((char *)w) + onset);
+
+ nitems = array->a_n;
+ if (indx < 0) indx = 0;
+ if (indx >= nitems) indx = nitems-1;
+
+ gpointer_setarray(&x->x_gp, array,
+ (t_word *)((char *)(array->a_vec) + indx * elemsize));
+ outlet_pointer(x->x_obj.ob_outlet, &x->x_gp);
+}
+
+static void elem_free(t_elem *x, t_gpointer *gp)
+{
+ gpointer_unset(&x->x_gp);
+ gpointer_unset(&x->x_gparent);
+}
+
+static void elem_setup(void)
+{
+ elem_class = class_new(gensym("element"), (t_newmethod)elem_new,
+ (t_method)elem_free, sizeof(t_elem), 0, A_DEFSYM, A_DEFSYM, 0);
+ class_addfloat(elem_class, elem_float);
+}
+
+/* ---------------------- getsize ----------------------------- */
+
+static t_class *getsize_class;
+
+typedef struct _getsize
+{
+ t_object x_obj;
+ t_symbol *x_templatesym;
+ t_symbol *x_fieldsym;
+} t_getsize;
+
+static void *getsize_new(t_symbol *templatesym, t_symbol *fieldsym)
+{
+ t_getsize *x = (t_getsize *)pd_new(getsize_class);
+ x->x_templatesym = canvas_makebindsym(templatesym);
+ x->x_fieldsym = fieldsym;
+ outlet_new(&x->x_obj, &s_float);
+ return (x);
+}
+
+static void getsize_pointer(t_getsize *x, t_gpointer *gp)
+{
+ int nitems, onset, type;
+ t_symbol *templatesym = x->x_templatesym, *fieldsym = x->x_fieldsym,
+ *elemtemplatesym;
+ t_template *template = template_findbyname(templatesym);
+ t_word *w;
+ t_array *array;
+ int elemsize;
+ t_gstub *gs = gp->gp_stub;
+ if (!template)
+ {
+ pd_error(x, "getsize: couldn't find template %s", templatesym->s_name);
+ return;
+ }
+ if (!template_find_field(template, fieldsym,
+ &onset, &type, &elemtemplatesym))
+ {
+ pd_error(x, "getsize: couldn't find array field %s", fieldsym->s_name);
+ return;
+ }
+ if (type != DT_ARRAY)
+ {
+ pd_error(x, "getsize: field %s not of type array", fieldsym->s_name);
+ return;
+ }
+ if (gpointer_ishead(gp))
+ {
+ pd_error(x, "getsize: empty pointer");
+ return;
+ }
+ if (gpointer_gettemplatesym(gp) != x->x_templatesym)
+ {
+ pd_error(x, "getsize %s: got wrong template (%s)",
+ x->x_templatesym->s_name, gpointer_gettemplatesym(gp)->s_name);
+ return;
+ }
+ if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w;
+ else w = gp->gp_un.gp_scalar->sc_vec;
+
+ array = *(t_array **)(((char *)w) + onset);
+ outlet_float(x->x_obj.ob_outlet, (float)(array->a_n));
+}
+
+static void getsize_setup(void)
+{
+ getsize_class = class_new(gensym("getsize"), (t_newmethod)getsize_new, 0,
+ sizeof(t_getsize), 0, A_DEFSYM, A_DEFSYM, 0);
+ class_addpointer(getsize_class, getsize_pointer);
+}
+
+/* ---------------------- setsize ----------------------------- */
+
+static t_class *setsize_class;
+
+typedef struct _setsize
+{
+ t_object x_obj;
+ t_symbol *x_templatesym;
+ t_symbol *x_fieldsym;
+ t_gpointer x_gp;
+} t_setsize;
+
+static void *setsize_new(t_symbol *templatesym, t_symbol *fieldsym,
+ t_floatarg newsize)
+{
+ t_setsize *x = (t_setsize *)pd_new(setsize_class);
+ x->x_templatesym = canvas_makebindsym(templatesym);
+ x->x_fieldsym = fieldsym;
+ gpointer_init(&x->x_gp);
+
+ pointerinlet_new(&x->x_obj, &x->x_gp);
+ return (x);
+}
+
+static void setsize_float(t_setsize *x, t_float f)
+{
+ int nitems, onset, type;
+ t_symbol *templatesym = x->x_templatesym, *fieldsym = x->x_fieldsym,
+ *elemtemplatesym;
+ t_template *template = template_findbyname(templatesym);
+ t_template *elemtemplate;
+ t_word *w;
+ t_atom at;
+ t_array *array;
+ int elemsize;
+ int newsize = f;
+ t_gpointer *gp = &x->x_gp;
+ t_gstub *gs = gp->gp_stub;
+ if (!gpointer_check(&x->x_gp, 0))
+ {
+ pd_error(x, "setsize: empty pointer");
+ return;
+ }
+ if (gpointer_gettemplatesym(&x->x_gp) != x->x_templatesym)
+ {
+ pd_error(x, "setsize %s: got wrong template (%s)",
+ x->x_templatesym->s_name,
+ gpointer_gettemplatesym(&x->x_gp)->s_name);
+ return;
+ }
+ if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w;
+ else w = gp->gp_un.gp_scalar->sc_vec;
+
+ if (!template)
+ {
+ pd_error(x,"setsize: couldn't find template %s", templatesym->s_name);
+ return;
+ }
+ if (!template_find_field(template, fieldsym,
+ &onset, &type, &elemtemplatesym))
+ {
+ pd_error(x,"setsize: couldn't find array field %s", fieldsym->s_name);
+ return;
+ }
+ if (type != DT_ARRAY)
+ {
+ pd_error(x,"setsize: field %s not of type array", fieldsym->s_name);
+ return;
+ }
+
+ if (!(elemtemplate = template_findbyname(elemtemplatesym)))
+ {
+ pd_error(x,"element: couldn't find field template %s",
+ elemtemplatesym->s_name);
+ return;
+ }
+
+ elemsize = elemtemplate->t_n * sizeof(t_word);
+
+ array = *(t_array **)(((char *)w) + onset);
+
+ if (elemsize != array->a_elemsize) bug("setsize_gpointer");
+
+ nitems = array->a_n;
+ if (newsize < 1) newsize = 1;
+ if (newsize == nitems) return;
+
+ /* erase the array before resizing it. If we belong to a
+ scalar it's easy, but if we belong to an element of another
+ array we have to search back until we get to a scalar to erase.
+ When graphics updates become queueable this may fall apart... */
+
+
+ if (gs->gs_which == GP_GLIST)
+ {
+ if (glist_isvisible(gs->gs_un.gs_glist))
+ gobj_vis((t_gobj *)(gp->gp_un.gp_scalar), gs->gs_un.gs_glist, 0);
+ }
+ else
+ {
+ t_array *owner_array = gs->gs_un.gs_array;
+ while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)
+ owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;
+ if (glist_isvisible(owner_array->a_gp.gp_stub->gs_un.gs_glist))
+ gobj_vis((t_gobj *)(owner_array->a_gp.gp_un.gp_scalar),
+ owner_array->a_gp.gp_stub->gs_un.gs_glist, 0);
+ }
+ /* now do the resizing and, if growing, initialize new scalars */
+ array->a_vec = (char *)resizebytes(array->a_vec,
+ elemsize * nitems, elemsize * newsize);
+ array->a_n = newsize;
+ if (newsize > nitems)
+ {
+ char *newelem = ((char *)array->a_vec) + nitems * elemsize;
+ int i = 0, nnew = newsize - nitems;
+
+ while (nnew--)
+ {
+ word_init((t_word *)newelem, elemtemplate, gp);
+ newelem += elemsize;
+ /* post("new %x %x, ntypes %d", newelem, *(int *)newelem, ntypes); */
+ }
+ }
+
+ /* redraw again. */
+ if (gs->gs_which == GP_GLIST)
+ {
+ if (glist_isvisible(gs->gs_un.gs_glist))
+ gobj_vis((t_gobj *)(gp->gp_un.gp_scalar), gs->gs_un.gs_glist, 1);
+ }
+ else
+ {
+ t_array *owner_array = gs->gs_un.gs_array;
+ while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)
+ owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;
+ if (glist_isvisible(owner_array->a_gp.gp_stub->gs_un.gs_glist))
+ gobj_vis((t_gobj *)(owner_array->a_gp.gp_un.gp_scalar),
+ owner_array->a_gp.gp_stub->gs_un.gs_glist, 1);
+ }
+}
+
+
+static void setsize_free(t_setsize *x)
+{
+ gpointer_unset(&x->x_gp);
+}
+
+static void setsize_setup(void)
+{
+ setsize_class = class_new(gensym("setsize"), (t_newmethod)setsize_new,
+ (t_method)setsize_free, sizeof(t_setsize), 0,
+ A_DEFSYM, A_DEFSYM, A_DEFFLOAT, 0);
+ class_addfloat(setsize_class, setsize_float);
+}
+
+/* ---------------------- append ----------------------------- */
+
+static t_class *append_class;
+
+typedef struct _appendvariable
+{
+ t_symbol *gv_sym;
+ t_float gv_f;
+} t_appendvariable;
+
+typedef struct _append
+{
+ t_object x_obj;
+ t_gpointer x_gp;
+ t_symbol *x_templatesym;
+ int x_nin;
+ t_appendvariable *x_variables;
+} t_append;
+
+static void *append_new(t_symbol *why, int argc, t_atom *argv)
+{
+ t_append *x = (t_append *)pd_new(append_class);
+ int i;
+ t_appendvariable *sp;
+ x->x_templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv));
+ if (argc) argc--, argv++;
+ x->x_variables
+ = (t_appendvariable *)getbytes(argc * sizeof (*x->x_variables));
+ x->x_nin = argc;
+ if (argc)
+ {
+ for (i = 0, sp = x->x_variables; i < argc; i++, sp++)
+ {
+ sp->gv_sym = atom_getsymbolarg(i, argc, argv);
+ sp->gv_f = 0;
+ if (i) floatinlet_new(&x->x_obj, &sp->gv_f);
+ }
+ }
+ pointerinlet_new(&x->x_obj, &x->x_gp);
+ outlet_new(&x->x_obj, &s_pointer);
+ gpointer_init(&x->x_gp);
+ return (x);
+}
+
+static void append_float(t_append *x, t_float f)
+{
+ int nitems = x->x_nin, i;
+ t_symbol *templatesym = x->x_templatesym;
+ t_template *template = template_findbyname(templatesym);
+ t_appendvariable *vp;
+ t_gpointer *gp = &x->x_gp;
+ t_gstub *gs = gp->gp_stub;
+ t_word *vec;
+ t_scalar *sc, *oldsc;
+ t_glist *glist;
+ if (!template)
+ {
+ pd_error(x, "append: couldn't find template %s", templatesym->s_name);
+ return;
+ }
+ if (!gs)
+ {
+ pd_error(x, "append: no current pointer");
+ return;
+ }
+ if (gs->gs_which != GP_GLIST)
+ {
+ pd_error(x, "append: lists only, not arrays");
+ return;
+ }
+ glist = gs->gs_un.gs_glist;
+ if (glist->gl_valid != gp->gp_valid)
+ {
+ pd_error(x, "append: stale pointer");
+ return;
+ }
+ if (!nitems) return;
+ x->x_variables[0].gv_f = f;
+
+ sc = scalar_new(glist, templatesym);
+ if (!sc)
+ {
+ pd_error(x, "%s: couldn't create scalar", templatesym->s_name);
+ return;
+ }
+ oldsc = gp->gp_un.gp_scalar;
+
+ if (oldsc)
+ {
+ sc->sc_gobj.g_next = oldsc->sc_gobj.g_next;
+ oldsc->sc_gobj.g_next = &sc->sc_gobj;
+ }
+ else
+ {
+ sc->sc_gobj.g_next = glist->gl_list;
+ glist->gl_list = &sc->sc_gobj;
+ }
+ if (glist_isvisible(glist_getcanvas(glist)))
+ gobj_vis(&sc->sc_gobj, glist, 1);
+
+ gp->gp_un.gp_scalar = sc;
+ vec = sc->sc_vec;
+ for (i = 0, vp = x->x_variables; i < nitems; i++, vp++)
+ {
+ template_setfloat(template, vp->gv_sym, vec, vp->gv_f, 1);
+ }
+
+ glist_redrawitem(glist, (t_gobj *)sc);
+
+ outlet_pointer(x->x_obj.ob_outlet, gp);
+}
+
+static void append_free(t_append *x)
+{
+ freebytes(x->x_variables, x->x_nin * sizeof (*x->x_variables));
+ gpointer_unset(&x->x_gp);
+}
+
+static void append_setup(void)
+{
+ append_class = class_new(gensym("append"), (t_newmethod)append_new,
+ (t_method)append_free, sizeof(t_append), 0, A_GIMME, 0);
+ class_addfloat(append_class, append_float);
+}
+
+/* ---------------------- sublist ----------------------------- */
+
+static t_class *sublist_class;
+
+typedef struct _sublist
+{
+ t_object x_obj;
+ t_symbol *x_templatesym;
+ t_symbol *x_fieldsym;
+ t_gpointer x_gp;
+} t_sublist;
+
+static void *sublist_new(t_symbol *templatesym, t_symbol *fieldsym)
+{
+ t_sublist *x = (t_sublist *)pd_new(sublist_class);
+ x->x_templatesym = canvas_makebindsym(templatesym);
+ x->x_fieldsym = fieldsym;
+ gpointer_init(&x->x_gp);
+ outlet_new(&x->x_obj, &s_pointer);
+ return (x);
+}
+
+static void sublist_pointer(t_sublist *x, t_gpointer *gp)
+{
+ t_symbol *templatesym = x->x_templatesym, *dummy;
+ t_template *template = template_findbyname(templatesym);
+ t_gstub *gs = gp->gp_stub;
+ t_word *vec;
+ t_getvariable *vp;
+ int onset, type;
+ t_word *w;
+
+ if (!template)
+ {
+ pd_error(x, "sublist: couldn't find template %s", templatesym->s_name);
+ return;
+ }
+ if (gpointer_ishead(gp))
+ {
+ pd_error(x, "sublist: empty pointer");
+ return;
+ }
+ if (!template_find_field(template, x->x_fieldsym,
+ &onset, &type, &dummy))
+ {
+ pd_error(x, "sublist: couldn't find field %s", x->x_fieldsym->s_name);
+ return;
+ }
+ if (type != DT_LIST)
+ {
+ pd_error(x, "sublist: field %s not of type list", x->x_fieldsym->s_name);
+ return;
+ }
+ if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w;
+ else w = gp->gp_un.gp_scalar->sc_vec;
+
+ gpointer_setglist(&x->x_gp, *(t_glist **)(((char *)w) + onset), 0);
+
+ outlet_pointer(x->x_obj.ob_outlet, &x->x_gp);
+}
+
+static void sublist_free(t_sublist *x, t_gpointer *gp)
+{
+ gpointer_unset(&x->x_gp);
+}
+
+static void sublist_setup(void)
+{
+ sublist_class = class_new(gensym("sublist"), (t_newmethod)sublist_new,
+ (t_method)sublist_free, sizeof(t_sublist), 0, A_DEFSYM, A_DEFSYM, 0);
+ class_addpointer(sublist_class, sublist_pointer);
+}
+
+/* ----------------- setup function ------------------- */
+
+void g_traversal_setup(void)
+{
+ ptrobj_setup();
+ get_setup();
+ set_setup();
+ elem_setup();
+ getsize_setup();
+ setsize_setup();
+ append_setup();
+ sublist_setup();
+}
diff --git a/pd/src/g_vdial.c b/pd/src/g_vdial.c
new file mode 100644
index 00000000..49c8d0d4
--- /dev/null
+++ b/pd/src/g_vdial.c
@@ -0,0 +1,672 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* vdial.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include "t_tk.h"
+#include "g_all_guis.h"
+#include <math.h>
+
+/*------------------ global varaibles -------------------------*/
+
+
+/*------------------ global functions -------------------------*/
+
+
+
+
+/* ------------- vdl gui-vertical dial ---------------------- */
+
+t_widgetbehavior vdial_widgetbehavior;
+static t_class *vdial_class;
+
+/* widget helper functions */
+
+void vdial_draw_update(t_vdial *x, t_glist *glist)
+{
+ if(glist_isvisible(glist))
+ {
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n",
+ canvas, x, x->x_on_old,
+ x->x_gui.x_bcol, x->x_gui.x_bcol);
+ sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n",
+ canvas, x, x->x_on,
+ x->x_gui.x_fcol, x->x_gui.x_fcol);
+ }
+}
+
+void vdial_draw_new(t_vdial *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int n=x->x_number, i, dy=x->x_gui.x_h, s4=dy/4;
+ int yy11b=text_ypix(&x->x_gui.x_obj, glist);
+ int yy11=yy11b, yy12=yy11+dy;
+ int yy21=yy11+s4, yy22=yy12-s4;
+ int xx11=text_xpix(&x->x_gui.x_obj, glist), xx12=xx11+dy;
+ int xx21=xx11+s4, xx22=xx12-s4;
+
+ for(i=0; i<n; i++)
+ {
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE%d\n",
+ canvas, xx11, yy11, xx12, yy12,
+ x->x_gui.x_bcol, x, i);
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -outline #%6.6x -tags %xBUT%d\n",
+ canvas, xx21, yy21, xx22, yy22,
+ (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol,
+ (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, x, i);
+ yy11 += dy;
+ yy12 += dy;
+ yy21 += dy;
+ yy22 += dy;
+ }
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n",
+ canvas, xx11+x->x_gui.x_ldx, yy11b+x->x_gui.x_ldy,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"",
+ x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_lcol, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas, xx11, yy11-1, xx11 + IOWIDTH, yy11, x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas, xx11, yy11b, xx11 + IOWIDTH, yy11b+1, x, 0);
+}
+
+void vdial_draw_move(t_vdial *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int n=x->x_number, i, dy=x->x_gui.x_h, s4=dy/4;
+ int yy11b=text_ypix(&x->x_gui.x_obj, glist);
+ int yy11=yy11b, yy12=yy11+dy;
+ int yy21=yy11+s4, yy22=yy12-s4;
+ int xx11=text_xpix(&x->x_gui.x_obj, glist), xx12=xx11+dy;
+ int xx21=xx11+s4, xx22=xx12-s4;
+
+ for(i=0; i<n; i++)
+ {
+ sys_vgui(".x%x.c coords %xBASE%d %d %d %d %d\n",
+ canvas, x, i, xx11, yy11, xx12, yy12);
+ sys_vgui(".x%x.c coords %xBUT%d %d %d %d %d\n",
+ canvas, x, i, xx21, yy21, xx22, yy22);
+ yy11 += dy;
+ yy12 += dy;
+ yy21 += dy;
+ yy22 += dy;
+ }
+ sys_vgui(".x%x.c coords %xLABEL %d %d\n",
+ canvas, x, xx11+x->x_gui.x_ldx, yy11b+x->x_gui.x_ldy);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n",
+ canvas, x, 0, xx11, yy11-1, xx11 + IOWIDTH, yy11);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n",
+ canvas, x, 0, xx11, yy11b, xx11 + IOWIDTH, yy11b+1);
+}
+
+void vdial_draw_erase(t_vdial* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int n=x->x_number, i;
+
+ for(i=0; i<n; i++)
+ {
+ sys_vgui(".x%x.c delete %xBASE%d\n", canvas, x, i);
+ sys_vgui(".x%x.c delete %xBUT%d\n", canvas, x, i);
+ }
+ sys_vgui(".x%x.c delete %xLABEL\n", canvas, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+void vdial_draw_config(t_vdial* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int n=x->x_number, i;
+
+ sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n",
+ canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"");
+ for(i=0; i<n; i++)
+ {
+ sys_vgui(".x%x.c itemconfigure %xBASE%d -fill #%6.6x\n", canvas, x, i,
+ x->x_gui.x_bcol);
+ sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", canvas, x, i,
+ (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol,
+ (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol);
+ }
+}
+
+void vdial_draw_io(t_vdial* x, t_glist* glist, int old_snd_rcv_flags)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+
+ if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas, xpos,
+ ypos+(x->x_number*x->x_gui.x_h)-1,
+ xpos+ IOWIDTH,
+ ypos+(x->x_number*x->x_gui.x_h), x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas, xpos, ypos,
+ xpos+ IOWIDTH, ypos+1,
+ x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+void vdial_draw_select(t_vdial* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int n=x->x_number, i;
+
+ if(x->x_gui.x_fsf.x_selected)
+ {
+ pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ for(i=0; i<n; i++)
+ {
+ sys_vgui(".x%x.c itemconfigure %xBASE%d -outline #%6.6x\n", canvas, x, i,
+ IEM_GUI_COLOR_SELECTED);
+ }
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ }
+ else
+ {
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ for(i=0; i<n; i++)
+ {
+ sys_vgui(".x%x.c itemconfigure %xBASE%d -outline #%6.6x\n", canvas, x, i,
+ IEM_GUI_COLOR_NORMAL);
+ }
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x,
+ x->x_gui.x_lcol);
+ }
+}
+
+void vdial_draw(t_vdial *x, t_glist *glist, int mode)
+{
+ if(mode == IEM_GUI_DRAW_MODE_UPDATE)
+ vdial_draw_update(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_MOVE)
+ vdial_draw_move(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_NEW)
+ vdial_draw_new(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_SELECT)
+ vdial_draw_select(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_ERASE)
+ vdial_draw_erase(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_CONFIG)
+ vdial_draw_config(x, glist);
+ else if(mode >= IEM_GUI_DRAW_MODE_IO)
+ vdial_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO);
+}
+
+/* ------------------------ vdl widgetbehaviour----------------------------- */
+
+static void vdial_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_vdial *x = (t_vdial *)z;
+
+ *xp1 = text_xpix(&x->x_gui.x_obj, glist);
+ *yp1 = text_ypix(&x->x_gui.x_obj, glist);
+ *xp2 = *xp1 + x->x_gui.x_w;
+ *yp2 = *yp1 + x->x_gui.x_h*x->x_number;
+}
+
+static void vdial_save(t_gobj *z, t_binbuf *b)
+{
+ t_vdial *x = (t_vdial *)z;
+ int bflcol[3], *ip1, *ip2;
+ t_symbol *srl[3];
+
+ iemgui_save(&x->x_gui, srl, bflcol);
+ ip1 = (int *)(&x->x_gui.x_isa);
+ ip2 = (int *)(&x->x_gui.x_fsf);
+ binbuf_addv(b, "ssiisiiiisssiiiiiiii", gensym("#X"),gensym("obj"),
+ (t_int)x->x_gui.x_obj.te_xpix,
+ (t_int)x->x_gui.x_obj.te_ypix,
+ gensym("vdl"), x->x_gui.x_w,
+ x->x_change, (*ip1)&IEM_INIT_ARGS_ALL, x->x_number,
+ srl[0], srl[1], srl[2],
+ x->x_gui.x_ldx, x->x_gui.x_ldy,
+ (*ip2)&IEM_FSTYLE_FLAGS_ALL, x->x_gui.x_fontsize,
+ bflcol[0], bflcol[1], bflcol[2], x->x_on);
+ binbuf_addv(b, ";");
+}
+
+static void vdial_properties(t_gobj *z, t_glist *owner)
+{
+ t_vdial *x = (t_vdial *)z;
+ char buf[800];
+ t_symbol *srl[3];
+
+ iemgui_properties(&x->x_gui, srl);
+ sprintf(buf, "pdtk_iemgui_dialog %%s VDIAL \
+ ----------dimensions(pix):----------- %d %d size: 0 0 empty \
+ empty 0.0 empty 0.0 empty %d \
+ %d new-only new&old %d %d number: %d \
+ %s %s \
+ %s %d %d \
+ %d %d \
+ %d %d %d\n",
+ x->x_gui.x_w, IEM_GUI_MINSIZE,
+ 0,/*no_schedule*/
+ x->x_change, x->x_gui.x_isa.x_loadinit, -1, x->x_number,
+ srl[0]->s_name, srl[1]->s_name,
+ srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy,
+ x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize,
+ 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol);
+ gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf);
+}
+
+static void vdial_dialog(t_vdial *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *srl[3];
+ int a = (int)atom_getintarg(0, argc, argv);
+ int chg = (int)atom_getintarg(4, argc, argv);
+ int num = (int)atom_getintarg(6, argc, argv);
+ int sr_flags;
+
+ if(chg != 0) chg = 1;
+ x->x_change = chg;
+ sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);
+ x->x_gui.x_w = iemgui_clip_size(a);
+ x->x_gui.x_h = x->x_gui.x_w;
+ if(x->x_number != num)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE);
+ x->x_number = num;
+ if(x->x_on >= x->x_number)
+ {
+ x->x_on = x->x_number - 1;
+ x->x_on_old = x->x_on;
+ }
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW);
+ }
+ else
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+ }
+}
+
+static void vdial_set(t_vdial *x, t_floatarg f)
+{
+ int i=(int)f;
+ int old;
+
+ if(i < 0)
+ i = 0;
+ if(i >= x->x_number)
+ i = x->x_number-1;
+ if(x->x_on != x->x_on_old)
+ {
+ old = x->x_on_old;
+ x->x_on_old = x->x_on;
+ x->x_on = i;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ x->x_on_old = old;
+ }
+ else
+ {
+ x->x_on = i;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ }
+}
+
+static void vdial_bang(t_vdial *x)
+{
+ if((x->x_change)&&(x->x_on != x->x_on_old))
+ {
+ SETFLOAT(x->x_at, (float)x->x_on_old);
+ SETFLOAT(x->x_at+1, 0.0);
+ outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);
+ }
+ x->x_on_old = x->x_on;
+ SETFLOAT(x->x_at, (float)x->x_on);
+ SETFLOAT(x->x_at+1, 1.0);
+ outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);
+}
+
+static void vdial_fout(t_vdial *x, t_floatarg f)
+{
+ int i=(int)f;
+
+ if(i < 0)
+ i = 0;
+ if(i >= x->x_number)
+ i = x->x_number-1;
+
+ if((x->x_change)&&(i != x->x_on_old))
+ {
+ SETFLOAT(x->x_at, (float)x->x_on_old);
+ SETFLOAT(x->x_at+1, 0.0);
+ outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);
+ }
+ if(x->x_on != x->x_on_old)
+ x->x_on_old = x->x_on;
+ x->x_on = i;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ x->x_on_old = x->x_on;
+ SETFLOAT(x->x_at, (float)x->x_on);
+ SETFLOAT(x->x_at+1, 1.0);
+ outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);
+}
+
+static void vdial_float(t_vdial *x, t_floatarg f)
+{
+ int i=(int)f;
+
+ if(i < 0)
+ i = 0;
+ if(i >= x->x_number)
+ i = x->x_number-1;
+
+ if((x->x_change)&&(i != x->x_on_old))
+ {
+ if(x->x_gui.x_fsf.x_put_in2out)
+ {
+ SETFLOAT(x->x_at, (float)x->x_on_old);
+ SETFLOAT(x->x_at+1, 0.0);
+ outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);
+ }
+ }
+ if(x->x_on != x->x_on_old)
+ x->x_on_old = x->x_on;
+ x->x_on = i;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ x->x_on_old = x->x_on;
+ if(x->x_gui.x_fsf.x_put_in2out)
+ {
+ SETFLOAT(x->x_at, (float)x->x_on);
+ SETFLOAT(x->x_at+1, 1.0);
+ outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);
+ }
+}
+
+static void vdial_click(t_vdial *x, t_floatarg xpos, t_floatarg ypos,
+ t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
+{
+ int yy = (int)ypos - text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist);
+
+ vdial_fout(x, (float)(yy / x->x_gui.x_h));
+}
+
+static int vdial_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ if(doit)
+ vdial_click((t_vdial *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt);
+ return (1);
+}
+
+static void vdial_loadbang(t_vdial *x)
+{
+ if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit)
+ vdial_bang(x);
+}
+
+static void vdial_number(t_vdial *x, t_floatarg num)
+{
+ int n=(int)num;
+
+ if(n < 1)
+ n = 1;
+ if(n > IEM_RADIO_MAX)
+ n = IEM_RADIO_MAX;
+ if(n != x->x_number)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE);
+ x->x_number = n;
+ if(x->x_on >= x->x_number)
+ x->x_on = x->x_number - 1;
+ x->x_on_old = x->x_on;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW);
+ }
+}
+
+static void vdial_size(t_vdial *x, t_symbol *s, int ac, t_atom *av)
+{
+ x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av));
+ x->x_gui.x_h = x->x_gui.x_w;
+ iemgui_size((void *)x, &x->x_gui);
+}
+
+static void vdial_delta(t_vdial *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}
+
+static void vdial_pos(t_vdial *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void vdial_color(t_vdial *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_color((void *)x, &x->x_gui, s, ac, av);}
+
+static void vdial_send(t_vdial *x, t_symbol *s)
+{iemgui_send(x, &x->x_gui, s);}
+
+static void vdial_receive(t_vdial *x, t_symbol *s)
+{iemgui_receive(x, &x->x_gui, s);}
+
+static void vdial_label(t_vdial *x, t_symbol *s)
+{iemgui_label((void *)x, &x->x_gui, s);}
+
+static void vdial_label_pos(t_vdial *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void vdial_label_font(t_vdial *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);}
+
+static void vdial_init(t_vdial *x, t_floatarg f)
+{
+ x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1;
+}
+
+static void vdial_double_change(t_vdial *x)
+{x->x_change = 1;}
+
+static void vdial_single_change(t_vdial *x)
+{x->x_change = 0;}
+
+static void vdial_list(t_vdial *x, t_symbol *s, int ac, t_atom *av)
+{
+ int l=iemgui_list((void *)x, &x->x_gui, s, ac, av);
+
+ if(l < 0)
+ {
+ if(IS_A_FLOAT(av,0))
+ vdial_float(x, atom_getfloatarg(0, ac, av));
+ }
+ else if(l > 0)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+ }
+}
+
+static void *vdial_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_vdial *x = (t_vdial *)pd_new(vdial_class);
+ int bflcol[]={-262144, -1, -1};
+ t_symbol *srl[3];
+ int a=IEM_GUI_DEFAULTSIZE, on=0, f=0;
+ int ldx=0, ldy=-6, chg=1, num=8;
+ int fs=8, iinit=0, ifstyle=0;
+ int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME;
+ t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit);
+ t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle);
+ char str[144];
+
+ srl[0] = gensym("empty");
+ srl[1] = gensym("empty");
+ srl[2] = gensym("empty");
+
+ if((argc == 15)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2)
+ &&IS_A_FLOAT(argv,3)
+ &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4))
+ &&(IS_A_SYMBOL(argv,5)||IS_A_FLOAT(argv,5))
+ &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6))
+ &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8)
+ &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,11)
+ &&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13)&&IS_A_FLOAT(argv,14))
+ {
+ a = (int)atom_getintarg(0, argc, argv);
+ chg = (int)atom_getintarg(1, argc, argv);
+ iinit = (int)atom_getintarg(2, argc, argv);
+ num = (int)atom_getintarg(3, argc, argv);
+ if(IS_A_SYMBOL(argv,4))
+ srl[0] = atom_getsymbolarg(4, argc, argv);
+ else if(IS_A_FLOAT(argv,4))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(4, argc, argv));
+ srl[0] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,5))
+ srl[1] = atom_getsymbolarg(5, argc, argv);
+ else if(IS_A_FLOAT(argv,5))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(5, argc, argv));
+ srl[1] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,6))
+ srl[2] = atom_getsymbolarg(6, argc, argv);
+ else if(IS_A_FLOAT(argv,6))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(6, argc, argv));
+ srl[2] = gensym(str);
+ }
+ ldx = (int)atom_getintarg(7, argc, argv);
+ ldy = (int)atom_getintarg(8, argc, argv);
+ ifstyle = (int)atom_getintarg(9, argc, argv);
+ fs = (int)atom_getintarg(10, argc, argv);
+ bflcol[0] = (int)atom_getintarg(11, argc, argv);
+ bflcol[1] = (int)atom_getintarg(12, argc, argv);
+ bflcol[2] = (int)atom_getintarg(13, argc, argv);
+ on = (int)atom_getintarg(14, argc, argv);
+ }
+ x->x_gui.x_draw = (t_iemfunptr)vdial_draw;
+ iinit &= IEM_INIT_ARGS_ALL;
+ ifstyle &= IEM_FSTYLE_FLAGS_ALL;
+ fstyle->x_snd_able = 1;
+ fstyle->x_rcv_able = 1;
+ x->x_gui.x_glist = (t_glist *)canvas_getcurrent();
+ x->x_gui.x_isa = *init;
+ if(!strcmp(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0;
+ if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0;
+ if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica");
+ else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times");
+ else { fstyle->x_font_style = 0;
+ strcpy(x->x_gui.x_font, "courier"); }
+ x->x_gui.x_fsf = *fstyle;
+ x->x_gui.x_unique_num = 0;
+ if(num < 1)
+ num = 1;
+ if(num > IEM_RADIO_MAX)
+ num = IEM_RADIO_MAX;
+ x->x_number = num;
+ if(on < 0)
+ on = 0;
+ if(on >= x->x_number)
+ on = x->x_number - 1;
+ if(x->x_gui.x_isa.x_loadinit)
+ x->x_on = on;
+ else
+ x->x_on = 0;
+ x->x_on_old = x->x_on;
+ x->x_change = (chg==0)?0:1;
+ iemgui_first_dollararg2sym(&x->x_gui, srl);
+ if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]);
+ x->x_gui.x_snd = srl[0];
+ x->x_gui.x_rcv = srl[1];
+ x->x_gui.x_lab = srl[2];
+ x->x_gui.x_ldx = ldx;
+ x->x_gui.x_ldy = ldy;
+ if(fs < 4)
+ fs = 4;
+ x->x_gui.x_fontsize = fs;
+ x->x_gui.x_w = iemgui_clip_size(a);
+ x->x_gui.x_h = x->x_gui.x_w;
+ iemgui_verify_snd_ne_rcv(&x->x_gui);
+ iemgui_all_colfromload(&x->x_gui, bflcol);
+ outlet_new(&x->x_gui.x_obj, &s_list);
+ return (x);
+}
+
+static void vdial_ff(t_vdial *x)
+{
+ if(x->x_gui.x_fsf.x_selected)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ if(x->x_gui.x_fsf.x_rcv_able)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);
+ gfxstub_deleteforkey(x);
+}
+
+void g_vdial_setup(void)
+{
+ vdial_class = class_new(gensym("vdl"), (t_newmethod)vdial_new,
+ (t_method)vdial_ff, sizeof(t_vdial), 0, A_GIMME, 0);
+ class_addbang(vdial_class, vdial_bang);
+ class_addfloat(vdial_class, vdial_float);
+ class_addlist(vdial_class, vdial_list);
+ class_addmethod(vdial_class, (t_method)vdial_click, gensym("click"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(vdial_class, (t_method)vdial_dialog, gensym("dialog"),
+ A_GIMME, 0);
+ class_addmethod(vdial_class, (t_method)vdial_loadbang, gensym("loadbang"), 0);
+ class_addmethod(vdial_class, (t_method)vdial_set, gensym("set"), A_FLOAT, 0);
+ class_addmethod(vdial_class, (t_method)vdial_size, gensym("size"), A_GIMME, 0);
+ class_addmethod(vdial_class, (t_method)vdial_delta, gensym("delta"), A_GIMME, 0);
+ class_addmethod(vdial_class, (t_method)vdial_pos, gensym("pos"), A_GIMME, 0);
+ class_addmethod(vdial_class, (t_method)vdial_color, gensym("color"), A_GIMME, 0);
+ class_addmethod(vdial_class, (t_method)vdial_send, gensym("send"), A_DEFSYM, 0);
+ class_addmethod(vdial_class, (t_method)vdial_receive, gensym("receive"), A_DEFSYM, 0);
+ class_addmethod(vdial_class, (t_method)vdial_label, gensym("label"), A_DEFSYM, 0);
+ class_addmethod(vdial_class, (t_method)vdial_label_pos, gensym("label_pos"), A_GIMME, 0);
+ class_addmethod(vdial_class, (t_method)vdial_label_font, gensym("label_font"), A_GIMME, 0);
+ class_addmethod(vdial_class, (t_method)vdial_init, gensym("init"), A_FLOAT, 0);
+ class_addmethod(vdial_class, (t_method)vdial_number, gensym("number"), A_FLOAT, 0);
+ class_addmethod(vdial_class, (t_method)vdial_single_change, gensym("single_change"), 0);
+ class_addmethod(vdial_class, (t_method)vdial_double_change, gensym("double_change"), 0);
+ if(!iemgui_key_sym)
+ iemgui_key_sym = gensym("#keyname");
+ vdial_widgetbehavior.w_getrectfn = vdial_getrect;
+ vdial_widgetbehavior.w_displacefn = iemgui_displace;
+ vdial_widgetbehavior.w_selectfn = iemgui_select;
+ vdial_widgetbehavior.w_activatefn = NULL;
+ vdial_widgetbehavior.w_deletefn = iemgui_delete;
+ vdial_widgetbehavior.w_visfn = iemgui_vis;
+ vdial_widgetbehavior.w_clickfn = vdial_newclick;
+ vdial_widgetbehavior.w_propertiesfn = vdial_properties;
+ vdial_widgetbehavior.w_savefn = vdial_save;
+ class_setwidget(vdial_class, &vdial_widgetbehavior);
+ class_sethelpsymbol(vdial_class, gensym("vdial"));
+}
diff --git a/pd/src/g_vslider.c b/pd/src/g_vslider.c
new file mode 100644
index 00000000..12cc4213
--- /dev/null
+++ b/pd/src/g_vslider.c
@@ -0,0 +1,688 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */
+/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include "t_tk.h"
+#include "g_all_guis.h"
+#include <math.h>
+
+#ifdef NT
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+
+/* ------------ vsl gui-vertical slider ----------------------- */
+
+t_widgetbehavior vslider_widgetbehavior;
+static t_class *vslider_class;
+
+/* widget helper functions */
+
+static void vslider_draw_update(t_vslider *x, t_glist *glist)
+{
+ if (glist_isvisible(glist))
+ {
+ int r = text_ypix(&x->x_gui.x_obj, glist) + x->x_gui.x_h - (x->x_val + 50)/100;
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+
+ sys_vgui(".x%x.c coords %xKNOB %d %d %d %d\n",
+ glist_getcanvas(glist), x, xpos+1, r,
+ xpos + x->x_gui.x_w, r);
+ }
+}
+
+static void vslider_draw_new(t_vslider *x, t_glist *glist)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ int r = ypos + x->x_gui.x_h - (x->x_val + 50)/100;
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n",
+ canvas, xpos, ypos-2,
+ xpos + x->x_gui.x_w, ypos + x->x_gui.x_h+3,
+ x->x_gui.x_bcol, x);
+ sys_vgui(".x%x.c create line %d %d %d %d -width 3 -fill #%6.6x -tags %xKNOB\n",
+ canvas, xpos+1, r,
+ xpos + x->x_gui.x_w, r, x->x_gui.x_fcol, x);
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n",
+ canvas, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"",
+ x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas,
+ xpos, ypos + x->x_gui.x_h+2,
+ xpos+7, ypos + x->x_gui.x_h+3,
+ x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas,
+ xpos, ypos-2,
+ xpos+7, ypos-1,
+ x, 0);
+}
+
+static void vslider_draw_move(t_vslider *x, t_glist *glist)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ int r = ypos + x->x_gui.x_h - (x->x_val + 50)/100;
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n",
+ canvas, x,
+ xpos, ypos-2,
+ xpos + x->x_gui.x_w, ypos + x->x_gui.x_h+3);
+ sys_vgui(".x%x.c coords %xKNOB %d %d %d %d\n",
+ canvas, x, xpos+1, r,
+ xpos + x->x_gui.x_w, r);
+ sys_vgui(".x%x.c coords %xLABEL %d %d\n",
+ canvas, x, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n",
+ canvas, x, 0,
+ xpos, ypos + x->x_gui.x_h+2,
+ xpos+7, ypos + x->x_gui.x_h+3);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n",
+ canvas, x, 0,
+ xpos, ypos-2,
+ xpos+7, ypos-1);
+}
+
+static void vslider_draw_erase(t_vslider* x,t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c delete %xBASE\n", canvas, x);
+ sys_vgui(".x%x.c delete %xKNOB\n", canvas, x);
+ sys_vgui(".x%x.c delete %xLABEL\n", canvas, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+static void vslider_draw_config(t_vslider* x,t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n",
+ canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"");
+ sys_vgui(".x%x.c itemconfigure %xKNOB -fill #%6.6x\n", canvas,
+ x, x->x_gui.x_fcol);
+ sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas,
+ x, x->x_gui.x_bcol);
+}
+
+static void vslider_draw_io(t_vslider* x,t_glist* glist, int old_snd_rcv_flags)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas,
+ xpos, ypos + x->x_gui.x_h+2,
+ xpos+7, ypos + x->x_gui.x_h+3,
+ x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able)
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas,
+ xpos, ypos-2,
+ xpos+7, ypos-1,
+ x, 0);
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able)
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+}
+
+static void vslider_draw_select(t_vslider *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if(x->x_gui.x_fsf.x_selected)
+ {
+ pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ }
+ else
+ {
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL);
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol);
+ }
+}
+
+void vslider_draw(t_vslider *x, t_glist *glist, int mode)
+{
+ if(mode == IEM_GUI_DRAW_MODE_UPDATE)
+ vslider_draw_update(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_MOVE)
+ vslider_draw_move(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_NEW)
+ vslider_draw_new(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_SELECT)
+ vslider_draw_select(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_ERASE)
+ vslider_draw_erase(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_CONFIG)
+ vslider_draw_config(x, glist);
+ else if(mode >= IEM_GUI_DRAW_MODE_IO)
+ vslider_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO);
+}
+
+/* ------------------------ vsl widgetbehaviour----------------------------- */
+
+
+static void vslider_getrect(t_gobj *z, t_glist *glist,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_vslider* x = (t_vslider*)z;
+
+ *xp1 = text_xpix(&x->x_gui.x_obj, glist);
+ *yp1 = text_ypix(&x->x_gui.x_obj, glist) - 2;
+ *xp2 = *xp1 + x->x_gui.x_w;
+ *yp2 = *yp1 + x->x_gui.x_h + 5;
+}
+
+static void vslider_save(t_gobj *z, t_binbuf *b)
+{
+ t_vslider *x = (t_vslider *)z;
+ int bflcol[3], *ip1, *ip2;
+ t_symbol *srl[3];
+
+ iemgui_save(&x->x_gui, srl, bflcol);
+ ip1 = (int *)(&x->x_gui.x_isa);
+ ip2 = (int *)(&x->x_gui.x_fsf);
+ binbuf_addv(b, "ssiisiiffiisssiiiiiiiii", gensym("#X"),gensym("obj"),
+ (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix,
+ gensym("vsl"), x->x_gui.x_w, x->x_gui.x_h,
+ (float)x->x_min, (float)x->x_max,
+ x->x_lin0_log1, (*ip1)&IEM_INIT_ARGS_ALL,
+ srl[0], srl[1], srl[2],
+ x->x_gui.x_ldx, x->x_gui.x_ldy,
+ (*ip2)&IEM_FSTYLE_FLAGS_ALL, x->x_gui.x_fontsize,
+ bflcol[0], bflcol[1], bflcol[2],
+ x->x_val, x->x_steady);
+ binbuf_addv(b, ";");
+}
+
+void vslider_check_height(t_vslider *x, int h)
+{
+ if(h < IEM_SL_MINSIZE)
+ h = IEM_SL_MINSIZE;
+ x->x_gui.x_h = h;
+ if(x->x_val > (x->x_gui.x_h*100 - 100))
+ {
+ x->x_pos = x->x_gui.x_h*100 - 100;
+ x->x_val = x->x_pos;
+ }
+ if(x->x_lin0_log1)
+ x->x_k = log(x->x_max/x->x_min)/(double)(x->x_gui.x_h - 1);
+ else
+ x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_h - 1);
+}
+
+void vslider_check_minmax(t_vslider *x, double min, double max)
+{
+ if(x->x_lin0_log1)
+ {
+ if((min == 0.0)&&(max == 0.0))
+ max = 1.0;
+ if(max > 0.0)
+ {
+ if(min <= 0.0)
+ min = 0.01*max;
+ }
+ else
+ {
+ if(min > 0.0)
+ max = 0.01*min;
+ }
+ }
+ x->x_min = min;
+ x->x_max = max;
+ if(x->x_min > x->x_max) /* bugfix */
+ x->x_gui.x_isa.x_reverse = 1;
+ else
+ x->x_gui.x_isa.x_reverse = 0;
+ if(x->x_lin0_log1)
+ x->x_k = log(x->x_max/x->x_min)/(double)(x->x_gui.x_h - 1);
+ else
+ x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_h - 1);
+}
+
+static void vslider_properties(t_gobj *z, t_glist *owner)
+{
+ t_vslider *x = (t_vslider *)z;
+ char buf[800];
+ t_symbol *srl[3];
+
+ iemgui_properties(&x->x_gui, srl);
+
+ sprintf(buf, "pdtk_iemgui_dialog %%s VSLIDER \
+ --------dimensions(pix)(pix):-------- %d %d width: %d %d height: \
+ -----------output-range:----------- %g bottom: %g top: %d \
+ %d lin log %d %d empty %d \
+ %s %s \
+ %s %d %d \
+ %d %d \
+ %d %d %d\n",
+ x->x_gui.x_w, IEM_GUI_MINSIZE, x->x_gui.x_h, IEM_SL_MINSIZE,
+ x->x_min, x->x_max, 0,/*no_schedule*/
+ x->x_lin0_log1, x->x_gui.x_isa.x_loadinit, x->x_steady, -1,/*no multi, but iem-characteristic*/
+ srl[0]->s_name, srl[1]->s_name,
+ srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy,
+ x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize,
+ 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol);
+ gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf);
+}
+
+static void vslider_bang(t_vslider *x)
+{
+ double out;
+
+ if(x->x_lin0_log1)
+ out = x->x_min*exp(x->x_k*(double)(x->x_val)*0.01);
+ else
+ out = (double)(x->x_val)*0.01*x->x_k + x->x_min;
+ if((out < 1.0e-10)&&(out > -1.0e-10))
+ out = 0.0;
+
+ outlet_float(x->x_gui.x_obj.ob_outlet, out);
+ if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)
+ pd_float(x->x_gui.x_snd->s_thing, out);
+}
+
+static void vslider_dialog(t_vslider *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *srl[3];
+ int w = (int)atom_getintarg(0, argc, argv);
+ int h = (int)atom_getintarg(1, argc, argv);
+ double min = (double)atom_getfloatarg(2, argc, argv);
+ double max = (double)atom_getfloatarg(3, argc, argv);
+ int lilo = (int)atom_getintarg(4, argc, argv);
+ int steady = (int)atom_getintarg(17, argc, argv);
+ int sr_flags;
+
+ if(lilo != 0) lilo = 1;
+ x->x_lin0_log1 = lilo;
+ if(steady)
+ x->x_steady = 1;
+ else
+ x->x_steady = 0;
+ sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);
+ x->x_gui.x_w = iemgui_clip_size(w);
+ vslider_check_height(x, h);
+ vslider_check_minmax(x, min, max);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+}
+
+static void vslider_motion(t_vslider *x, t_floatarg dx, t_floatarg dy)
+{
+ int old = x->x_val;
+
+ if(x->x_gui.x_fsf.x_finemoved)
+ x->x_pos -= (int)dy;
+ else
+ x->x_pos -= 100*(int)dy;
+ x->x_val = x->x_pos;
+ if(x->x_val > (100*x->x_gui.x_h - 100))
+ {
+ x->x_val = 100*x->x_gui.x_h - 100;
+ x->x_pos += 50;
+ x->x_pos -= x->x_pos%100;
+ }
+ if(x->x_val < 0)
+ {
+ x->x_val = 0;
+ x->x_pos -= 50;
+ x->x_pos -= x->x_pos%100;
+ }
+ if(old != x->x_val)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ vslider_bang(x);
+ }
+}
+
+static void vslider_click(t_vslider *x, t_floatarg xpos, t_floatarg ypos,
+ t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
+{
+ if(!x->x_steady)
+ x->x_val = (int)(100.0 * (x->x_gui.x_h + text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist) - ypos));
+ if(x->x_val > (100*x->x_gui.x_h - 100))
+ x->x_val = 100*x->x_gui.x_h - 100;
+ if(x->x_val < 0)
+ x->x_val = 0;
+ x->x_pos = x->x_val;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ vslider_bang(x);
+ glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.te_g, (t_glistmotionfn)vslider_motion,
+ 0, xpos, ypos);
+}
+
+static int vslider_newclick(t_gobj *z, struct _glist *glist,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ t_vslider* x = (t_vslider *)z;
+
+ if(doit)
+ {
+ vslider_click( x, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift,
+ 0, (t_floatarg)alt);
+ if(shift)
+ x->x_gui.x_fsf.x_finemoved = 1;
+ else
+ x->x_gui.x_fsf.x_finemoved = 0;
+ }
+ return (1);
+}
+
+static void vslider_set(t_vslider *x, t_floatarg f)
+{
+ double g;
+
+ if(x->x_gui.x_isa.x_reverse) /* bugfix */
+ {
+ if(f > x->x_min)
+ f = x->x_min;
+ if(f < x->x_max)
+ f = x->x_max;
+ }
+ else
+ {
+ if(f > x->x_max)
+ f = x->x_max;
+ if(f < x->x_min)
+ f = x->x_min;
+ }
+ if(x->x_lin0_log1)
+ g = log(f/x->x_min)/x->x_k;
+ else
+ g = (f - x->x_min) / x->x_k;
+ x->x_val = (int)(100.0*g + 0.49999);
+ x->x_pos = x->x_val;
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+}
+
+static void vslider_float(t_vslider *x, t_floatarg f)
+{
+ vslider_set(x, f);
+ if(x->x_gui.x_fsf.x_put_in2out)
+ vslider_bang(x);
+}
+
+static void vslider_size(t_vslider *x, t_symbol *s, int ac, t_atom *av)
+{
+ x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av));
+ if(ac > 1)
+ vslider_check_height(x, (int)atom_getintarg(1, ac, av));
+ iemgui_size((void *)x, &x->x_gui);
+}
+
+static void vslider_delta(t_vslider *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}
+
+static void vslider_pos(t_vslider *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void vslider_range(t_vslider *x, t_symbol *s, int ac, t_atom *av)
+{
+ vslider_check_minmax(x, (double)atom_getfloatarg(0, ac, av),
+ (double)atom_getfloatarg(1, ac, av));
+}
+
+static void vslider_color(t_vslider *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_color((void *)x, &x->x_gui, s, ac, av);}
+
+static void vslider_send(t_vslider *x, t_symbol *s)
+{iemgui_send(x, &x->x_gui, s);}
+
+static void vslider_receive(t_vslider *x, t_symbol *s)
+{iemgui_receive(x, &x->x_gui, s);}
+
+static void vslider_label(t_vslider *x, t_symbol *s)
+{iemgui_label((void *)x, &x->x_gui, s);}
+
+static void vslider_label_pos(t_vslider *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void vslider_label_font(t_vslider *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);}
+
+static void vslider_log(t_vslider *x)
+{
+ x->x_lin0_log1 = 1;
+ vslider_check_minmax(x, x->x_min, x->x_max);
+}
+
+static void vslider_lin(t_vslider *x)
+{
+ x->x_lin0_log1 = 0;
+ x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_h - 1);
+}
+
+static void vslider_init(t_vslider *x, t_floatarg f)
+{
+ x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1;
+}
+
+static void vslider_steady(t_vslider *x, t_floatarg f)
+{
+ x->x_steady = (f==0.0)?0:1;
+}
+
+static void vslider_loadbang(t_vslider *x)
+{
+ if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);
+ vslider_bang(x);
+ }
+}
+
+static void vslider_list(t_vslider *x, t_symbol *s, int ac, t_atom *av)
+{
+ int l=iemgui_list((void *)x, &x->x_gui, s, ac, av);
+
+ if(l < 0)
+ {
+ if(IS_A_FLOAT(av,0))
+ vslider_float(x, atom_getfloatarg(0, ac, av));
+ }
+ else if(l > 0)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+ }
+}
+
+static void *vslider_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_vslider *x = (t_vslider *)pd_new(vslider_class);
+ int bflcol[]={-262144, -1, -1};
+ t_symbol *srl[3];
+ int w=IEM_GUI_DEFAULTSIZE, h=IEM_SL_DEFAULTSIZE;
+ int lilo=0, f=0, ldx=0, ldy=-8;
+ int fs=8, iinit=0, ifstyle=0, v=0, steady=1;
+ double min=0.0, max=(double)(IEM_SL_DEFAULTSIZE-1);
+ t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit);
+ t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle);
+ char str[144];
+
+ srl[0] = gensym("empty");
+ srl[1] = gensym("empty");
+ srl[2] = gensym("empty");
+
+
+ if(((argc == 17)||(argc == 18))&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)
+ &&IS_A_FLOAT(argv,2)&&IS_A_FLOAT(argv,3)
+ &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5)
+ &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6))
+ &&(IS_A_SYMBOL(argv,7)||IS_A_FLOAT(argv,7))
+ &&(IS_A_SYMBOL(argv,8)||IS_A_FLOAT(argv,8))
+ &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)
+ &&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13)
+ &&IS_A_FLOAT(argv,14)&&IS_A_FLOAT(argv,15)&&IS_A_FLOAT(argv,16))
+ {
+ w = (int)atom_getintarg(0, argc, argv);
+ h = (int)atom_getintarg(1, argc, argv);
+ min = (double)atom_getfloatarg(2, argc, argv);
+ max = (double)atom_getfloatarg(3, argc, argv);
+ lilo = (int)atom_getintarg(4, argc, argv);
+ iinit = (int)atom_getintarg(5, argc, argv);
+ srl[0] = atom_getsymbolarg(6, argc, argv);
+ srl[1] = atom_getsymbolarg(7, argc, argv);
+ srl[2] = atom_getsymbolarg(8, argc, argv);
+ if(IS_A_SYMBOL(argv,6))
+ srl[0] = atom_getsymbolarg(6, argc, argv);
+ else if(IS_A_FLOAT(argv,6))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(6, argc, argv));
+ srl[0] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,7))
+ srl[1] = atom_getsymbolarg(7, argc, argv);
+ else if(IS_A_FLOAT(argv,7))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(7, argc, argv));
+ srl[1] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,8))
+ srl[2] = atom_getsymbolarg(8, argc, argv);
+ else if(IS_A_FLOAT(argv,8))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(8, argc, argv));
+ srl[2] = gensym(str);
+ }
+ ldx = (int)atom_getintarg(9, argc, argv);
+ ldy = (int)atom_getintarg(10, argc, argv);
+ ifstyle = (int)atom_getintarg(11, argc, argv);
+ fs = (int)atom_getintarg(12, argc, argv);
+ bflcol[0] = (int)atom_getintarg(13, argc, argv);
+ bflcol[1] = (int)atom_getintarg(14, argc, argv);
+ bflcol[2] = (int)atom_getintarg(15, argc, argv);
+ v = (int)atom_getintarg(16, argc, argv);
+ }
+ if((argc == 18)&&IS_A_FLOAT(argv,17))
+ steady = (int)atom_getintarg(17, argc, argv);
+ x->x_gui.x_draw = (t_iemfunptr)vslider_draw;
+ iinit &= IEM_INIT_ARGS_ALL;
+ ifstyle &= IEM_FSTYLE_FLAGS_ALL;
+ fstyle->x_snd_able = 1;
+ fstyle->x_rcv_able = 1;
+ x->x_gui.x_glist = (t_glist *)canvas_getcurrent();
+ x->x_gui.x_isa = *init;
+ if(x->x_gui.x_isa.x_loadinit)
+ x->x_val = v;
+ else
+ x->x_val = 0;
+ x->x_pos = x->x_val;
+ if(lilo != 0) lilo = 1;
+ x->x_lin0_log1 = lilo;
+ if(steady != 0) steady = 1;
+ x->x_steady = steady;
+ if(!strcmp(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0;
+ if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0;
+ x->x_gui.x_unique_num = 0;
+ if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica");
+ else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times");
+ else { fstyle->x_font_style = 0;
+ strcpy(x->x_gui.x_font, "courier"); }
+ x->x_gui.x_fsf = *fstyle;
+ iemgui_first_dollararg2sym(&x->x_gui, srl);
+ if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]);
+ x->x_gui.x_snd = srl[0];
+ x->x_gui.x_rcv = srl[1];
+ x->x_gui.x_lab = srl[2];
+ x->x_gui.x_ldx = ldx;
+ x->x_gui.x_ldy = ldy;
+ if(fs < 4)
+ fs = 4;
+ x->x_gui.x_fontsize = fs;
+ x->x_gui.x_w = iemgui_clip_size(w);
+ vslider_check_height(x, h);
+ vslider_check_minmax(x, min, max);
+ iemgui_all_colfromload(&x->x_gui, bflcol);
+ iemgui_verify_snd_ne_rcv(&x->x_gui);
+ outlet_new(&x->x_gui.x_obj, &s_float);
+ return (x);
+}
+
+static void vslider_free(t_vslider *x)
+{
+ if(x->x_gui.x_fsf.x_selected)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ if(x->x_gui.x_fsf.x_rcv_able)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);
+ gfxstub_deleteforkey(x);
+}
+
+void g_vslider_setup(void)
+{
+ vslider_class = class_new(gensym("vsl"), (t_newmethod)vslider_new,
+ (t_method)vslider_free, sizeof(t_vslider), 0, A_GIMME, 0);
+ class_addcreator((t_newmethod)vslider_new, gensym("vslider"), A_GIMME, 0);
+ class_addbang(vslider_class,vslider_bang);
+ class_addfloat(vslider_class,vslider_float);
+ class_addlist(vslider_class, vslider_list);
+ class_addmethod(vslider_class, (t_method)vslider_click, gensym("click"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(vslider_class, (t_method)vslider_motion, gensym("motion"),
+ A_FLOAT, A_FLOAT, 0);
+ class_addmethod(vslider_class, (t_method)vslider_dialog, gensym("dialog"),
+ A_GIMME, 0);
+ class_addmethod(vslider_class, (t_method)vslider_loadbang, gensym("loadbang"), 0);
+ class_addmethod(vslider_class, (t_method)vslider_set, gensym("set"), A_FLOAT, 0);
+ class_addmethod(vslider_class, (t_method)vslider_size, gensym("size"), A_GIMME, 0);
+ class_addmethod(vslider_class, (t_method)vslider_delta, gensym("delta"), A_GIMME, 0);
+ class_addmethod(vslider_class, (t_method)vslider_pos, gensym("pos"), A_GIMME, 0);
+ class_addmethod(vslider_class, (t_method)vslider_range, gensym("range"), A_GIMME, 0);
+ class_addmethod(vslider_class, (t_method)vslider_color, gensym("color"), A_GIMME, 0);
+ class_addmethod(vslider_class, (t_method)vslider_send, gensym("send"), A_DEFSYM, 0);
+ class_addmethod(vslider_class, (t_method)vslider_receive, gensym("receive"), A_DEFSYM, 0);
+ class_addmethod(vslider_class, (t_method)vslider_label, gensym("label"), A_DEFSYM, 0);
+ class_addmethod(vslider_class, (t_method)vslider_label_pos, gensym("label_pos"), A_GIMME, 0);
+ class_addmethod(vslider_class, (t_method)vslider_label_font, gensym("label_font"), A_GIMME, 0);
+ class_addmethod(vslider_class, (t_method)vslider_log, gensym("log"), 0);
+ class_addmethod(vslider_class, (t_method)vslider_lin, gensym("lin"), 0);
+ class_addmethod(vslider_class, (t_method)vslider_init, gensym("init"), A_FLOAT, 0);
+ class_addmethod(vslider_class, (t_method)vslider_steady, gensym("steady"), A_FLOAT, 0);
+ if(!iemgui_key_sym)
+ iemgui_key_sym = gensym("#keyname");
+ vslider_widgetbehavior.w_getrectfn = vslider_getrect;
+ vslider_widgetbehavior.w_displacefn = iemgui_displace;
+ vslider_widgetbehavior.w_selectfn = iemgui_select;
+ vslider_widgetbehavior.w_activatefn = NULL;
+ vslider_widgetbehavior.w_deletefn = iemgui_delete;
+ vslider_widgetbehavior.w_visfn = iemgui_vis;
+ vslider_widgetbehavior.w_clickfn = vslider_newclick;
+ vslider_widgetbehavior.w_propertiesfn = vslider_properties;;
+ vslider_widgetbehavior.w_savefn = vslider_save;
+ class_setwidget(vslider_class, &vslider_widgetbehavior);
+ class_sethelpsymbol(vslider_class, gensym("vslider"));
+}
diff --git a/pd/src/g_vumeter.c b/pd/src/g_vumeter.c
new file mode 100644
index 00000000..b257c588
--- /dev/null
+++ b/pd/src/g_vumeter.c
@@ -0,0 +1,762 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */
+/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include "t_tk.h"
+#include "g_all_guis.h"
+#include <math.h>
+
+#ifdef NT
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+/* ----- vu gui-peak- & rms- vu-meter-display ---------- */
+
+t_widgetbehavior vu_widgetbehavior;
+static t_class *vu_class;
+
+/* widget helper functions */
+
+static void vu_update_rms(t_vu *x, t_glist *glist)
+{
+ if(glist_isvisible(glist))
+ {
+ int w4=x->x_gui.x_w/4, off=text_ypix(&x->x_gui.x_obj, glist)-1;
+ int xpos=text_xpix(&x->x_gui.x_obj, glist), quad1=xpos+w4+1, quad3=xpos+x->x_gui.x_w-w4-1;
+
+ sys_vgui(".x%x.c coords %xRCOVER %d %d %d %d\n",
+ glist_getcanvas(glist), x, quad1, off, quad3,
+ off + (x->x_led_size+1)*(IEM_VU_STEPS-x->x_rms));
+ }
+}
+
+static void vu_update_peak(t_vu *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if(glist_isvisible(glist))
+ {
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+
+ if(x->x_peak)
+ {
+ int i=iemgui_vu_col[x->x_peak];
+ int j=ypos + (x->x_led_size+1)*(IEM_VU_STEPS+1-x->x_peak)
+ - (x->x_led_size+1)/2;
+
+ sys_vgui(".x%x.c coords %xPLED %d %d %d %d\n", canvas, x,
+ xpos, j,
+ xpos+x->x_gui.x_w+1, j);
+ sys_vgui(".x%x.c itemconfigure %xPLED -fill #%6.6x\n", canvas, x,
+ iemgui_color_hex[i]);
+ }
+ else
+ {
+ int mid=xpos+x->x_gui.x_w/2;
+
+ sys_vgui(".x%x.c itemconfigure %xPLED -fill #%6.6x\n",
+ canvas, x, x->x_gui.x_bcol);
+ sys_vgui(".x%x.c coords %xPLED %d %d %d %d\n",
+ canvas, x, mid, ypos+20,
+ mid, ypos+20);
+ }
+ }
+}
+
+static void vu_draw_new(t_vu *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ int w4=x->x_gui.x_w/4, mid=xpos+x->x_gui.x_w/2,
+ quad1=xpos+w4+1;
+ int quad3=xpos+x->x_gui.x_w-w4,
+ end=xpos+x->x_gui.x_w+4;
+ int k1=x->x_led_size+1, k2=IEM_VU_STEPS+1, k3=k1/2;
+ int led_col, yyy, i, k4=ypos-k3;
+
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n",
+ canvas, xpos-1, ypos-2,
+ xpos+x->x_gui.x_w+1,
+ ypos+x->x_gui.x_h+2, x->x_gui.x_bcol, x);
+ for(i=1; i<=IEM_VU_STEPS; i++)
+ {
+ led_col = iemgui_vu_col[i];
+ yyy = k4 + k1*(k2-i);
+ sys_vgui(".x%x.c create line %d %d %d %d -width %d -fill #%6.6x -tags %xRLED%d\n",
+ canvas, quad1, yyy, quad3, yyy, x->x_led_size, iemgui_color_hex[led_col], x, i);
+ if(((i+2)&3) && (x->x_scale))
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xSCALE%d\n",
+ canvas, end, yyy+k3, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_lcol, x, i);
+ }
+ if(x->x_scale)
+ {
+ i=IEM_VU_STEPS+1;
+ yyy = k4 + k1*(k2-i);
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xSCALE%d\n",
+ canvas, end, yyy+k3, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_lcol, x, i);
+ }
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -outline #%6.6x -tags %xRCOVER\n",
+ canvas, quad1, ypos-1, quad3-1,
+ ypos-1 + k1*IEM_VU_STEPS, x->x_gui.x_bcol, x->x_gui.x_bcol, x);
+ sys_vgui(".x%x.c create line %d %d %d %d -width %d -fill #%6.6x -tags %xPLED\n",
+ canvas, mid, ypos+10,
+ mid, ypos+10, x->x_led_size, x->x_gui.x_bcol, x);
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n",
+ canvas, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"",
+ x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ {
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas,
+ xpos-1, ypos + x->x_gui.x_h+1,
+ xpos + IOWIDTH-1, ypos + x->x_gui.x_h+2,
+ x, 0);
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas,
+ xpos+x->x_gui.x_w+1-IOWIDTH, ypos + x->x_gui.x_h+1,
+ xpos+x->x_gui.x_w+1, ypos + x->x_gui.x_h+2,
+ x, 1);
+ }
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ {
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas,
+ xpos-1, ypos-2,
+ xpos + IOWIDTH-1, ypos-1,
+ x, 0);
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas,
+ xpos+x->x_gui.x_w+1-IOWIDTH, ypos-2,
+ xpos+x->x_gui.x_w+1, ypos-1,
+ x, 1);
+ }
+}
+
+
+static void vu_draw_move(t_vu *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ int w4=x->x_gui.x_w/4, quad1=xpos+w4+1;
+ int quad3=xpos+x->x_gui.x_w-w4,
+ end=xpos+x->x_gui.x_w+4;
+ int k1=x->x_led_size+1, k2=IEM_VU_STEPS+1, k3=k1/2;
+ int yyy, i, k4=ypos-k3;
+
+ sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n",
+ canvas, x, xpos-1, ypos-2,
+ xpos+x->x_gui.x_w+1,ypos+x->x_gui.x_h+2);
+ for(i=1; i<=IEM_VU_STEPS; i++)
+ {
+ yyy = k4 + k1*(k2-i);
+ sys_vgui(".x%x.c coords %xRLED%d %d %d %d %d\n",
+ canvas, x, i, quad1, yyy, quad3, yyy);
+ if(((i+2)&3) && (x->x_scale))
+ sys_vgui(".x%x.c coords %xSCALE%d %d %d\n",
+ canvas, x, i, end, yyy+k3);
+ }
+ if(x->x_scale)
+ {
+ i=IEM_VU_STEPS+1;
+ yyy = k4 + k1*(k2-i);
+ sys_vgui(".x%x.c coords %xSCALE%d %d %d\n",
+ canvas, x, i, end, yyy+k3);
+ }
+ vu_update_peak(x, glist);
+ vu_update_rms(x, glist);
+ sys_vgui(".x%x.c coords %xLABEL %d %d\n",
+ canvas, x, xpos+x->x_gui.x_ldx,
+ ypos+x->x_gui.x_ldy);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ {
+ sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n",
+ canvas, x, 0,
+ xpos-1, ypos + x->x_gui.x_h+1,
+ xpos + IOWIDTH-1, ypos + x->x_gui.x_h+2);
+ sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n",
+ canvas, x, 1,
+ xpos+x->x_gui.x_w+1-IOWIDTH, ypos + x->x_gui.x_h+1,
+ xpos+x->x_gui.x_w+1, ypos + x->x_gui.x_h+2);
+ }
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ {
+ sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n",
+ canvas, x, 0,
+ xpos-1, ypos-2,
+ xpos + IOWIDTH-1, ypos-1);
+ sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n",
+ canvas, x, 1,
+ xpos+x->x_gui.x_w+1-IOWIDTH, ypos-2,
+ xpos+x->x_gui.x_w+1, ypos-1);
+ }
+}
+
+static void vu_draw_erase(t_vu* x,t_glist* glist)
+{
+ int i;
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c delete %xBASE\n", canvas, x);
+ for(i=1; i<=IEM_VU_STEPS; i++)
+ {
+ sys_vgui(".x%x.c delete %xRLED%d\n", canvas, x, i);
+ if(((i+2)&3) && (x->x_scale))
+ sys_vgui(".x%x.c delete %xSCALE%d\n", canvas, x, i);
+ }
+ if(x->x_scale)
+ {
+ i=IEM_VU_STEPS+1;
+ sys_vgui(".x%x.c delete %xSCALE%d\n", canvas, x, i);
+ }
+ sys_vgui(".x%x.c delete %xPLED\n", canvas, x);
+ sys_vgui(".x%x.c delete %xRCOVER\n", canvas, x);
+ sys_vgui(".x%x.c delete %xLABEL\n", canvas, x);
+ if(!x->x_gui.x_fsf.x_snd_able)
+ {
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 1);
+ }
+ if(!x->x_gui.x_fsf.x_rcv_able)
+ {
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 1);
+ }
+}
+
+static void vu_draw_config(t_vu* x, t_glist* glist)
+{
+ int i;
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, x, x->x_gui.x_bcol);
+ for(i=1; i<=IEM_VU_STEPS; i++)
+ {
+ sys_vgui(".x%x.c itemconfigure %xRLED%d -width %d\n", canvas, x, i,
+ x->x_led_size);
+ if(((i+2)&3) && (x->x_scale))
+ sys_vgui(".x%x.c itemconfigure %xSCALE%d -text {%s} -font {%s %d bold} -fill #%6.6x\n",
+ canvas, x, i, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol);
+ }
+ if(x->x_scale)
+ {
+ i=IEM_VU_STEPS+1;
+ sys_vgui(".x%x.c itemconfigure %xSCALE%d -text {%s} -font {%s %d bold} -fill #%6.6x\n",
+ canvas, x, i, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol);
+ }
+ sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n",
+ canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol,
+ strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"");
+
+ sys_vgui(".x%x.c itemconfigure %xRCOVER -fill #%6.6x -outline #%6.6x\n", canvas,
+ x, x->x_gui.x_bcol, x->x_gui.x_bcol);
+ sys_vgui(".x%x.c itemconfigure %xPLED -width %d\n", canvas, x,
+ x->x_led_size);
+}
+
+static void vu_draw_io(t_vu* x, t_glist* glist, int old_snd_rcv_flags)
+{
+ int xpos=text_xpix(&x->x_gui.x_obj, glist);
+ int ypos=text_ypix(&x->x_gui.x_obj, glist);
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able)
+ {
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas,
+ xpos-1, ypos + x->x_gui.x_h+1,
+ xpos + IOWIDTH-1, ypos + x->x_gui.x_h+2,
+ x, 0);
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n",
+ canvas,
+ xpos+x->x_gui.x_w+1-IOWIDTH, ypos + x->x_gui.x_h+1,
+ xpos+x->x_gui.x_w+1, ypos + x->x_gui.x_h+2,
+ x, 1);
+ }
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able)
+ {
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0);
+ sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 1);
+ }
+ if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able)
+ {
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas,
+ xpos-1, ypos-2,
+ xpos + IOWIDTH-1, ypos-1,
+ x, 0);
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n",
+ canvas,
+ xpos+x->x_gui.x_w+1-IOWIDTH, ypos-2,
+ xpos+x->x_gui.x_w+1, ypos-1,
+ x, 1);
+ }
+ if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able)
+ {
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0);
+ sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 1);
+ }
+}
+
+static void vu_draw_select(t_vu* x,t_glist* glist)
+{
+ int i;
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ if(x->x_gui.x_fsf.x_selected)
+ {
+ pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ for(i=1; i<=IEM_VU_STEPS; i++)
+ {
+ if(((i+2)&3) && (x->x_scale))
+ sys_vgui(".x%x.c itemconfigure %xSCALE%d -fill #%6.6x\n",
+ canvas, x, i, IEM_GUI_COLOR_SELECTED);
+ }
+ if(x->x_scale)
+ {
+ i=IEM_VU_STEPS+1;
+ sys_vgui(".x%x.c itemconfigure %xSCALE%d -fill #%6.6x\n",
+ canvas, x, i, IEM_GUI_COLOR_SELECTED);
+ }
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED);
+ }
+ else
+ {
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL);
+ for(i=1; i<=IEM_VU_STEPS; i++)
+ {
+ if(((i+2)&3) && (x->x_scale))
+ sys_vgui(".x%x.c itemconfigure %xSCALE%d -fill #%6.6x\n",
+ canvas, x, i, x->x_gui.x_lcol);
+ }
+ if(x->x_scale)
+ {
+ i=IEM_VU_STEPS+1;
+ sys_vgui(".x%x.c itemconfigure %xSCALE%d -fill #%6.6x\n",
+ canvas, x, i, x->x_gui.x_lcol);
+ }
+ sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol);
+ }
+}
+
+void vu_draw(t_vu *x, t_glist *glist, int mode)
+{
+ if(mode == IEM_GUI_DRAW_MODE_MOVE)
+ vu_draw_move(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_NEW)
+ vu_draw_new(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_SELECT)
+ vu_draw_select(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_ERASE)
+ vu_draw_erase(x, glist);
+ else if(mode == IEM_GUI_DRAW_MODE_CONFIG)
+ vu_draw_config(x, glist);
+ else if(mode >= IEM_GUI_DRAW_MODE_IO)
+ vu_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO);
+}
+
+/* ------------------------ vu widgetbehaviour----------------------------- */
+
+
+static void vu_getrect(t_gobj *z, t_glist *glist,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_vu* x = (t_vu*)z;
+
+ *xp1 = text_xpix(&x->x_gui.x_obj, glist) - 1;
+ *yp1 = text_ypix(&x->x_gui.x_obj, glist) - 2;
+ *xp2 = *xp1 + x->x_gui.x_w + 2;
+ *yp2 = *yp1 + x->x_gui.x_h + 4;
+}
+
+static void vu_save(t_gobj *z, t_binbuf *b)
+{
+ t_vu *x = (t_vu *)z;
+ int bflcol[3], *ip1, *ip2;
+ t_symbol *srl[3];
+
+ iemgui_save(&x->x_gui, srl, bflcol);
+ ip1 = (int *)(&x->x_gui.x_isa);
+ ip2 = (int *)(&x->x_gui.x_fsf);
+ binbuf_addv(b, "ssiisiissiiiiiiii", gensym("#X"),gensym("obj"),
+ (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix,
+ gensym("vu"), x->x_gui.x_w, x->x_gui.x_h,
+ srl[1], srl[2],
+ x->x_gui.x_ldx, x->x_gui.x_ldy,
+ (*ip2)&IEM_FSTYLE_FLAGS_ALL, x->x_gui.x_fontsize,
+ bflcol[0], bflcol[2], x->x_scale, (*ip1)&IEM_INIT_ARGS_ALL);
+ binbuf_addv(b, ";");
+}
+
+void vu_check_height(t_vu *x, int h)
+{
+ int n;
+
+ n = h / IEM_VU_STEPS;
+ if(n < IEM_VU_MINSIZE)
+ n = IEM_VU_MINSIZE;
+ x->x_led_size = n-1;
+ x->x_gui.x_h = IEM_VU_STEPS * n;
+}
+
+static void vu_scale(t_vu *x, t_floatarg fscale)
+{
+ int i, scale = (int)fscale;
+
+ if(scale != 0) scale = 1;
+ if(x->x_scale && !scale)
+ {
+ t_canvas *canvas=glist_getcanvas(x->x_gui.x_glist);
+
+ x->x_scale = (int)scale;
+ if(glist_isvisible(x->x_gui.x_glist))
+ {
+ for(i=1; i<=IEM_VU_STEPS; i++)
+ {
+ if((i+2)&3)
+ sys_vgui(".x%x.c delete %xSCALE%d\n", canvas, x, i);
+ }
+ i=IEM_VU_STEPS+1;
+ sys_vgui(".x%x.c delete %xSCALE%d\n", canvas, x, i);
+ }
+ }
+ if(!x->x_scale && scale)
+ {
+ int w4=x->x_gui.x_w/4, end=text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist)+x->x_gui.x_w+4;
+ int k1=x->x_led_size+1, k2=IEM_VU_STEPS+1, k3=k1/2;
+ int yyy, k4=text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist)-k3;
+ t_canvas *canvas=glist_getcanvas(x->x_gui.x_glist);
+
+ x->x_scale = (int)scale;
+ if(glist_isvisible(x->x_gui.x_glist))
+ {
+ for(i=1; i<=IEM_VU_STEPS; i++)
+ {
+ yyy = k4 + k1*(k2-i);
+ if((i+2)&3)
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xSCALE%d\n",
+ canvas, end, yyy+k3, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_lcol, x, i);
+ }
+ i=IEM_VU_STEPS+1;
+ yyy = k4 + k1*(k2-i);
+ sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \
+ -font {%s %d bold} -fill #%6.6x -tags %xSCALE%d\n",
+ canvas, end, yyy+k3, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize,
+ x->x_gui.x_lcol, x, i);
+ }
+ }
+}
+
+static void vu_properties(t_gobj *z, t_glist *owner)
+{
+ t_vu *x = (t_vu *)z;
+ char buf[800];
+ t_symbol *srl[3];
+
+ iemgui_properties(&x->x_gui, srl);
+ sprintf(buf, "pdtk_iemgui_dialog %%s VU-METER \
+ --------dimensions(pix)(pix):-------- %d %d width: %d %d height: \
+ empty 0.0 empty 0.0 empty %d \
+ %d no_scale scale %d %d empty %d \
+ %s %s \
+ %s %d %d \
+ %d %d \
+ %d %d %d\n",
+ x->x_gui.x_w, IEM_GUI_MINSIZE, x->x_gui.x_h, IEM_VU_STEPS*IEM_VU_MINSIZE,
+ 0,/*no_schedule*/
+ x->x_scale, -1, -1, -1,/*no linlog, no init, no multi*/
+ "nosndno", srl[1]->s_name,/*no send*/
+ srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy,
+ x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize,
+ 0xffffff & x->x_gui.x_bcol, -1/*no front-color*/, 0xffffff & x->x_gui.x_lcol);
+ gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf);
+}
+
+static void vu_dialog(t_vu *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *srl[3];
+ int w = (int)atom_getintarg(0, argc, argv);
+ int h = (int)atom_getintarg(1, argc, argv);
+ int scale = (int)atom_getintarg(4, argc, argv);
+ int sr_flags;
+
+ srl[0] = gensym("empty");
+ sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);
+ x->x_gui.x_fsf.x_snd_able = 0;
+ x->x_gui.x_isa.x_loadinit = 0;
+ x->x_gui.x_w = iemgui_clip_size(w);
+ vu_check_height(x, h);
+ if(scale != 0)
+ scale = 1;
+ vu_scale(x, (float)scale);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+}
+
+static void vu_size(t_vu *x, t_symbol *s, int ac, t_atom *av)
+{
+ x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av));
+ if(ac > 1)
+ vu_check_height(x, (int)atom_getintarg(1, ac, av));
+ if(glist_isvisible(x->x_gui.x_glist))
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+ }
+}
+
+static void vu_delta(t_vu *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}
+
+static void vu_pos(t_vu *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void vu_color(t_vu *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_color((void *)x, &x->x_gui, s, ac, av);}
+
+static void vu_receive(t_vu *x, t_symbol *s)
+{iemgui_receive(x, &x->x_gui, s);}
+
+static void vu_label(t_vu *x, t_symbol *s)
+{iemgui_label((void *)x, &x->x_gui, s);}
+
+static void vu_label_pos(t_vu *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}
+
+static void vu_label_font(t_vu *x, t_symbol *s, int ac, t_atom *av)
+{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);}
+
+static void vu_float(t_vu *x, t_floatarg rms)
+{
+ int i;
+
+ if(rms <= IEM_VU_MINDB)
+ x->x_rms = 0;
+ else if(rms >= IEM_VU_MAXDB)
+ x->x_rms = IEM_VU_STEPS;
+ else
+ {
+ int i = (int)(2.0*(rms + IEM_VU_OFFSET));
+ x->x_rms = iemgui_vu_db2i[i];
+ }
+ i = (int)(100.0*rms + 10000.5);
+ rms = 0.01*(float)(i - 10000);
+ x->x_fr = rms;
+ outlet_float(x->x_out_rms, rms);
+ vu_update_rms(x, x->x_gui.x_glist);
+}
+
+static void vu_ft1(t_vu *x, t_floatarg peak)
+{
+ int i;
+
+ if(peak <= IEM_VU_MINDB)
+ x->x_peak = 0;
+ else if(peak >= IEM_VU_MAXDB)
+ x->x_peak = IEM_VU_STEPS;
+ else
+ {
+ int i = (int)(2.0*(peak + IEM_VU_OFFSET));
+ x->x_peak = iemgui_vu_db2i[i];
+ }
+ i = (int)(100.0*peak + 10000.5);
+ peak = 0.01*(float)(i - 10000);
+ x->x_fp = peak;
+ outlet_float(x->x_out_peak, peak);
+ vu_update_peak(x, x->x_gui.x_glist);
+}
+
+static void vu_list(t_vu *x, t_symbol *s, int ac, t_atom *av)
+{
+ int l=iemgui_list((void *)x, &x->x_gui, s, ac, av);
+
+ if(l < 0)
+ {
+ if((IS_A_FLOAT(av,0))&&(IS_A_FLOAT(av,1)))
+ {
+ vu_ft1(x, atom_getfloatarg(1, ac, av));
+ vu_float(x, atom_getfloatarg(0, ac, av));
+ }
+ }
+ else if(l > 0)
+ {
+ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE);
+ canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x);
+ }
+}
+
+static void vu_bang(t_vu *x)
+{
+ outlet_float(x->x_out_peak, x->x_fp);
+ outlet_float(x->x_out_rms, x->x_fr);
+ vu_update_rms(x, x->x_gui.x_glist);
+ vu_update_peak(x, x->x_gui.x_glist);
+}
+
+static void *vu_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_vu *x = (t_vu *)pd_new(vu_class);
+ int bflcol[]={-66577, -1, -1};
+ t_symbol *srl[3];
+ int w=IEM_GUI_DEFAULTSIZE, h=IEM_VU_STEPS*IEM_VU_DEFAULTSIZE;
+ int ldx=-1, ldy=-8, f=0, fs=8, scale=1;
+ int iinit=0, ifstyle=0;
+ int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME;
+ t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit);
+ t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle);
+ char str[144];
+
+ srl[0] = gensym("empty");
+ srl[1] = gensym("empty");
+ srl[2] = gensym("empty");
+
+ if((argc >= 11)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)
+ &&(IS_A_SYMBOL(argv,2)||IS_A_FLOAT(argv,2))
+ &&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3))
+ &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5)
+ &&IS_A_FLOAT(argv,6)&&IS_A_FLOAT(argv,7)
+ &&IS_A_FLOAT(argv,8)&&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10))
+ {
+ w = (int)atom_getintarg(0, argc, argv);
+ h = (int)atom_getintarg(1, argc, argv);
+ if(IS_A_SYMBOL(argv,2))
+ srl[1] = atom_getsymbolarg(2, argc, argv);
+ else if(IS_A_FLOAT(argv,2))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(2, argc, argv));
+ srl[1] = gensym(str);
+ }
+ if(IS_A_SYMBOL(argv,3))
+ srl[2] = atom_getsymbolarg(3, argc, argv);
+ else if(IS_A_FLOAT(argv,3))
+ {
+ sprintf(str, "%d", (int)atom_getintarg(3, argc, argv));
+ srl[2] = gensym(str);
+ }
+ ldx = (int)atom_getintarg(4, argc, argv);
+ ldy = (int)atom_getintarg(5, argc, argv);
+ ifstyle = (int)atom_getintarg(6, argc, argv);
+ fs = (int)atom_getintarg(7, argc, argv);
+ bflcol[0] = (int)atom_getintarg(8, argc, argv);
+ bflcol[2] = (int)atom_getintarg(9, argc, argv);
+ scale = (int)atom_getintarg(10, argc, argv);
+ }
+ if((argc == 12)&&IS_A_FLOAT(argv,11))
+ iinit = (int)atom_getintarg(11, argc, argv);
+ x->x_gui.x_draw = (t_iemfunptr)vu_draw;
+ iinit &= IEM_INIT_ARGS_ALL;
+ ifstyle &= IEM_FSTYLE_FLAGS_ALL;
+
+ fstyle->x_snd_able = 0;
+ fstyle->x_rcv_able = 1;
+ x->x_gui.x_glist = (t_glist *)canvas_getcurrent();
+ x->x_gui.x_isa = *init;
+ if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0;
+ x->x_gui.x_unique_num = 0;
+ if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica");
+ else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times");
+ else { fstyle->x_font_style = 0;
+ strcpy(x->x_gui.x_font, "courier"); }
+ x->x_gui.x_fsf = *fstyle;
+ iemgui_first_dollararg2sym(&x->x_gui, srl);
+ if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]);
+ x->x_gui.x_snd = srl[0];
+ x->x_gui.x_rcv = srl[1];
+ x->x_gui.x_lab = srl[2];
+ x->x_gui.x_ldx = ldx;
+ x->x_gui.x_ldy = ldy;
+
+ if(fs < 4)
+ fs = 4;
+ x->x_gui.x_fontsize = fs;
+ x->x_gui.x_w = iemgui_clip_size(w);
+ vu_check_height(x, h);
+ iemgui_all_colfromload(&x->x_gui, bflcol);
+ if(scale != 0)
+ scale = 1;
+ x->x_scale = scale;
+ x->x_peak = 0;
+ x->x_rms = 0;
+ x->x_fp = -101.0;
+ x->x_fr = -101.0;
+ iemgui_verify_snd_ne_rcv(&x->x_gui);
+ inlet_new(&x->x_gui.x_obj, &x->x_gui.x_obj.ob_pd, &s_float, gensym("ft1"));
+ x->x_out_rms = outlet_new(&x->x_gui.x_obj, &s_float);
+ x->x_out_peak = outlet_new(&x->x_gui.x_obj, &s_float);
+ return (x);
+}
+
+static void vu_free(t_vu *x)
+{
+ if(x->x_gui.x_fsf.x_selected)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym);
+ if(x->x_gui.x_fsf.x_rcv_able)
+ pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);
+ gfxstub_deleteforkey(x);
+}
+
+void g_vumeter_setup(void)
+{
+ vu_class = class_new(gensym("vu"), (t_newmethod)vu_new, (t_method)vu_free,
+ sizeof(t_vu), 0, A_GIMME, 0);
+ class_addbang(vu_class,vu_bang);
+ class_addfloat(vu_class,vu_float);
+ class_addmethod(vu_class, (t_method)vu_ft1, gensym("ft1"), A_FLOAT, 0);
+ class_addlist(vu_class, vu_list);
+ class_addmethod(vu_class, (t_method)vu_dialog, gensym("dialog"),
+ A_GIMME, 0);
+ class_addmethod(vu_class, (t_method)vu_size, gensym("size"), A_GIMME, 0);
+ class_addmethod(vu_class, (t_method)vu_scale, gensym("scale"), A_DEFFLOAT, 0);
+ class_addmethod(vu_class, (t_method)vu_delta, gensym("delta"), A_GIMME, 0);
+ class_addmethod(vu_class, (t_method)vu_pos, gensym("pos"), A_GIMME, 0);
+ class_addmethod(vu_class, (t_method)vu_color, gensym("color"), A_GIMME, 0);
+ class_addmethod(vu_class, (t_method)vu_receive, gensym("receive"), A_DEFSYM, 0);
+ class_addmethod(vu_class, (t_method)vu_label, gensym("label"), A_DEFSYM, 0);
+ class_addmethod(vu_class, (t_method)vu_label_pos, gensym("label_pos"), A_GIMME, 0);
+ class_addmethod(vu_class, (t_method)vu_label_font, gensym("label_font"), A_GIMME, 0);
+ if(!iemgui_key_sym)
+ iemgui_key_sym = gensym("#keyname");
+ vu_widgetbehavior.w_getrectfn = vu_getrect;
+ vu_widgetbehavior.w_displacefn = iemgui_displace;
+ vu_widgetbehavior.w_selectfn = iemgui_select;
+ vu_widgetbehavior.w_activatefn = NULL;
+ vu_widgetbehavior.w_deletefn = iemgui_delete;
+ vu_widgetbehavior.w_visfn = iemgui_vis;
+ vu_widgetbehavior.w_clickfn = NULL;
+ vu_widgetbehavior.w_propertiesfn = vu_properties;
+ vu_widgetbehavior.w_savefn = vu_save;
+ class_setwidget(vu_class,&vu_widgetbehavior);
+ class_sethelpsymbol(vu_class, gensym("vu"));
+}
diff --git a/pd/src/install-sh b/pd/src/install-sh
new file mode 100644
index 00000000..e9de2384
--- /dev/null
+++ b/pd/src/install-sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/pd/src/m_atom.c b/pd/src/m_atom.c
new file mode 100644
index 00000000..c9c4c284
--- /dev/null
+++ b/pd/src/m_atom.c
@@ -0,0 +1,129 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include "m_imp.h"
+#include <stdio.h>
+#include <string.h>
+
+ /* convenience routines for checking and getting values of
+ atoms. There's no "pointer" version since there's nothing
+ safe to return if there's an error. */
+
+t_float atom_getfloat(t_atom *a)
+{
+ if (a->a_type == A_FLOAT) return (a->a_w.w_float);
+ else return (0);
+}
+
+t_int atom_getint(t_atom *a)
+{
+ return (atom_getfloat(a));
+}
+
+t_symbol *atom_getsymbol(t_atom *a) /* LATER think about this more carefully */
+{
+ char buf[30];
+ if (a->a_type == A_SYMBOL) return (a->a_w.w_symbol);
+ else return (&s_float);
+}
+
+t_symbol *atom_gensym(t_atom *a) /* this works better for graph labels */
+{
+ char buf[30];
+ if (a->a_type == A_SYMBOL) return (a->a_w.w_symbol);
+ else if (a->a_type == A_FLOAT)
+ sprintf(buf, "%g", a->a_w.w_float);
+ else strcpy(buf, "???");
+ return (gensym(buf));
+}
+
+t_float atom_getfloatarg(int which, int argc, t_atom *argv)
+{
+ if (argc <= which) return (0);
+ argv += which;
+ if (argv->a_type == A_FLOAT) return (argv->a_w.w_float);
+ else return (0);
+}
+
+t_int atom_getintarg(int which, int argc, t_atom *argv)
+{
+ return (atom_getfloatarg(which, argc, argv));
+}
+
+t_symbol *atom_getsymbolarg(int which, int argc, t_atom *argv)
+{
+ if (argc <= which) return (&s_);
+ argv += which;
+ if (argv->a_type == A_SYMBOL) return (argv->a_w.w_symbol);
+ else return (&s_);
+}
+
+/* convert an atom into a string, in the reverse sense of binbuf_text (q.v.)
+* special attention is paid to symbols containing the special characters
+* ';', ',', '$', and '\'; these are quoted with a preceding '\', except that
+* the '$' only gets quoted at the beginning of the string.
+*/
+
+void atom_string(t_atom *a, char *buf, unsigned int bufsize)
+{
+ char tbuf[30];
+ switch(a->a_type)
+ {
+ case A_SEMI: strcpy(buf, ";"); break;
+ case A_COMMA: strcpy(buf, ","); break;
+ case A_POINTER:
+ strcpy(buf, "(pointer)");
+ break;
+ case A_FLOAT:
+ sprintf(tbuf, "%g", a->a_w.w_float);
+ if (strlen(tbuf) < bufsize-1) strcpy(buf, tbuf);
+ else if (a->a_w.w_float < 0) strcpy(buf, "-");
+ else strcat(buf, "+");
+ break;
+ case A_SYMBOL:
+ {
+ char *sp;
+ unsigned int len;
+ int quote;
+ for (sp = a->a_w.w_symbol->s_name, len = 0, quote = 0; *sp; sp++, len++)
+ if (*sp == ';' || *sp == ',' || *sp == '\\' ||
+ (*sp == '$' && sp == a->a_w.w_symbol->s_name && sp[1] >= '0'
+ && sp[1] <= '9'))
+ quote = 1;
+ if (quote)
+ {
+ char *bp = buf, *ep = buf + (bufsize-2);
+ sp = a->a_w.w_symbol->s_name;
+ while (bp < ep && *sp)
+ {
+ if (*sp == ';' || *sp == ',' || *sp == '\\' ||
+ (*sp == '$' && bp == buf && sp[1] >= '0' && sp[1] <= '9'))
+ *bp++ = '\\';
+ *bp++ = *sp++;
+ }
+ if (*sp) *bp++ = '*';
+ *bp = 0;
+ /* post("quote %s -> %s", a->a_w.w_symbol->s_name, buf); */
+ }
+ else
+ {
+ if (len < bufsize-1) strcpy(buf, a->a_w.w_symbol->s_name);
+ else
+ {
+ strncpy(buf, a->a_w.w_symbol->s_name, bufsize - 2);
+ strcpy(buf + (bufsize - 2), "*");
+ }
+ }
+ }
+ break;
+ case A_DOLLAR:
+ sprintf(buf, "$%d", a->a_w.w_index);
+ break;
+ case A_DOLLSYM:
+ sprintf(buf, "$%s", a->a_w.w_symbol->s_name);
+ break;
+ default:
+ bug("atom_string");
+ }
+}
diff --git a/pd/src/m_binbuf.c b/pd/src/m_binbuf.c
new file mode 100644
index 00000000..6ff79e93
--- /dev/null
+++ b/pd/src/m_binbuf.c
@@ -0,0 +1,1138 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+
+/* IOhannes :
+ * changed the canvas_restore in "g_canvas.c", so that it might accept $args as well (like "pd $0_test")
+ * so you can make multiple & distinguishable templates
+ * 1511:forum::für::umläute:2001
+ * change marked with IOhannes
+ */
+
+#include <stdlib.h>
+#include "m_pd.h"
+#include <stdio.h>
+#ifdef UNIX
+#include <unistd.h>
+#endif
+#ifdef NT
+#include <io.h>
+#endif
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+
+struct _binbuf
+{
+ int b_n;
+ t_atom *b_vec;
+};
+
+t_binbuf *binbuf_new(void)
+{
+ t_binbuf *x = (t_binbuf *)t_getbytes(sizeof(*x));
+ x->b_n = 0;
+ x->b_vec = t_getbytes(0);
+ return (x);
+}
+
+void binbuf_free(t_binbuf *x)
+{
+ t_freebytes(x->b_vec, x->b_n * sizeof(*x->b_vec));
+ t_freebytes(x, sizeof(*x));
+}
+
+void binbuf_clear(t_binbuf *x)
+{
+ x->b_vec = t_resizebytes(x->b_vec, x->b_n * sizeof(*x->b_vec), 0);
+ x->b_n = 0;
+}
+
+ /* convert text to a binbuf */
+void binbuf_text(t_binbuf *x, char *text, size_t size)
+{
+ char buf[MAXPDSTRING+1], *bufp, *ebuf = buf+MAXPDSTRING;
+ const char *textp = text, *etext = text+size;
+ t_atom *ap;
+ int nalloc = 16, natom = 0;
+ t_freebytes(x->b_vec, x->b_n * sizeof(*x->b_vec));
+ x->b_vec = t_getbytes(nalloc * sizeof(*x->b_vec));
+ ap = x->b_vec;
+ x->b_n = 0;
+ while (1)
+ {
+ int type;
+ /* skip leading space */
+ while ((textp != etext) && (*textp == ' ' || *textp == '\n'
+ || *textp == '\r' || *textp == '\t')) textp++;
+ if (textp == etext) break;
+ if (*textp == ';') SETSEMI(ap), textp++;
+ else if (*textp == ',') SETCOMMA(ap), textp++;
+ else
+ {
+ /* it's an atom other than a comma or semi */
+ char c;
+ int floatstate = 0, slash = 0, lastslash = 0,
+ firstslash = (*textp == '\\');
+ bufp = buf;
+ do
+ {
+ c = *bufp = *textp++;
+ lastslash = slash;
+ slash = (c == '\\');
+
+ if (floatstate >= 0)
+ {
+ int digit = (c >= '0' && c <= '9'),
+ dot = (c == '.'), minus = (c == '-'),
+ plusminus = (minus || (c == '+')),
+ expon = (c == 'e' || c == 'E');
+ if (floatstate == 0) /* beginning */
+ {
+ if (minus) floatstate = 1;
+ else if (digit) floatstate = 2;
+ else if (dot) floatstate = 3;
+ else floatstate = -1;
+ }
+ else if (floatstate == 1) /* got minus */
+ {
+ if (digit) floatstate = 2;
+ else if (dot) floatstate = 3;
+ else floatstate = -1;
+ }
+ else if (floatstate == 2) /* got digits */
+ {
+ if (dot) floatstate = 4;
+ else if (expon) floatstate = 6;
+ else if (!digit) floatstate = -1;
+ }
+ else if (floatstate == 3) /* got '.' without digits */
+ {
+ if (digit) floatstate = 5;
+ else floatstate = -1;
+ }
+ else if (floatstate == 4) /* got '.' after digits */
+ {
+ if (digit) floatstate = 5;
+ else if (expon) floatstate = 6;
+ else floatstate = -1;
+ }
+ else if (floatstate == 5) /* got digits after . */
+ {
+ if (expon) floatstate = 6;
+ else if (!digit) floatstate = -1;
+ }
+ else if (floatstate == 6) /* got 'e' */
+ {
+ if (plusminus) floatstate = 7;
+ else if (digit) floatstate = 8;
+ else floatstate = -1;
+ }
+ else if (floatstate == 7) /* got plus or minus */
+ {
+ if (digit) floatstate = 8;
+ else floatstate = -1;
+ }
+ else if (floatstate == 8) /* got digits */
+ {
+ if (!digit) floatstate = -1;
+ }
+ }
+ if (!slash) bufp++;
+ }
+ while (textp != etext && bufp != ebuf &&
+ (slash || (*textp != ' ' && *textp != '\n' && *textp != '\r'
+ && *textp != '\t' &&*textp != ',' && *textp != ';')));
+ *bufp = 0;
+#if 0
+ post("buf %s", buf);
+#endif
+ if (*buf == '$' && buf[1] >= '0' && buf[1] <= '9' && !firstslash)
+ {
+ for (bufp = buf+2; *bufp; bufp++)
+ if (*bufp < '0' || *bufp > '9')
+ {
+ SETDOLLSYM(ap, gensym(buf+1));
+ goto didit;
+ }
+ SETDOLLAR(ap, atoi(buf+1));
+ didit: ;
+ }
+ else
+ {
+ if (floatstate == 2 || floatstate == 4 || floatstate == 5 ||
+ floatstate == 8)
+ SETFLOAT(ap, atof(buf));
+ else SETSYMBOL(ap, gensym(buf));
+ }
+ }
+ ap++;
+ natom++;
+ if (natom == nalloc)
+ {
+ x->b_vec = t_resizebytes(x->b_vec, nalloc * sizeof(*x->b_vec),
+ nalloc * (2*sizeof(*x->b_vec)));
+ nalloc = nalloc * 2;
+ ap = x->b_vec + natom;
+ }
+ if (textp == etext) break;
+ }
+ /* reallocate the vector to exactly the right size */
+ x->b_vec = t_resizebytes(x->b_vec, nalloc * sizeof(*x->b_vec),
+ natom * sizeof(*x->b_vec));
+ x->b_n = natom;
+}
+
+ /* convert a binbuf to text; no null termination. */
+void binbuf_gettext(t_binbuf *x, char **bufp, int *lengthp)
+{
+ char *buf = getbytes(0), *newbuf;
+ int length = 0;
+ char string[MAXPDSTRING];
+ t_atom *ap;
+ int indx;
+
+ for (ap = x->b_vec, indx = x->b_n; indx--; ap++)
+ {
+ int newlength;
+ if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) &&
+ length && buf[length-1] == ' ') length--;
+ atom_string(ap, string, MAXPDSTRING);
+ newlength = length + strlen(string) + 1;
+ if (!(newbuf = resizebytes(buf, length, newlength))) break;
+ buf = newbuf;
+ strcpy(buf + length, string);
+ length = newlength;
+ if (ap->a_type == A_SEMI) buf[length-1] = '\n';
+ else buf[length-1] = ' ';
+ }
+ if (length && buf[length-1] == ' ')
+ {
+ if (newbuf = t_resizebytes(buf, length, length-1))
+ {
+ buf = newbuf;
+ length--;
+ }
+ }
+ *bufp = buf;
+ *lengthp = length;
+}
+
+/* LATER improve the out-of-space behavior below. Also fix this so that
+writing to file doesn't buffer everything together. */
+
+void binbuf_add(t_binbuf *x, int argc, t_atom *argv)
+{
+ int newsize = x->b_n + argc, i;
+ t_atom *ap;
+ if (ap = t_resizebytes(x->b_vec, x->b_n * sizeof(*x->b_vec),
+ newsize * sizeof(*x->b_vec)))
+ x->b_vec = ap;
+ else
+ {
+ error("binbuf_addmessage: out of space");
+ return;
+ }
+#if 0
+ startpost("binbuf_add: ");
+ postatom(argc, argv);
+ endpost();
+#endif
+ for (ap = x->b_vec + x->b_n, i = argc; i--; ap++)
+ *ap = *(argv++);
+ x->b_n = newsize;
+}
+
+#define MAXADDMESSV 100
+void binbuf_addv(t_binbuf *x, char *fmt, ...)
+{
+ va_list ap;
+ t_atom arg[MAXADDMESSV], *at =arg;
+ int nargs = 0;
+ char *fp = fmt;
+
+ va_start(ap, fmt);
+ while (1)
+ {
+ if (nargs >= MAXADDMESSV)
+ {
+ error("binbuf_addmessv: only %d allowed", MAXADDMESSV);
+ break;
+ }
+ switch(*fp++)
+ {
+ case 'i': SETFLOAT(at, va_arg(ap, t_int)); break;
+ case 'f': SETFLOAT(at, va_arg(ap, double)); break;
+ case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break;
+ case ';': SETSEMI(at); break;
+ case ',': SETCOMMA(at); break;
+ default: goto done;
+ }
+ at++;
+ nargs++;
+ }
+done:
+ va_end(ap);
+ binbuf_add(x, nargs, arg);
+}
+
+/* add a binbuf to another one for saving. Semicolons and commas go to
+symbols ";", "'",; the symbol ";" goes to "\;", etc. */
+
+void binbuf_addbinbuf(t_binbuf *x, t_binbuf *y)
+{
+ t_binbuf *z = binbuf_new();
+ int i;
+ t_atom *ap;
+ binbuf_add(z, y->b_n, y->b_vec);
+ for (i = 0, ap = z->b_vec; i < z->b_n; i++, ap++)
+ {
+ char tbuf[MAXPDSTRING];
+ switch (ap->a_type)
+ {
+ case A_FLOAT:
+ break;
+ case A_SEMI:
+ SETSYMBOL(ap, gensym(";"));
+ break;
+ case A_COMMA:
+ SETSYMBOL(ap, gensym(","));
+ break;
+ case A_DOLLAR:
+ sprintf(tbuf, "$%d", ap->a_w.w_index);
+ SETSYMBOL(ap, gensym(tbuf));
+ break;
+ case A_DOLLSYM:
+ sprintf(tbuf, "$%s", ap->a_w.w_symbol->s_name);
+ SETSYMBOL(ap, gensym(tbuf));
+ break;
+ case A_SYMBOL:
+ /* FIXME make this general */
+ if (!strcmp(ap->a_w.w_symbol->s_name, ";"))
+ SETSYMBOL(ap, gensym(";"));
+ else if (!strcmp(ap->a_w.w_symbol->s_name, ","))
+ SETSYMBOL(ap, gensym(","));
+ break;
+ default:
+ bug("binbuf_addbinbuf");
+ }
+ }
+
+ binbuf_add(x, z->b_n, z->b_vec);
+}
+
+void binbuf_addsemi(t_binbuf *x)
+{
+ t_atom a;
+ SETSEMI(&a);
+ binbuf_add(x, 1, &a);
+}
+
+/* Supply atoms to a binbuf from a message, making the opposite changes
+from binbuf_addbinbuf. The symbol ";" goes to a semicolon, etc. */
+
+void binbuf_restore(t_binbuf *x, int argc, t_atom *argv)
+{
+ int newsize = x->b_n + argc, i;
+ t_atom *ap;
+ if (ap = t_resizebytes(x->b_vec, x->b_n * sizeof(*x->b_vec),
+ newsize * sizeof(*x->b_vec)))
+ x->b_vec = ap;
+ else
+ {
+ error("binbuf_addmessage: out of space");
+ return;
+ }
+
+ for (ap = x->b_vec + x->b_n, i = argc; i--; ap++)
+ {
+ if (argv->a_type == A_SYMBOL)
+ {
+ char *str = argv->a_w.w_symbol->s_name;
+ if (!strcmp(str, ";")) SETSEMI(ap);
+ else if (!strcmp(str, ",")) SETCOMMA(ap);
+ else if (str[0] == '$' && str[1] >= '0' && str[1] <= '9')
+ {
+ int dollsym = 0;
+ char *str2;
+ for (str2 = str + 2; *str2; str2++)
+ if (*str2 < '0' || *str2 > '9')
+ dollsym = 1;
+ if (dollsym)
+ SETDOLLSYM(ap, gensym(str + 1));
+ else
+ {
+ int dollar = 0;
+ sscanf(argv->a_w.w_symbol->s_name + 1, "%d", &dollar);
+ SETDOLLAR(ap, dollar);
+ }
+ }
+ else *ap = *argv;
+ argv++;
+ }
+ else *ap = *(argv++);
+ }
+ x->b_n = newsize;
+}
+
+
+#define MSTACKSIZE 2048
+
+void binbuf_print(t_binbuf *x)
+{
+ int i, startedpost = 0, newline = 1;
+ for (i = 0; i < x->b_n; i++)
+ {
+ if (newline)
+ {
+ if (startedpost) endpost();
+ startpost("");
+ startedpost = 1;
+ }
+ postatom(1, x->b_vec + i);
+ if (x->b_vec[i].a_type == A_SEMI)
+ newline = 1;
+ else newline = 0;
+ }
+ if (startedpost) endpost();
+}
+
+int binbuf_getnatom(t_binbuf *x)
+{
+ return (x->b_n);
+}
+
+t_atom *binbuf_getvec(t_binbuf *x)
+{
+ return (x->b_vec);
+}
+
+int canvas_getdollarzero( void);
+
+t_symbol *realizedollsym(t_symbol *s, int ac, t_atom *av, int tonew) /* IOhannes: not static any more */
+{
+ int argno = atol(s->s_name), lastnum;
+ char buf[MAXPDSTRING], c, *sp;
+ for (lastnum = 0, sp = s->s_name; ((c = *sp) && c >= '0' && c <= '9');
+ sp++, lastnum++)
+ if (!c || argno < 0 || argno > ac)
+ {
+ if (!tonew)
+ return (0);
+ else sprintf(buf, "$%d", argno);
+ }
+ else if (argno == 0)
+ sprintf(buf, "%d", canvas_getdollarzero());
+ else
+ atom_string(av+(argno-1), buf, MAXPDSTRING/2-1);
+ strncat(buf, sp, MAXPDSTRING/2-1);
+ return (gensym(buf));
+}
+
+void binbuf_eval(t_binbuf *x, t_pd *target, int argc, t_atom *argv)
+{
+ static t_atom mstack[MSTACKSIZE], *msp = mstack, *ems = mstack+MSTACKSIZE;
+ t_atom *stackwas = msp;
+ t_atom *at = x->b_vec;
+ int ac = x->b_n;
+ int nargs;
+
+ while (1)
+ {
+ t_pd *nexttarget;
+ /* get a target. */
+ while (!target)
+ {
+ t_symbol *s;
+ while (ac && (at->a_type == A_SEMI || at->a_type == A_COMMA))
+ ac--, at++;
+ if (!ac) break;
+ if (at->a_type == A_DOLLAR)
+ {
+ if (at->a_w.w_index <= 0 || at->a_w.w_index > argc)
+ {
+ error("$%d: not enough arguments supplied",
+ at->a_w.w_index);
+ goto cleanup;
+ }
+ else if (argv[at->a_w.w_index-1].a_type != A_SYMBOL)
+ {
+ error("$%d: symbol needed as message destination",
+ at->a_w.w_index);
+ goto cleanup;
+ }
+ else s = argv[at->a_w.w_index-1].a_w.w_symbol;
+ }
+ else if (at->a_type == A_DOLLSYM)
+ {
+ if (!(s = realizedollsym(at->a_w.w_symbol, argc, argv, 0)))
+ {
+ error("$%s: not enough arguments supplied",
+ at->a_w.w_symbol->s_name);
+ goto cleanup;
+ }
+ }
+ else s = atom_getsymbol(at);
+ if (!(target = s->s_thing))
+ {
+ error("%s: no such object", s->s_name);
+ cleanup:
+ do at++, ac--;
+ while (ac && at->a_type != A_SEMI);
+ /* LATER eat args until semicolon and continue */
+ continue;
+ }
+ else
+ {
+ at++, ac--;
+ break;
+ }
+ }
+ if (!ac) break;
+ nargs = 0;
+ nexttarget = target;
+ while (1)
+ {
+ t_symbol *s9;
+ if (!ac) goto gotmess;
+ if (msp >= ems)
+ {
+ error("message stack overflow");
+ goto broken;
+ }
+ switch (at->a_type)
+ {
+ case A_SEMI:
+ /* semis and commas in new message just get bashed to
+ a symbol. This is needed so you can pass them to "expr." */
+ if (target == &pd_objectmaker)
+ {
+ SETSYMBOL(msp, gensym(";"));
+ break;
+ }
+ else
+ {
+ nexttarget = 0;
+ goto gotmess;
+ }
+ case A_COMMA:
+ if (target == &pd_objectmaker)
+ {
+ SETSYMBOL(msp, gensym(","));
+ break;
+ }
+ else goto gotmess;
+ case A_FLOAT:
+ case A_SYMBOL:
+ *msp = *at;
+ break;
+ case A_DOLLAR:
+ if (at->a_w.w_index > 0 && at->a_w.w_index <= argc)
+ *msp = argv[at->a_w.w_index-1];
+ else if (at->a_w.w_index == 0)
+ SETFLOAT(msp, canvas_getdollarzero());
+ else
+ {
+ if (target == &pd_objectmaker)
+ SETFLOAT(msp, 0);
+ else
+ {
+ error("$%d: argument number out of range",
+ at->a_w.w_index);
+ SETFLOAT(msp, 0);
+ }
+ }
+ break;
+ case A_DOLLSYM:
+ s9 = realizedollsym(at->a_w.w_symbol, argc, argv,
+ target == &pd_objectmaker);
+ if (!s9)
+ goto broken;
+ SETSYMBOL(msp, s9);
+ break;
+ default:
+ bug("bad item in binbuf");
+ goto broken;
+ }
+ msp++;
+ ac--;
+ at++;
+ nargs++;
+ }
+ gotmess:
+ if (nargs)
+ {
+ switch (stackwas->a_type)
+ {
+ case A_SYMBOL:
+ typedmess(target, stackwas->a_w.w_symbol, nargs-1, stackwas+1);
+ break;
+ case A_FLOAT:
+ if (nargs == 1) pd_float(target, stackwas->a_w.w_float);
+ else pd_list(target, 0, nargs, stackwas);
+ break;
+ }
+ }
+ msp = stackwas;
+ if (!ac) break;
+ target = nexttarget;
+ at++;
+ ac--;
+ }
+
+ return;
+broken:
+ msp = stackwas;
+}
+
+static int binbuf_doopen(char *s, int mode)
+{
+ char namebuf[MAXPDSTRING];
+#ifdef NT
+ mode |= O_BINARY;
+#endif
+ sys_bashfilename(s, namebuf);
+ return (open(namebuf, mode));
+}
+
+static FILE *binbuf_dofopen(char *s, char *mode)
+{
+ char namebuf[MAXPDSTRING];
+ sys_bashfilename(s, namebuf);
+ return (fopen(namebuf, mode));
+}
+
+int binbuf_read(t_binbuf *b, char *filename, char *dirname, int crflag)
+{
+ long length;
+ int fd;
+ int readret;
+ char *buf;
+ char namebuf[MAXPDSTRING];
+
+ namebuf[0] = 0;
+ if (*dirname)
+ strcat(namebuf, dirname), strcat(namebuf, "/");
+ strcat(namebuf, filename);
+
+ if ((fd = binbuf_doopen(namebuf, 0)) < 0)
+ {
+ fprintf(stderr, "open: ");
+ perror(namebuf);
+ return (1);
+ }
+ if ((length = lseek(fd, 0, SEEK_END)) < 0 || lseek(fd, 0, SEEK_SET) < 0
+ || !(buf = t_getbytes(length)))
+ {
+ fprintf(stderr, "lseek: ");
+ perror(namebuf);
+ close(fd);
+ return(1);
+ }
+ if ((readret = read(fd, buf, length)) < length)
+ {
+ fprintf(stderr, "read (%d %ld) -> %d\n", fd, length, readret);
+ perror(namebuf);
+ close(fd);
+ t_freebytes(buf, length);
+ return(1);
+ }
+ /* optionally map carriage return to semicolon */
+ if (crflag)
+ {
+ int i;
+ for (i = 0; i < length; i++)
+ if (buf[i] == '\n')
+ buf[i] = ';';
+ }
+ binbuf_text(b, buf, length);
+
+#if 0
+ startpost("binbuf_read "); postatom(b->b_n, b->b_vec); endpost();
+#endif
+
+ t_freebytes(buf, length);
+ close(fd);
+ return (0);
+}
+
+int binbuf_read_via_path(t_binbuf *b, char *filename, char *dirname,
+ int crflag)
+{
+ int filedesc;
+ char buf[MAXPDSTRING], *bufptr;
+ if ((filedesc = open_via_path(
+ dirname, filename, "", buf, &bufptr, MAXPDSTRING, 0)) < 0)
+ {
+ error("%s: can't open", filename);
+ return (1);
+ }
+ else close (filedesc);
+ if (binbuf_read(b, bufptr, buf, crflag))
+ return (1);
+ else return (0);
+}
+
+#define WBUFSIZE 4096
+static t_binbuf *binbuf_convert(t_binbuf *oldb, int maxtopd);
+
+ /* write a binbuf to a text file. If "crflag" is set we suppress
+ semicolons. */
+int binbuf_write(t_binbuf *x, char *filename, char *dir, int crflag)
+{
+ FILE *f = 0;
+ char sbuf[WBUFSIZE], fbuf[MAXPDSTRING], *bp = sbuf, *ep = sbuf + WBUFSIZE;
+ t_atom *ap;
+ int indx, deleteit = 0;
+ int ncolumn = 0;
+
+ fbuf[0] = 0;
+ if (*dir)
+ strcat(fbuf, dir), strcat(fbuf, "/");
+ strcat(fbuf, filename);
+ if (!strcmp(filename + strlen(filename) - 4, ".pat"))
+ {
+ x = binbuf_convert(x, 0);
+ deleteit = 1;
+ }
+
+ if (!(f = binbuf_dofopen(fbuf, "w")))
+ {
+ fprintf(stderr, "open: ");
+ sys_unixerror(fbuf);
+ goto fail;
+ }
+ for (ap = x->b_vec, indx = x->b_n; indx--; ap++)
+ {
+ int length;
+ /* estimate how many characters will be needed. Printing out
+ symbols may need extra characters for inserting backslashes. */
+ if (ap->a_type == A_SYMBOL || ap->a_type == A_DOLLSYM)
+ length = 80 + strlen(ap->a_w.w_symbol->s_name);
+ else length = 40;
+ if (ep - bp < length)
+ {
+ if (fwrite(sbuf, bp-sbuf, 1, f) < 1)
+ {
+ sys_unixerror(fbuf);
+ goto fail;
+ }
+ bp = sbuf;
+ }
+ if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) &&
+ bp > sbuf && bp[-1] == ' ') bp--;
+ if (!crflag || ap->a_type != A_SEMI)
+ {
+ atom_string(ap, bp, (ep-bp)-2);
+ length = strlen(bp);
+ bp += length;
+ ncolumn += length;
+ }
+ if (ap->a_type == A_SEMI || ncolumn > 65)
+ {
+ *bp++ = '\n';
+ ncolumn = 0;
+ }
+ else
+ {
+ *bp++ = ' ';
+ ncolumn++;
+ }
+ }
+ if (fwrite(sbuf, bp-sbuf, 1, f) < 1)
+ {
+ sys_unixerror(fbuf);
+ goto fail;
+ }
+ if (deleteit)
+ binbuf_free(x);
+ fclose(f);
+ return (0);
+fail:
+ if (deleteit)
+ binbuf_free(x);
+ if (f)
+ fclose(f);
+ return (1);
+}
+
+/* The following routine attempts to convert from max to pd or back. The
+max to pd direction is working OK but you will need to make lots of
+abstractions for objects like "gate" which don't exist in Pd. conversion
+from Pd to Max hasn't been tested for patches with subpatches yet! */
+
+#define MAXSTACK 1000
+
+#define ISSYMBOL(a, b) ((a)->a_type == A_SYMBOL && \
+ !strcmp((a)->a_w.w_symbol->s_name, (b)))
+
+static t_binbuf *binbuf_convert(t_binbuf *oldb, int maxtopd)
+{
+ t_binbuf *newb = binbuf_new();
+ t_atom *vec = oldb->b_vec;
+ t_int n = oldb->b_n, nextindex, stackdepth = 0, stack[MAXSTACK],
+ nobj = 0, i;
+ t_atom outmess[MAXSTACK], *nextmess;
+ if (!maxtopd)
+ binbuf_addv(newb, "ss;", gensym("max"), gensym("v2"));
+ for (nextindex = 0; nextindex < n; )
+ {
+ int endmess, natom;
+ char *first, *second;
+ for (endmess = nextindex; endmess < n && vec[endmess].a_type != A_SEMI;
+ endmess++)
+ ;
+ if (endmess == n) break;
+ if (endmess == nextindex || endmess == nextindex + 1
+ || vec[nextindex].a_type != A_SYMBOL ||
+ vec[nextindex+1].a_type != A_SYMBOL)
+ {
+ nextindex = endmess + 1;
+ continue;
+ }
+ natom = endmess - nextindex;
+ if (natom > MAXSTACK-10) natom = MAXSTACK-10;
+ nextmess = vec + nextindex;
+ first = nextmess->a_w.w_symbol->s_name;
+ second = (nextmess+1)->a_w.w_symbol->s_name;
+ if (maxtopd)
+ {
+ /* case 1: importing a ".pat" file into Pd. */
+
+ /* dollar signs in file translate to symbols */
+ for (i = 0; i < natom; i++)
+ {
+ if (nextmess[i].a_type == A_DOLLAR)
+ {
+ char buf[100];
+ sprintf(buf, "$%d", nextmess[i].a_w.w_index);
+ SETSYMBOL(nextmess+i, gensym(buf));
+ }
+ else if (nextmess[i].a_type == A_DOLLSYM)
+ {
+ char buf[100];
+ sprintf(buf, "$%s", nextmess[i].a_w.w_symbol->s_name);
+ SETSYMBOL(nextmess+i, gensym(buf));
+ }
+ }
+ if (!strcmp(first, "#N"))
+ {
+ if (!strcmp(second, "vpatcher"))
+ {
+ if (stackdepth >= MAXSTACK)
+ {
+ post("too many embedded patches");
+ return (newb);
+ }
+ stack[stackdepth] = nobj;
+ stackdepth++;
+ nobj = 0;
+ binbuf_addv(newb, "ssfffff;",
+ gensym("#N"), gensym("canvas"),
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(3, natom, nextmess),
+ atom_getfloatarg(4, natom, nextmess) -
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(5, natom, nextmess) -
+ atom_getfloatarg(3, natom, nextmess),
+ 12.);
+ }
+ }
+ if (!strcmp(first, "#P"))
+ {
+ if (natom >= 7 && !strcmp(second, "newobj")
+ && (ISSYMBOL(&nextmess[6], "patcher") ||
+ ISSYMBOL(&nextmess[6], "p")))
+ {
+ binbuf_addv(newb, "ssffss;",
+ gensym("#X"), gensym("restore"),
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(3, natom, nextmess),
+ gensym("pd"), atom_getsymbolarg(7, natom, nextmess));
+ if (stackdepth) stackdepth--;
+ nobj = stack[stackdepth];
+ nobj++;
+ }
+ else if (!strcmp(second, "newex") || !strcmp(second, "newobj"))
+ {
+ t_symbol *classname =
+ atom_getsymbolarg(6, natom, nextmess);
+ if (classname == gensym("trigger") ||
+ classname == gensym("t"))
+ {
+ for (i = 7; i < natom; i++)
+ if (nextmess[i].a_type == A_SYMBOL &&
+ nextmess[i].a_w.w_symbol == gensym("i"))
+ nextmess[i].a_w.w_symbol = gensym("f");
+ }
+ SETSYMBOL(outmess, gensym("#X"));
+ SETSYMBOL(outmess + 1, gensym("obj"));
+ outmess[2] = nextmess[2];
+ outmess[3] = nextmess[3];
+ for (i = 6; i < natom; i++)
+ outmess[i-2] = nextmess[i];
+ SETSEMI(outmess + natom - 2);
+ binbuf_add(newb, natom - 1, outmess);
+ nobj++;
+ }
+ else if (!strcmp(second, "message") ||
+ !strcmp(second, "comment"))
+ {
+ SETSYMBOL(outmess, gensym("#X"));
+ SETSYMBOL(outmess + 1, gensym(
+ (strcmp(second, "message") ? "text" : "msg")));
+ outmess[2] = nextmess[2];
+ outmess[3] = nextmess[3];
+ for (i = 6; i < natom; i++)
+ outmess[i-2] = nextmess[i];
+ SETSEMI(outmess + natom - 2);
+ binbuf_add(newb, natom - 1, outmess);
+ nobj++;
+ }
+ else if (!strcmp(second, "button"))
+ {
+ binbuf_addv(newb, "ssffs;",
+ gensym("#X"), gensym("msg"),
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(3, natom, nextmess),
+ gensym("bang"));
+ nobj++;
+ }
+ else if (!strcmp(second, "slider") || !strcmp(second, "number")
+ || !strcmp(second, "flonum") || !strcmp(second, "toggle"))
+ {
+ binbuf_addv(newb, "ssff;",
+ gensym("#X"), gensym("floatatom"),
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(3, natom, nextmess));
+ nobj++;
+ }
+ else if (!strcmp(second, "inlet"))
+ {
+ binbuf_addv(newb, "ssffs;",
+ gensym("#X"), gensym("obj"),
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(3, natom, nextmess),
+ gensym((natom > 5 ? "inlet~" : "inlet")));
+ nobj++;
+ }
+ else if (!strcmp(second, "outlet"))
+ {
+ binbuf_addv(newb, "ssffs;",
+ gensym("#X"), gensym("obj"),
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(3, natom, nextmess),
+ gensym((natom > 5 ? "outlet~" : "outlet")));
+ nobj++;
+ }
+ else if (!strcmp(second, "connect")||
+ !strcmp(second, "fasten"))
+ {
+ binbuf_addv(newb, "ssffff;",
+ gensym("#X"), gensym("connect"),
+ nobj - atom_getfloatarg(2, natom, nextmess) - 1,
+ atom_getfloatarg(3, natom, nextmess),
+ nobj - atom_getfloatarg(4, natom, nextmess) - 1,
+ atom_getfloatarg(5, natom, nextmess));
+ }
+ }
+ }
+ else /* Pd to Max */
+ {
+ if (!strcmp(first, "#N"))
+ {
+ if (!strcmp(second, "canvas"))
+ {
+ if (stackdepth >= MAXSTACK)
+ {
+ post("too many embedded patches");
+ return (newb);
+ }
+ stack[stackdepth] = nobj;
+ stackdepth++;
+ nobj = 0;
+ binbuf_addv(newb, "ssffff;",
+ gensym("#N"), gensym("vpatcher"),
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(3, natom, nextmess),
+ atom_getfloatarg(4, natom, nextmess),
+ atom_getfloatarg(5, natom, nextmess));
+ }
+ }
+ if (!strcmp(first, "#X"))
+ {
+ if (natom >= 5 && !strcmp(second, "restore")
+ && (ISSYMBOL (&nextmess[4], "pd")))
+ {
+ binbuf_addv(newb, "ss;", gensym("#P"), gensym("pop"));
+ binbuf_addv(newb, "ssffffss;",
+ gensym("#P"), gensym("newobj"),
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(3, natom, nextmess), 50., 1.,
+ gensym("patcher"),
+ atom_getsymbolarg(5, natom, nextmess));
+ if (stackdepth) stackdepth--;
+ nobj = stack[stackdepth];
+ nobj++;
+ }
+ else if (!strcmp(second, "obj"))
+ {
+ t_symbol *classname =
+ atom_getsymbolarg(4, natom, nextmess);
+ if (classname == gensym("inlet"))
+ binbuf_addv(newb, "ssfff;", gensym("#P"),
+ gensym("inlet"),
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(3, natom, nextmess),
+ 15.);
+ else if (classname == gensym("inlet~"))
+ binbuf_addv(newb, "ssffff;", gensym("#P"),
+ gensym("inlet"),
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(3, natom, nextmess),
+ 15., 1.);
+ else if (classname == gensym("outlet"))
+ binbuf_addv(newb, "ssfff;", gensym("#P"),
+ gensym("outlet"),
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(3, natom, nextmess),
+ 15.);
+ else if (classname == gensym("outlet~"))
+ binbuf_addv(newb, "ssffff;", gensym("#P"),
+ gensym("outlet"),
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(3, natom, nextmess),
+ 15., 1.);
+ else
+ {
+ SETSYMBOL(outmess, gensym("#P"));
+ SETSYMBOL(outmess + 1, gensym("newex"));
+ outmess[2] = nextmess[2];
+ outmess[3] = nextmess[3];
+ SETFLOAT(outmess + 4, 50);
+ SETFLOAT(outmess + 5, 1);
+ for (i = 4; i < natom; i++)
+ outmess[i+2] = nextmess[i];
+ SETSEMI(outmess + natom + 2);
+ binbuf_add(newb, natom + 3, outmess);
+ }
+ nobj++;
+
+ }
+ else if (!strcmp(second, "msg") ||
+ !strcmp(second, "text"))
+ {
+ SETSYMBOL(outmess, gensym("#P"));
+ SETSYMBOL(outmess + 1, gensym(
+ (strcmp(second, "msg") ? "comment" : "message")));
+ outmess[2] = nextmess[2];
+ outmess[3] = nextmess[3];
+ SETFLOAT(outmess + 4, 50);
+ SETFLOAT(outmess + 5, 1);
+ for (i = 4; i < natom; i++)
+ outmess[i+2] = nextmess[i];
+ SETSEMI(outmess + natom + 2);
+ binbuf_add(newb, natom + 3, outmess);
+ nobj++;
+ }
+ else if (!strcmp(second, "floatatom"))
+ {
+ binbuf_addv(newb, "ssfff;",
+ gensym("#P"), gensym("flonum"),
+ atom_getfloatarg(2, natom, nextmess),
+ atom_getfloatarg(3, natom, nextmess), 35);
+ nobj++;
+ }
+ else if (!strcmp(second, "connect"))
+ {
+ binbuf_addv(newb, "ssffff;",
+ gensym("#P"), gensym("connect"),
+ nobj - atom_getfloatarg(2, natom, nextmess) - 1,
+ atom_getfloatarg(3, natom, nextmess),
+ nobj - atom_getfloatarg(4, natom, nextmess) - 1,
+ atom_getfloatarg(5, natom, nextmess));
+ }
+ }
+ }
+ nextindex = endmess + 1;
+ }
+ if (!maxtopd)
+ binbuf_addv(newb, "ss;", gensym("#P"), gensym("pop"));
+#if 1
+ binbuf_write(newb, "import-result.pd", "/tmp", 0);
+#endif
+ return (newb);
+}
+
+ /* function to support searching */
+int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf)
+{
+ int indexin, nmatched;
+ for (indexin = 0; indexin <= inbuf->b_n - searchbuf->b_n; indexin++)
+ {
+ for (nmatched = 0; nmatched < searchbuf->b_n; nmatched++)
+ {
+ t_atom *a1 = &inbuf->b_vec[indexin + nmatched],
+ *a2 = &searchbuf->b_vec[nmatched];
+ if (a1->a_type != a2->a_type ||
+ a1->a_type == A_SYMBOL && a1->a_w.w_symbol != a2->a_w.w_symbol
+ ||
+ a1->a_type == A_FLOAT && a1->a_w.w_float != a2->a_w.w_float
+ ||
+ a1->a_type == A_DOLLAR && a1->a_w.w_index != a2->a_w.w_index
+ ||
+ a1->a_type == A_DOLLSYM && a1->a_w.w_symbol != a2->a_w.w_symbol)
+ goto nomatch;
+ }
+ return (1);
+ nomatch: ;
+ }
+ return (0);
+}
+
+void pd_doloadbang(void);
+
+/* LATER make this evaluate the file on-the-fly. */
+/* LATER figure out how to log errors */
+void binbuf_evalfile(t_symbol *name, t_symbol *dir)
+{
+ t_binbuf *b = binbuf_new();
+ int import = !strcmp(name->s_name + strlen(name->s_name) - 4, ".pat");
+ /* set filename so that new canvases can pick them up */
+ int dspstate = canvas_suspend_dsp();
+ glob_setfilename(0, name, dir);
+ if (binbuf_read(b, name->s_name, dir->s_name, 0))
+ {
+ perror(name->s_name);
+ }
+ else
+ {
+ if (import)
+ {
+ t_binbuf *newb = binbuf_convert(b, 1);
+ binbuf_free(b);
+ b = newb;
+ }
+ binbuf_eval(b, 0, 0, 0);
+ }
+ glob_setfilename(0, &s_, &s_); /* bug fix by Krzysztof Czaja */
+ binbuf_free(b);
+ canvas_resume_dsp(dspstate);
+}
+
+void glob_evalfile(t_pd *ignore, t_symbol *name, t_symbol *dir)
+{
+ t_pd *x = 0;
+ /* even though binbuf_evalfile appears to take care of dspstate,
+ we have to do it again here, because canvas_startdsp() assumes
+ that all toplevel canvases are visible. LATER check if this
+ is still necessary -- probably not. */
+
+ int dspstate = canvas_suspend_dsp();
+ binbuf_evalfile(name, dir);
+ while ((x != s__X.s_thing) && (x = s__X.s_thing))
+ vmess(x, gensym("pop"), "i", 1);
+ pd_doloadbang();
+ canvas_resume_dsp(dspstate);
+}
diff --git a/pd/src/m_class.c b/pd/src/m_class.c
new file mode 100644
index 00000000..9d6329d6
--- /dev/null
+++ b/pd/src/m_class.c
@@ -0,0 +1,772 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#define PD_CLASS_DEF
+#include "m_imp.h"
+#include <stdlib.h>
+#ifdef UNIX
+#include <unistd.h>
+#endif
+#ifdef NT
+#include <io.h>
+#endif
+
+#include <stdarg.h>
+#include <string.h>
+
+static t_symbol *class_loadsym; /* name under which an extern is invoked */
+static void pd_defaultfloat(t_pd *x, t_float f);
+static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv);
+t_pd pd_objectmaker; /* factory for creating "object" boxes */
+t_pd pd_canvasmaker; /* factory for creating canvases */
+
+static void pd_defaultanything(t_pd *x, t_symbol *s, int argc, t_atom *argv)
+{
+ pd_error(x, "%s: no method for '%s'", (*x)->c_name->s_name, s->s_name);
+}
+
+static void pd_defaultbang(t_pd *x)
+{
+ if (*(*x)->c_listmethod != pd_defaultlist)
+ (*(*x)->c_listmethod)(x, 0, 0, 0);
+ else (*(*x)->c_anymethod)(x, &s_bang, 0, 0);
+}
+
+static void pd_defaultpointer(t_pd *x, t_gpointer *gp)
+{
+ if (*(*x)->c_listmethod != pd_defaultlist)
+ {
+ t_atom at;
+ SETPOINTER(&at, gp);
+ (*(*x)->c_listmethod)(x, 0, 1, &at);
+ }
+ else
+ {
+ t_atom at;
+ SETPOINTER(&at, gp);
+ (*(*x)->c_anymethod)(x, &s_pointer, 1, &at);
+ }
+}
+
+static void pd_defaultfloat(t_pd *x, t_float f)
+{
+ if (*(*x)->c_listmethod != pd_defaultlist)
+ {
+ t_atom at;
+ SETFLOAT(&at, f);
+ (*(*x)->c_listmethod)(x, 0, 1, &at);
+ }
+ else
+ {
+ t_atom at;
+ SETFLOAT(&at, f);
+ (*(*x)->c_anymethod)(x, &s_float, 1, &at);
+ }
+}
+
+static void pd_defaultsymbol(t_pd *x, t_symbol *s)
+{
+ if (*(*x)->c_listmethod != pd_defaultlist)
+ {
+ t_atom at;
+ SETSYMBOL(&at, s);
+ (*(*x)->c_listmethod)(x, 0, 1, &at);
+ }
+ else
+ {
+ t_atom at;
+ SETSYMBOL(&at, s);
+ (*(*x)->c_anymethod)(x, &s_symbol, 1, &at);
+ }
+}
+
+void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv);
+
+ /* handle "list" messages to Pds without explicit list methods defined. */
+static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv)
+{
+ /* a list with one element which is a number can be handled by a
+ "float" method if any is defined; same for "symbol", "pointer". */
+ if (argc == 1)
+ {
+ if (argv->a_type == A_FLOAT &&
+ *(*x)->c_floatmethod != pd_defaultfloat)
+ {
+ (*(*x)->c_floatmethod)(x, argv->a_w.w_float);
+ return;
+ }
+ else if (argv->a_type == A_SYMBOL &&
+ *(*x)->c_symbolmethod != pd_defaultsymbol)
+ {
+ (*(*x)->c_symbolmethod)(x, argv->a_w.w_symbol);
+ return;
+ }
+ else if (argv->a_type == A_POINTER &&
+ *(*x)->c_pointermethod != pd_defaultpointer)
+ {
+ (*(*x)->c_pointermethod)(x, argv->a_w.w_gpointer);
+ return;
+ }
+ }
+ /* Next try for an "anything" method */
+ if ((*x)->c_anymethod != pd_defaultanything)
+ (*(*x)->c_anymethod)(x, &s_list, argc, argv);
+
+ /* if the object is patchable (i.e., can have proper inlets)
+ send it on to obj_list which will unpack the list into the inlets */
+ else if ((*x)->c_patchable)
+ obj_list((t_object *)x, s, argc, argv);
+ /* otherwise gove up and complain. */
+ else pd_defaultanything(x, &s_list, argc, argv);
+}
+
+ /* for now we assume that all "gobjs" are text unless explicitly
+ overridden later by calling class_setbehavior(). I'm not sure
+ how to deal with Pds that aren't gobjs; shouldn't there be a
+ way to check that at run time? Perhaps the presence of a "newmethod"
+ should be our cue, or perhaps the "tiny" flag. */
+
+ /* another matter. This routine does two unrelated things: it creates
+ a Pd class, but also adds a "new" method to create an instance of it.
+ These are combined for historical reasons and for brevity in writing
+ objects. To avoid adding a "new" method send a null function pointer.
+ To add additional ones, use class_addcreator below. Some "classes", like
+ "select", are actually two classes of the same name, one for the single-
+ argument form, one for the multiple one; see select_setup() to find out
+ how this is handled. */
+
+extern t_widgetbehavior text_widgetbehavior;
+
+t_class *class_new(t_symbol *s, t_newmethod newmethod, t_method freemethod,
+ size_t size, int flags, t_atomtype type1, ...)
+{
+ va_list ap;
+ t_atomtype vec[MAXPDARG+1], *vp = vec;
+ int count = 0;
+ t_class *c;
+ int typeflag = flags & CLASS_TYPEMASK;
+ if (!typeflag) typeflag = CLASS_PATCHABLE;
+ *vp = type1;
+
+ va_start(ap, type1);
+ while (*vp)
+ {
+ if (count == MAXPDARG)
+ {
+ error("class %s: sorry: only %d creation args allowed",
+ s->s_name, MAXPDARG);
+ break;
+ }
+ vp++;
+ count++;
+ *vp = va_arg(ap, t_atomtype);
+ }
+ va_end(ap);
+ if (pd_objectmaker && newmethod)
+ {
+ /* add a "new" method by the name specified by the object */
+ class_addmethod(pd_objectmaker, (t_method)newmethod, s,
+ vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]);
+ if (class_loadsym)
+ {
+ /* if we're loading an extern it might have been invoked by a
+ longer file name; in this case, make this an admissible name
+ too. */
+ char *loadstring = class_loadsym->s_name,
+ l1 = strlen(s->s_name), l2 = strlen(loadstring);
+ if (l2 > l1 && !strcmp(s->s_name, loadstring + (l2 - l1)))
+ class_addmethod(pd_objectmaker, (t_method)newmethod,
+ class_loadsym,
+ vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]);
+ }
+ }
+ c = (t_class *)t_getbytes(sizeof(*c));
+ c->c_name = c->c_helpname = s;
+ c->c_size = size;
+ c->c_methods = t_getbytes(0);
+ c->c_nmethod = 0;
+ c->c_freemethod = (t_method)freemethod;
+ c->c_bangmethod = pd_defaultbang;
+ c->c_pointermethod = pd_defaultpointer;
+ c->c_floatmethod = pd_defaultfloat;
+ c->c_symbolmethod = pd_defaultsymbol;
+ c->c_listmethod = pd_defaultlist;
+ c->c_anymethod = pd_defaultanything;
+ c->c_wb = (typeflag == CLASS_PATCHABLE ? &text_widgetbehavior : 0);
+ c->c_pwb = 0;
+ c->c_firstin = ((flags & CLASS_NOINLET) == 0);
+ c->c_patchable = (typeflag == CLASS_PATCHABLE);
+ c->c_gobj = (typeflag >= CLASS_GOBJ);
+ c->c_drawcommand = 0;
+ c->c_floatsignalin = 0;
+#if 0
+ post("class: %s", c->c_name->s_name);
+#endif
+ return (c);
+}
+
+ /* add a creation method, which is a function that returns a Pd object
+ suitable for putting in an object box. We presume you've got a class it
+ can belong to, but this won't be used until the newmethod is actually
+ called back (and the new method explicitly takes care of this.) */
+
+void class_addcreator(t_newmethod newmethod, t_symbol *s,
+ t_atomtype type1, ...)
+{
+ va_list ap;
+ t_atomtype vec[MAXPDARG+1], *vp = vec;
+ int count = 0;
+ *vp = type1;
+
+ va_start(ap, type1);
+ while (*vp)
+ {
+ if (count == MAXPDARG)
+ {
+ error("class %s: sorry: only %d creation args allowed",
+ s->s_name, MAXPDARG);
+ break;
+ }
+ vp++;
+ count++;
+ *vp = va_arg(ap, t_atomtype);
+ }
+ va_end(ap);
+ class_addmethod(pd_objectmaker, (t_method)newmethod, s,
+ vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]);
+}
+
+void class_addmethod(t_class *c, t_method fn, t_symbol *sel,
+ t_atomtype arg1, ...)
+{
+ va_list ap;
+ t_methodentry *m;
+ t_atomtype argtype = arg1;
+ int nargs;
+
+ va_start(ap, arg1);
+ /* "signal" method specifies that we take audio signals but
+ that we don't want automatic float to signal conversion. This
+ is obsolete; you should now use the CLASS_MAINSIGNALIN macro. */
+ if (sel == &s_signal)
+ {
+ if (c->c_floatsignalin)
+ post("warning: signal method overrides class_mainsignalin");
+ c->c_floatsignalin = -1;
+ }
+ /* check for special cases. "Pointer" is missing here so that
+ pd_objectmaker's pointer method can be typechecked differently. */
+ if (sel == &s_bang)
+ {
+ if (argtype) goto phooey;
+ class_addbang(c, fn);
+ }
+ else if (sel == &s_float)
+ {
+ if (argtype != A_FLOAT || va_arg(ap, t_atomtype)) goto phooey;
+ class_doaddfloat(c, fn);
+ }
+ else if (sel == &s_symbol)
+ {
+ if (argtype != A_SYMBOL || va_arg(ap, t_atomtype)) goto phooey;
+ class_addsymbol(c, fn);
+ }
+ else if (sel == &s_list)
+ {
+ if (argtype != A_GIMME) goto phooey;
+ class_addlist(c, fn);
+ }
+ else if (sel == &s_anything)
+ {
+ if (argtype != A_GIMME) goto phooey;
+ class_addanything(c, fn);
+ }
+ else
+ {
+ c->c_methods = t_resizebytes(c->c_methods,
+ c->c_nmethod * sizeof(*c->c_methods),
+ (c->c_nmethod + 1) * sizeof(*c->c_methods));
+ m = c->c_methods + c->c_nmethod;
+ c->c_nmethod++;
+ m->me_name = sel;
+ m->me_fun = (t_gotfn)fn;
+ nargs = 0;
+ while (argtype != A_NULL && nargs < MAXPDARG)
+ {
+ m->me_arg[nargs++] = argtype;
+ argtype = va_arg(ap, t_atomtype);
+ }
+ if (argtype != A_NULL)
+ error("%s_%s: only 5 arguments are typecheckable; use A_GIMME",
+ c->c_name->s_name, sel->s_name);
+ va_end(ap);
+ m->me_arg[nargs] = A_NULL;
+ }
+ return;
+phooey:
+ bug("class_addmethod: %s_%s: bad argument types\n",
+ c->c_name->s_name, sel->s_name);
+}
+
+ /* Instead of these, see the "class_addfloat", etc., macros in m_pd.h */
+void class_addbang(t_class *c, t_method fn)
+{
+ c->c_bangmethod = (t_bangmethod)fn;
+}
+
+void class_addpointer(t_class *c, t_method fn)
+{
+ c->c_pointermethod = (t_pointermethod)fn;
+}
+
+void class_doaddfloat(t_class *c, t_method fn)
+{
+ c->c_floatmethod = (t_floatmethod)fn;
+}
+
+void class_addsymbol(t_class *c, t_method fn)
+{
+ c->c_symbolmethod = (t_symbolmethod)fn;
+}
+
+void class_addlist(t_class *c, t_method fn)
+{
+ c->c_listmethod = (t_listmethod)fn;
+}
+
+void class_addanything(t_class *c, t_method fn)
+{
+ c->c_anymethod = (t_anymethod)fn;
+}
+
+void class_setwidget(t_class *c, t_widgetbehavior *w)
+{
+ c->c_wb = w;
+}
+
+void class_setparentwidget(t_class *c, t_parentwidgetbehavior *pw)
+{
+ c->c_pwb = pw;
+}
+
+char *class_getname(t_class *c)
+{
+ return (c->c_name->s_name);
+}
+
+char *class_gethelpname(t_class *c)
+{
+ return (c->c_helpname->s_name);
+}
+
+void class_sethelpsymbol(t_class *c, t_symbol *s)
+{
+ c->c_helpname = s;
+}
+
+t_parentwidgetbehavior *pd_getparentwidget(t_pd *x)
+{
+ return ((*x)->c_pwb);
+}
+
+void class_setdrawcommand(t_class *c)
+{
+ c->c_drawcommand = 1;
+}
+
+int class_isdrawcommand(t_class *c)
+{
+ return (c->c_drawcommand);
+}
+
+static void pd_floatforsignal(t_pd *x, t_float f)
+{
+ int offset = (*x)->c_floatsignalin;
+ if (offset > 0)
+ *(t_sample *)(((char *)x) + offset) = f;
+ else
+ pd_error(x, "%s: float unexpected for signal input",
+ (*x)->c_name->s_name);
+}
+
+void class_domainsignalin(t_class *c, int onset)
+{
+ if (onset <= 0) onset = -1;
+ else
+ {
+ if (c->c_floatmethod != pd_defaultfloat)
+ post("warning: %s: float method overwritten", c->c_name->s_name);
+ c->c_floatmethod = (t_floatmethod)pd_floatforsignal;
+ }
+ c->c_floatsignalin = onset;
+}
+
+/* ---------------- the symbol table ------------------------ */
+
+#define HASHSIZE 1024
+
+static t_symbol *symhash[HASHSIZE];
+
+t_symbol *dogensym(char *s, t_symbol *oldsym)
+{
+ t_symbol **sym1, *sym2;
+ unsigned int hash1 = 0, hash2 = 0;
+ int length = 0;
+ char *s2 = s;
+ while (*s2)
+ {
+ hash1 += *s2;
+ hash2 += hash1;
+ length++;
+ s2++;
+ }
+ sym1 = symhash + (hash2 & (HASHSIZE-1));
+ while (sym2 = *sym1)
+ {
+ if (!strcmp(sym2->s_name, s)) return(sym2);
+ sym1 = &sym2->s_next;
+ }
+ if (oldsym) sym2 = oldsym;
+ else
+ {
+ sym2 = (t_symbol *)t_getbytes(sizeof(*sym2));
+ sym2->s_name = t_getbytes(length+1);
+ sym2->s_next = 0;
+ sym2->s_thing = 0;
+ strcpy(sym2->s_name, s);
+ }
+ *sym1 = sym2;
+ return (sym2);
+}
+
+t_symbol *gensym(char *s)
+{
+ return(dogensym(s, 0));
+}
+
+static t_symbol *addfileextent(t_symbol *s)
+{
+ char namebuf[MAXPDSTRING], *str = s->s_name;
+ int ln = strlen(str);
+ if (!strcmp(str + ln - 3, ".pd")) return (s);
+ strcpy(namebuf, str);
+ strcpy(namebuf+ln, ".pd");
+ return (gensym(namebuf));
+}
+
+static int tryingalready;
+
+void canvas_popabstraction(t_canvas *x);
+extern t_pd *newest;
+
+t_symbol* pathsearch(t_symbol *s,char* ext);
+int pd_setloadingabstraction(t_symbol *sym);
+
+ /* this routine is called when a new "object" is requested whose class Pd
+ doesn't know. Pd tries to load it as an extern, then as an absteaction. */
+void new_anything(void *dummy, t_symbol *s, int argc, t_atom *argv)
+{
+ t_pd *current;
+ t_symbol *dir = canvas_getcurrentdir();
+ int fd;
+ char dirbuf[MAXPDSTRING], *nameptr;
+ if (tryingalready) return;
+ newest = 0;
+ class_loadsym = s;
+ if (sys_load_lib(dir->s_name, s->s_name))
+ {
+ tryingalready = 1;
+ typedmess(dummy, s, argc, argv);
+ tryingalready = 0;
+ return;
+ }
+ class_loadsym = 0;
+ current = s__X.s_thing;
+ if ((fd = open_via_path(dir->s_name, s->s_name, ".pd",
+ dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0)
+ {
+ close (fd);
+ if (!pd_setloadingabstraction(s))
+ {
+ canvas_setargs(argc, argv); /* bug fix by Krzysztof Czaja */
+ binbuf_evalfile(gensym(nameptr), gensym(dirbuf));
+ if (s__X.s_thing != current)
+ canvas_popabstraction((t_canvas *)(s__X.s_thing));
+ canvas_setargs(0, 0);
+ }
+ else error("%s: can't load abstraction within itself\n", s->s_name);
+ }
+ else newest = 0;
+}
+
+t_symbol s_pointer = {"pointer", 0, 0};
+t_symbol s_float = {"float", 0, 0};
+t_symbol s_symbol = {"symbol", 0, 0};
+t_symbol s_bang = {"bang", 0, 0};
+t_symbol s_list = {"list", 0, 0};
+t_symbol s_anything = {"anything", 0, 0};
+t_symbol s_signal = {"signal", 0, 0};
+t_symbol s__N = {"#N", 0, 0};
+t_symbol s__X = {"#X", 0, 0};
+t_symbol s_x = {"x", 0, 0};
+t_symbol s_y = {"y", 0, 0};
+t_symbol s_ = {"", 0, 0};
+
+static t_symbol *symlist[] = { &s_pointer, &s_float, &s_symbol, &s_bang,
+ &s_list, &s_anything, &s_signal, &s__N, &s__X, &s_x, &s_y, &s_};
+
+void mess_init(void)
+{
+ t_symbol **sp;
+ int i;
+
+ if (pd_objectmaker) return;
+ for (i = sizeof(symlist)/sizeof(*symlist), sp = symlist; i--; sp++)
+ (void) dogensym((*sp)->s_name, *sp);
+ pd_objectmaker = class_new(gensym("objectmaker"), 0, 0, sizeof(t_pd),
+ CLASS_DEFAULT, A_NULL);
+ pd_canvasmaker = class_new(gensym("classmaker"), 0, 0, sizeof(t_pd),
+ CLASS_DEFAULT, A_NULL);
+ pd_bind(&pd_canvasmaker, &s__N);
+ class_addanything(pd_objectmaker, (t_method)new_anything);
+}
+
+t_pd *newest;
+
+ /* horribly, we need prototypes for each of the artificial function
+ calls in typedmess(), to keep the compiler quiet. */
+typedef t_pd *(*t_newgimme)(t_symbol *s, int argc, t_atom *argv);
+typedef void(*t_messgimme)(t_pd *x, t_symbol *s, int argc, t_atom *argv);
+
+typedef t_pd *(*t_fun0)(
+ t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);
+typedef t_pd *(*t_fun1)(t_int i1,
+ t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);
+typedef t_pd *(*t_fun2)(t_int i1, t_int i2,
+ t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);
+typedef t_pd *(*t_fun3)(t_int i1, t_int i2, t_int i3,
+ t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);
+typedef t_pd *(*t_fun4)(t_int i1, t_int i2, t_int i3, t_int i4,
+ t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);
+typedef t_pd *(*t_fun5)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5,
+ t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);
+typedef t_pd *(*t_fun6)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5, t_int i6,
+ t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);
+
+void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_method *f;
+ t_class *c = *x;
+ t_methodentry *m;
+ t_atomtype *wp, wanttype;
+ int i;
+ t_int ai[MAXPDARG+1], *ap = ai;
+ t_floatarg ad[MAXPDARG+1], *dp = ad;
+ int narg = 0;
+ t_pd *bonzo;
+
+ /* check for messages that are handled by fixed slots in the class
+ structure. We don't catch "pointer" though so that sending "pointer"
+ to pd_objectmaker doesn't require that we supply a pointer value. */
+ if (s == &s_float)
+ {
+ if (!argc) (*c->c_floatmethod)(x, 0.);
+ else if (argv->a_type == A_FLOAT)
+ (*c->c_floatmethod)(x, argv->a_w.w_float);
+ else goto badarg;
+ return;
+ }
+ if (s == &s_bang)
+ {
+ (*c->c_bangmethod)(x);
+ return;
+ }
+ if (s == &s_list)
+ {
+ (*c->c_listmethod)(x, s, argc, argv);
+ return;
+ }
+ if (s == &s_symbol)
+ {
+ if (argc && argv->a_type == A_SYMBOL)
+ (*c->c_symbolmethod)(x, argv->a_w.w_symbol);
+ else
+ (*c->c_symbolmethod)(x, &s_);
+ return;
+ }
+ for (i = c->c_nmethod, m = c->c_methods; i--; m++)
+ if (m->me_name == s)
+ {
+ wp = m->me_arg;
+ if (*wp == A_GIMME)
+ {
+ if (x == &pd_objectmaker)
+ newest = (*((t_newgimme)(m->me_fun)))(s, argc, argv);
+ else (*((t_messgimme)(m->me_fun)))(x, s, argc, argv);
+ return;
+ }
+ if (argc > MAXPDARG) argc = MAXPDARG;
+ if (x != &pd_objectmaker) *(ap++) = (t_int)x, narg++;
+ while (wanttype = *wp++)
+ {
+ switch (wanttype)
+ {
+ case A_POINTER:
+ if (!argc) goto badarg;
+ else
+ {
+ if (argv->a_type == A_POINTER)
+ *ap = (t_int)(argv->a_w.w_gpointer);
+ else goto badarg;
+ argc--;
+ argv++;
+ }
+ narg++;
+ ap++;
+ break;
+ case A_FLOAT:
+ if (!argc) goto badarg;
+ case A_DEFFLOAT:
+ if (!argc) *dp = 0;
+ else
+ {
+ if (argv->a_type == A_FLOAT)
+ *dp = argv->a_w.w_float;
+ else goto badarg;
+ argc--;
+ argv++;
+ }
+ dp++;
+ break;
+ case A_SYMBOL:
+ if (!argc) goto badarg;
+ case A_DEFSYM:
+ if (!argc) *ap = (t_int)(&s_);
+ else
+ {
+ if (argv->a_type == A_SYMBOL)
+ *ap = (t_int)(argv->a_w.w_symbol);
+ /* if it's an unfilled "dollar" argument it appears
+ as zero here; cheat and bash it to the null
+ symbol. Unfortunately, this lets real zeros
+ pass as symbols too, which seems wrong... */
+ else if (x == &pd_objectmaker && argv->a_type == A_FLOAT
+ && argv->a_w.w_float == 0)
+ *ap = (t_int)(&s_);
+ else goto badarg;
+ argc--;
+ argv++;
+ }
+ narg++;
+ ap++;
+ }
+ }
+ switch (narg)
+ {
+ case 0 : bonzo = (*(t_fun0)(m->me_fun))
+ (ad[0], ad[1], ad[2], ad[3], ad[4]); break;
+ case 1 : bonzo = (*(t_fun1)(m->me_fun))
+ (ai[0], ad[0], ad[1], ad[2], ad[3], ad[4]); break;
+ case 2 : bonzo = (*(t_fun2)(m->me_fun))
+ (ai[0], ai[1], ad[0], ad[1], ad[2], ad[3], ad[4]); break;
+ case 3 : bonzo = (*(t_fun3)(m->me_fun))
+ (ai[0], ai[1], ai[2], ad[0], ad[1], ad[2], ad[3], ad[4]); break;
+ case 4 : bonzo = (*(t_fun4)(m->me_fun))
+ (ai[0], ai[1], ai[2], ai[3],
+ ad[0], ad[1], ad[2], ad[3], ad[4]); break;
+ case 5 : bonzo = (*(t_fun5)(m->me_fun))
+ (ai[0], ai[1], ai[2], ai[3], ai[4],
+ ad[0], ad[1], ad[2], ad[3], ad[4]); break;
+ case 6 : bonzo = (*(t_fun6)(m->me_fun))
+ (ai[0], ai[1], ai[2], ai[3], ai[4], ai[5],
+ ad[0], ad[1], ad[2], ad[3], ad[4]); break;
+ default: bonzo = 0;
+ }
+ if (x == &pd_objectmaker)
+ newest = bonzo;
+ return;
+ }
+ (*c->c_anymethod)(x, s, argc, argv);
+ return;
+badarg:
+ pd_error(x, "Bad arguments for message '%s' to object '%s'",
+ s->s_name, c->c_name->s_name);
+}
+
+void pd_vmess(t_pd *x, t_symbol *sel, char *fmt, ...)
+{
+ va_list ap;
+ t_atom arg[MAXPDARG], *at =arg;
+ int nargs = 0;
+ char *fp = fmt;
+
+ va_start(ap, fmt);
+ while (1)
+ {
+ if (nargs > MAXPDARG)
+ {
+ pd_error(x, "pd_vmess: only %d allowed", MAXPDARG);
+ break;
+ }
+ switch(*fp++)
+ {
+ case 'f': SETFLOAT(at, va_arg(ap, double)); break;
+ case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break;
+ case 'i': SETFLOAT(at, va_arg(ap, t_int)); break;
+ case 'p': SETPOINTER(at, va_arg(ap, t_gpointer *)); break;
+ default: goto done;
+ }
+ at++;
+ nargs++;
+ }
+done:
+ va_end(ap);
+ typedmess(x, sel, nargs, arg);
+}
+
+void pd_forwardmess(t_pd *x, int argc, t_atom *argv)
+{
+ if (argc)
+ {
+ t_atomtype t = argv->a_type;
+ if (t == A_SYMBOL) pd_typedmess(x, argv->a_w.w_symbol, argc-1, argv+1);
+ else if (t == A_POINTER)
+ {
+ if (argc == 1) pd_pointer(x, argv->a_w.w_gpointer);
+ else pd_list(x, &s_list, argc, argv);
+ }
+ else if (t == A_FLOAT)
+ {
+ if (argc == 1) pd_float(x, argv->a_w.w_float);
+ else pd_list(x, &s_list, argc, argv);
+ }
+ else bug("pd_forwardmess");
+ }
+
+}
+
+void nullfn(void) {}
+
+t_gotfn getfn(t_pd *x, t_symbol *s)
+{
+ t_class *c = *x;
+ t_methodentry *m;
+ int i;
+
+ for (i = c->c_nmethod, m = c->c_methods; i--; m++)
+ if (m->me_name == s) return(m->me_fun);
+ pd_error(x, "%s: no method for message '%s'", c->c_name->s_name, s->s_name);
+ return((t_gotfn)nullfn);
+}
+
+t_gotfn zgetfn(t_pd *x, t_symbol *s)
+{
+ t_class *c = *x;
+ t_methodentry *m;
+ int i;
+
+ for (i = c->c_nmethod, m = c->c_methods; i--; m++)
+ if (m->me_name == s) return(m->me_fun);
+ return(0);
+}
diff --git a/pd/src/m_conf.c b/pd/src/m_conf.c
new file mode 100644
index 00000000..04cddeda
--- /dev/null
+++ b/pd/src/m_conf.c
@@ -0,0 +1,100 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* changes by Thomas Musil IEM KUG Graz Austria 2001 */
+/* all changes are labeled with iemlib */
+
+#include "m_imp.h"
+
+void g_array_setup(void);
+void g_canvas_setup(void);
+void g_guiconnect_setup(void);
+/* iemlib */
+void g_bang_setup(void);
+void g_hdial_setup(void);
+void g_hslider_setup(void);
+void g_mycanvas_setup(void);
+void g_numbox_setup(void);
+void g_toggle_setup(void);
+void g_vdial_setup(void);
+void g_vslider_setup(void);
+void g_vumeter_setup(void);
+/* iemlib */
+void g_io_setup(void);
+void g_scalar_setup(void);
+void g_template_setup(void);
+void g_text_setup(void);
+void g_traversal_setup(void);
+void m_pd_setup(void);
+void x_acoustics_setup(void);
+void x_interface_setup(void);
+void x_connective_setup(void);
+void x_time_setup(void);
+void x_arithmetic_setup(void);
+void x_midi_setup(void);
+void x_misc_setup(void);
+void x_net_setup(void);
+void x_qlist_setup(void);
+void x_gui_setup(void);
+void d_arithmetic_setup(void);
+void d_array_setup(void);
+void d_ctl_setup(void);
+void d_dac_setup(void);
+void d_delay_setup(void);
+void d_fft_setup(void);
+void d_filter_setup(void);
+void d_global_setup(void);
+void d_math_setup(void);
+void d_misc_setup(void);
+void d_osc_setup(void);
+void d_soundfile_setup(void);
+void d_ugen_setup(void);
+
+void conf_init(void)
+{
+ g_array_setup();
+ g_canvas_setup();
+ g_guiconnect_setup();
+/* iemlib */
+ g_bang_setup();
+ g_hdial_setup();
+ g_hslider_setup();
+ g_mycanvas_setup();
+ g_numbox_setup();
+ g_toggle_setup();
+ g_vdial_setup();
+ g_vslider_setup();
+ g_vumeter_setup();
+/* iemlib */
+ g_io_setup();
+ g_scalar_setup();
+ g_template_setup();
+ g_text_setup();
+ g_traversal_setup();
+ m_pd_setup();
+ x_acoustics_setup();
+ x_interface_setup();
+ x_connective_setup();
+ x_time_setup();
+ x_arithmetic_setup();
+ x_midi_setup();
+ x_misc_setup();
+ x_net_setup();
+ x_qlist_setup();
+ x_gui_setup();
+ d_arithmetic_setup();
+ d_array_setup();
+ d_ctl_setup();
+ d_dac_setup();
+ d_delay_setup();
+ d_fft_setup();
+ d_filter_setup();
+ d_global_setup();
+ d_math_setup();
+ d_misc_setup();
+ d_osc_setup();
+ d_soundfile_setup();
+ d_ugen_setup();
+}
+
diff --git a/pd/src/m_glob.c b/pd/src/m_glob.c
new file mode 100644
index 00000000..3be209d6
--- /dev/null
+++ b/pd/src/m_glob.c
@@ -0,0 +1,121 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include "m_imp.h"
+
+static t_class *pdclass;
+static t_class *maxclass;
+
+/* These "glob" routines, which implement messages to Pd, are from all
+over. Some others are prototyped in m_imp.h as well. */
+
+void glob_setfilename(void *dummy, t_symbol *name, t_symbol *dir);
+void glob_quit(void *dummy);
+void glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv);
+void glob_meters(void *dummy, t_floatarg f);
+void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av);
+void glob_audiostatus(void *dummy);
+void glob_finderror(t_pd *dummy);
+void glob_ping(t_pd *dummy);
+
+#if 0 /* this doesn't work for OSS or for ALSA... discouraged... */
+
+#ifdef UNIX
+#include <unistd.h>
+#endif
+
+void glob_restart_audio(t_pd *dummy)
+{
+ int inchans = sys_get_inchannels();
+ int outchans = sys_get_outchannels();
+ post("1");
+ sys_close_audio();
+ post("2");
+#ifdef UNIX
+ sleep(2);
+#endif
+ post("3");
+ sys_open_audio(1, 0, 1, 0, /* IOhannes */
+ 1, &inchans, 1, &outchans, sys_dacsr);
+ post("4");
+}
+#endif
+
+void alsa_resync( void);
+
+
+#ifdef ALSA01
+void glob_restart_alsa(t_pd *dummy)
+{
+ alsa_resync();
+}
+#endif
+
+#ifdef NT
+void glob_audio(void *dummy, t_floatarg adc, t_floatarg dac);
+#endif
+
+/* a method you add for debugging printout */
+void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv);
+
+#if 0
+void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv)
+{
+ *(int *)1 = 3;
+}
+#endif
+
+void max_default(t_pd *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int i;
+ char str[80];
+ startpost("%s: unknown message %s ", class_getname(pd_class(x)),
+ s->s_name);
+ for (i = 0; i < argc; i++)
+ {
+ atom_string(argv+i, str, 80);
+ poststring(str);
+ }
+ endpost();
+}
+
+void glob_init(void)
+{
+ maxclass = class_new(gensym("max"), 0, 0, sizeof(t_pd),
+ CLASS_DEFAULT, A_NULL);
+ class_addanything(maxclass, max_default);
+ pd_bind(&maxclass, gensym("max"));
+
+ pdclass = class_new(gensym("pd"), 0, 0, sizeof(t_pd),
+ CLASS_DEFAULT, A_NULL);
+ class_addmethod(pdclass, (t_method)glob_initfromgui, gensym("init"),
+ A_GIMME, 0);
+ class_addmethod(pdclass, (t_method)glob_setfilename, gensym("filename"),
+ A_SYMBOL, A_SYMBOL, 0);
+ class_addmethod(pdclass, (t_method)glob_evalfile, gensym("open"),
+ A_SYMBOL, A_SYMBOL, 0);
+ class_addmethod(pdclass, (t_method)glob_quit, gensym("quit"), 0);
+ class_addmethod(pdclass, (t_method)glob_foo, gensym("foo"), A_GIMME, 0);
+ class_addmethod(pdclass, (t_method)glob_dsp, gensym("dsp"), A_GIMME, 0);
+ class_addmethod(pdclass, (t_method)glob_meters, gensym("meters"),
+ A_FLOAT, 0);
+ class_addmethod(pdclass, (t_method)glob_key, gensym("key"), A_GIMME, 0);
+ class_addmethod(pdclass, (t_method)glob_audiostatus,
+ gensym("audiostatus"), 0);
+ class_addmethod(pdclass, (t_method)glob_finderror,
+ gensym("finderror"), 0);
+#ifdef UNIX
+ class_addmethod(pdclass, (t_method)glob_ping, gensym("ping"), 0);
+#endif
+#ifdef NT
+ class_addmethod(pdclass, (t_method)glob_audio, gensym("audio"),
+ A_DEFFLOAT, A_DEFFLOAT, 0);
+#endif
+#ifdef ALSA01
+ class_addmethod(pdclass, (t_method)glob_restart_alsa,
+ gensym("restart-audio"), 0);
+#endif
+ class_addanything(pdclass, max_default);
+ pd_bind(&pdclass, gensym("pd"));
+}
diff --git a/pd/src/m_imp.h b/pd/src/m_imp.h
new file mode 100644
index 00000000..7a6bd69e
--- /dev/null
+++ b/pd/src/m_imp.h
@@ -0,0 +1,223 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* This file contains function prototypes and data types used to implement
+Pd, but not shared with Pd objects. */
+
+#include "m_pd.h"
+
+/* LATER consider whether to use 'char' for method arg types to save space */
+
+/* the structure for a method handler ala Max */
+typedef struct _methodentry
+{
+ t_symbol *me_name;
+ t_gotfn me_fun;
+ t_atomtype me_arg[MAXPDARG+1];
+} t_methodentry;
+
+EXTERN_STRUCT _widgetbehavior;
+
+typedef void (*t_bangmethod)(t_pd *x);
+typedef void (*t_pointermethod)(t_pd *x, t_gpointer *gp);
+typedef void (*t_floatmethod)(t_pd *x, t_float f);
+typedef void (*t_symbolmethod)(t_pd *x, t_symbol *s);
+typedef void (*t_listmethod)(t_pd *x, t_symbol *s, int argc, t_atom *argv);
+typedef void (*t_anymethod)(t_pd *x, t_symbol *s, int argc, t_atom *argv);
+
+struct _class
+{
+ t_symbol *c_name; /* name (mostly for error reporting) */
+ t_symbol *c_helpname; /* name of help file */
+ size_t c_size; /* size of an instance */
+ t_methodentry *c_methods; /* methods other than bang, etc below */
+ int c_nmethod; /* number of methods */
+ t_method c_freemethod; /* function to call before freeing */
+ t_bangmethod c_bangmethod; /* common methods */
+ t_pointermethod c_pointermethod;
+ t_floatmethod c_floatmethod;
+ t_symbolmethod c_symbolmethod;
+ t_listmethod c_listmethod;
+ t_anymethod c_anymethod;
+ struct _widgetbehavior *c_wb; /* "gobjs" only */
+ struct _parentwidgetbehavior *c_pwb;/* widget behavior in parent */
+ int c_floatsignalin; /* onset to float for signal input */
+ char c_gobj; /* true if is a gobj */
+ char c_patchable; /* true if we have a t_object header */
+ char c_firstin; /* if patchable, true if draw first inlet */
+ char c_drawcommand; /* a drawing command for a template */
+};
+
+/* s_file.c */
+typedef struct _namelist
+{
+ struct _namelist *nl_next;
+ char *nl_string;
+} t_namelist;
+
+t_namelist *namelist_append(t_namelist *listwas, const char *s);
+void namelist_free(t_namelist *listwas);
+
+extern int sys_debuglevel;
+extern int sys_verbose;
+
+#define DEBUG_MESSUP 1 /* messages up from pd to pd-gui */
+#define DEBUG_MESSDOWN 2 /* messages down from pd-gui to pd */
+
+extern int sys_noloadbang;
+extern int sys_nogui;
+extern char *sys_guicmd;
+
+/* in s_main.c */
+EXTERN int sys_nearestfontsize(int fontsize);
+EXTERN int sys_hostfontsize(int fontsize);
+
+extern int sys_defaultfont;
+extern t_symbol *sys_libdir; /* library directory for auxilliary files */
+/* s_loader.c */
+int sys_load_lib(char *dirname, char *filename);
+
+/* s_unix.c */
+EXTERN void sys_microsleep(int microsec);
+
+/* s_sgi.c, s_nt.c, s_linux.c each implement the same API for audio
+and MIDI I/O as follows: */
+
+#define DACBLKSIZE 64
+
+#define SENDDACS_NO 0 /* return values for sys_send_dacs() */
+#define SENDDACS_YES 1
+#define SENDDACS_SLEPT 2
+
+#define API_OSS 0 /* API choices */
+#define API_ALSA 1
+#define API_RME 2
+#define API_MMIO 3
+#define API_PORTAUDIO 4
+
+
+ /* MIDI input and output */
+#define MAXMIDIINDEV 16 /* max. number of input ports */
+#define MAXMIDIOUTDEV 16 /* max. number of output ports */
+extern int sys_nmidiin;
+extern int sys_nmidiout;
+extern int sys_midiindevlist[];
+extern int sys_midioutdevlist[];
+
+EXTERN void sys_putmidimess(int portno, int a, int b, int c);
+EXTERN void sys_putmidibyte(int portno, int a);
+EXTERN void sys_poll_midi(void);
+EXTERN void sys_setmiditimediff(double inbuftime, double outbuftime);
+EXTERN void sys_midibytein(int portno, int byte);
+
+extern int sys_hipriority; /* real-time flag, true if priority boosted */
+extern t_sample *sys_soundout;
+extern t_sample *sys_soundin;
+extern float sys_dacsr;
+extern int sys_schedadvance;
+extern int sys_sleepgrain;
+EXTERN void sys_open_audio(int naudioindev, int *audioindev, int nchindev, int *chindev,
+ int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev,
+ int srate); /* IOhannes */
+
+EXTERN void sys_close_audio(void);
+
+EXTERN void sys_open_midi(int nmidiin, int *midiinvec,
+ int nmidiout, int *midioutvec);
+EXTERN void sys_close_midi(void);
+
+EXTERN int sys_send_dacs(void);
+EXTERN void sys_reportidle(void);
+EXTERN void sys_set_priority(int higher);
+EXTERN void sys_audiobuf(int nbufs);
+EXTERN void sys_getmeters(float *inmax, float *outmax);
+EXTERN void sys_listdevs(void);
+EXTERN void sys_setblocksize(int n);
+
+ /* for NT and Linux, there are additional bits of fluff as follows. */
+#ifdef NT
+EXTERN void nt_soundindev(int which);
+EXTERN void nt_soundoutdev(int which);
+EXTERN void nt_midiindev(int which);
+EXTERN void nt_midioutdev(int which);
+EXTERN void nt_noresync( void);
+EXTERN void nt_set_sound_api(int which);
+#endif
+
+#ifdef __linux__
+ /* the following definitions allow you to switch at run time
+ between audio APIs in Linux and later in NT. */
+void linux_set_sound_api(int which);
+
+void linux_setfrags(int n);
+void linux_streammode( void);
+void linux_32bit( void);
+void rme_soundindev(int which);
+void rme_soundoutdev(int which);
+void linux_alsa_queue_size(int size);
+#ifdef ALSA99 /* old fashioned ALSA */
+void linux_alsa_devno(int devno);
+#else
+void linux_alsa_devname(char *devname);
+#endif
+#endif /* __linux__ */
+
+/* portaudio, used in Windows and Mac versions... */
+
+int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin,
+ t_sample *soundout, int framesperbuf, int nbuffers,
+ int indeviceno, int outdeviceno);
+void pa_close_audio(void);
+int pa_send_dacs(void);
+void pa_reportidle(void);
+void pa_listdevs(void);
+
+/* m_sched.c */
+EXTERN void sys_log_error(int type);
+#define ERR_NOTHING 0
+#define ERR_ADCSLEPT 1
+#define ERR_DACSLEPT 2
+#define ERR_RESYNC 3
+#define ERR_DATALATE 4
+
+/* s_inter.c */
+
+EXTERN void sys_bail(int exitcode);
+EXTERN int sys_pollgui(void);
+
+EXTERN_STRUCT _socketreceiver;
+#define t_socketreceiver struct _socketreceiver
+
+typedef void (*t_socketnotifier)(void *x);
+typedef void (*t_socketreceivefn)(void *x, t_binbuf *b);
+
+EXTERN t_socketreceiver *socketreceiver_new(void *owner,
+ t_socketnotifier notifier, t_socketreceivefn socketreceivefn, int udp);
+EXTERN void socketreceiver_read(t_socketreceiver *x, int fd);
+EXTERN void sys_sockerror(char *s);
+EXTERN void sys_closesocket(int fd);
+
+typedef void (*t_fdpollfn)(void *ptr, int fd);
+EXTERN void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr);
+EXTERN void sys_rmpollfn(int fd);
+#ifdef UNIX
+void sys_setalarm(int microsec);
+void sys_setvirtualalarm( void);
+#endif
+
+/* m_obj.c */
+EXTERN int obj_noutlets(t_object *x);
+EXTERN int obj_ninlets(t_object *x);
+EXTERN t_outconnect *obj_starttraverseoutlet(t_object *x, t_outlet **op,
+ int nout);
+EXTERN t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect,
+ t_object **destp, t_inlet **inletp, int *whichp);
+EXTERN t_outconnect *obj_connect(t_object *source, int outno,
+ t_object *sink, int inno);
+EXTERN void obj_disconnect(t_object *source, int outno, t_object *sink,
+ int inno);
+EXTERN void outlet_setstacklim(void);
+/* misc */
+EXTERN void glob_evalfile(t_pd *ignore, t_symbol *name, t_symbol *dir);
+EXTERN void glob_initfromgui(void *dummy, t_symbol *s, int argc, t_atom *argv);
diff --git a/pd/src/m_memory.c b/pd/src/m_memory.c
new file mode 100644
index 00000000..cb94e4b1
--- /dev/null
+++ b/pd/src/m_memory.c
@@ -0,0 +1,88 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include <stdlib.h>
+#include <string.h>
+#include "m_imp.h"
+
+/* #define LOUD */
+#ifdef LOUD
+#include <stdio.h>
+#endif
+
+/* #define DEBUGMEM */
+#ifdef DEBUGMEM
+static int totalmem = 0;
+#endif
+
+void *getbytes(size_t nbytes)
+{
+ void *ret;
+ if (nbytes < 1) nbytes = 1;
+ ret = (void *)calloc(nbytes, 1);
+#ifdef LOUD
+ fprintf(stderr, "new %x %d\n", (int)ret, nbytes);
+#endif /* LOUD */
+#ifdef DEBUGMEM
+ totalmem += nbytes;
+#endif
+ if (!ret)
+ post("pd: getbytes() failed -- out of memory");
+ return (ret);
+}
+
+void *getzbytes(size_t nbytes) /* obsolete name */
+{
+ return (getbytes(nbytes));
+}
+
+void *copybytes(void *src, size_t nbytes)
+{
+ void *ret;
+ ret = getbytes(nbytes);
+ if (nbytes)
+ memcpy(ret, src, nbytes);
+ return (ret);
+}
+
+void *resizebytes(void *old, size_t oldsize, size_t newsize)
+{
+ void *ret;
+ if (newsize < 1) newsize = 1;
+ if (oldsize < 1) oldsize = 1;
+ ret = (void *)realloc((char *)old, newsize);
+ if (newsize > oldsize && ret)
+ memset(((char *)ret) + oldsize, 0, newsize - oldsize);
+#ifdef LOUD
+ fprintf(stderr, "resize %x %d --> %x %d\n", (int)old, oldsize, (int)ret, newsize);
+#endif /* LOUD */
+#ifdef DEBUGMEM
+ totalmem += (newsize - oldsize);
+#endif
+ if (!ret)
+ post("pd: resizebytes() failed -- out of memory");
+ return (ret);
+}
+
+void freebytes(void *fatso, size_t nbytes)
+{
+ if (nbytes == 0)
+ nbytes = 1;
+#ifdef LOUD
+ fprintf(stderr, "free %x %d\n", (int)fatso, nbytes);
+#endif /* LOUD */
+#ifdef DEBUGMEM
+ totalmem -= nbytes;
+#endif
+ free(fatso);
+}
+
+#ifdef DEBUGMEM
+#include <stdio.h>
+
+void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv)
+{
+ fprintf(stderr, "total mem %d\n", totalmem);
+}
+#endif
diff --git a/pd/src/m_obj.c b/pd/src/m_obj.c
new file mode 100644
index 00000000..6b9ea932
--- /dev/null
+++ b/pd/src/m_obj.c
@@ -0,0 +1,676 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* this file handles Max-style patchable objects, i.e., objects which
+can interconnect via inlets and outlets; also, the (terse) generic
+behavior for "gobjs" appears at the end of this file. */
+
+#include "m_imp.h"
+
+union inletunion
+{
+ t_symbol *iu_symto;
+ t_gpointer *iu_pointerslot;
+ t_float *iu_floatslot;
+ t_symbol **iu_symslot;
+ t_sample iu_floatsignalvalue;
+};
+
+struct _inlet
+{
+ t_pd i_pd;
+ struct _inlet *i_next;
+ t_object *i_owner;
+ t_pd *i_dest;
+ t_symbol *i_symfrom;
+ union inletunion i_un;
+};
+
+#define i_symto i_un.iu_symto
+#define i_pointerslot i_un.iu_pointerslot
+#define i_floatslot i_un.iu_floatslot
+#define i_symslot i_un.iu_symslot
+
+static t_class *inlet_class, *pointerinlet_class, *floatinlet_class,
+ *symbolinlet_class;
+
+#define ISINLET(pd) ((*(pd) == inlet_class) || \
+ (*(pd) == pointerinlet_class) || \
+ (*(pd) == floatinlet_class) || \
+ (*(pd) == symbolinlet_class))
+
+/* --------------------- generic inlets ala max ------------------ */
+
+t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1, t_symbol *s2)
+{
+ t_inlet *x = (t_inlet *)pd_new(inlet_class), *y, *y2;
+ x->i_owner = owner;
+ x->i_dest = dest;
+ if (s1 == &s_signal)
+ x->i_un.iu_floatsignalvalue = 0;
+ else x->i_symto = s2;
+ x->i_symfrom = s1;
+ x->i_next = 0;
+ if (y = owner->ob_inlet)
+ {
+ while (y2 = y->i_next) y = y2;
+ y->i_next = x;
+ }
+ else owner->ob_inlet = x;
+ return (x);
+}
+
+static void inlet_wrong(t_inlet *x, t_symbol *s)
+{
+ pd_error(x->i_owner, "inlet: expected '%s' but got '%s'",
+ x->i_symfrom->s_name, s->s_name);
+}
+
+ /* LATER figure out how to make these efficient: */
+static void inlet_bang(t_inlet *x)
+{
+ if (x->i_symfrom == &s_bang)
+ pd_vmess(x->i_dest, x->i_symto, "");
+ else if (!x->i_symfrom) pd_bang(x->i_dest);
+ else inlet_wrong(x, &s_bang);
+}
+
+static void inlet_pointer(t_inlet *x, t_gpointer *gp)
+{
+ if (x->i_symfrom == &s_pointer)
+ pd_vmess(x->i_dest, x->i_symto, "p", gp);
+ else if (!x->i_symfrom) pd_pointer(x->i_dest, gp);
+ else inlet_wrong(x, &s_pointer);
+}
+
+static void inlet_float(t_inlet *x, t_float f)
+{
+ if (x->i_symfrom == &s_float)
+ pd_vmess(x->i_dest, x->i_symto, "f", (t_floatarg)f);
+ else if (x->i_symfrom == &s_signal)
+ x->i_un.iu_floatsignalvalue = f;
+ else if (!x->i_symfrom)
+ pd_float(x->i_dest, f);
+ else inlet_wrong(x, &s_float);
+}
+
+static void inlet_symbol(t_inlet *x, t_symbol *s)
+{
+ if (x->i_symfrom == &s_symbol)
+ pd_vmess(x->i_dest, x->i_symto, "s", s);
+ else if (!x->i_symfrom) pd_symbol(x->i_dest, s);
+ else inlet_wrong(x, &s_symbol);
+}
+
+static void inlet_list(t_inlet *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_atom at;
+ if (x->i_symfrom == &s_list || x->i_symfrom == &s_float
+ || x->i_symfrom == &s_symbol || x->i_symfrom == &s_pointer)
+ typedmess(x->i_dest, x->i_symto, argc, argv);
+ else if (!x->i_symfrom) pd_list(x->i_dest, s, argc, argv);
+ else inlet_wrong(x, &s_list);
+}
+
+static void inlet_anything(t_inlet *x, t_symbol *s, int argc, t_atom *argv)
+{
+ if (x->i_symfrom == s)
+ typedmess(x->i_dest, x->i_symto, argc, argv);
+ else if (!x->i_symfrom)
+ typedmess(x->i_dest, s, argc, argv);
+ else inlet_wrong(x, s);
+}
+
+void inlet_free(t_inlet *x)
+{
+ t_object *y = x->i_owner;
+ t_inlet *x2;
+ if (y->ob_inlet == x) y->ob_inlet = x->i_next;
+ else for (x2 = y->ob_inlet; x2; x2 = x2->i_next)
+ if (x2->i_next == x)
+ {
+ x2->i_next = x->i_next;
+ break;
+ }
+ t_freebytes(x, sizeof(*x));
+}
+
+/* ----- pointerinlets, floatinlets, syminlets: optimized inlets ------- */
+
+static void pointerinlet_pointer(t_inlet *x, t_gpointer *gp)
+{
+ gpointer_unset(x->i_pointerslot);
+ *(x->i_pointerslot) = *gp;
+ if (gp->gp_stub) gp->gp_stub->gs_refcount++;
+}
+
+t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp)
+{
+ t_inlet *x = (t_inlet *)pd_new(pointerinlet_class), *y, *y2;
+ x->i_owner = owner;
+ x->i_dest = 0;
+ x->i_symfrom = &s_pointer;
+ x->i_pointerslot = gp;
+ x->i_next = 0;
+ if (y = owner->ob_inlet)
+ {
+ while (y2 = y->i_next) y = y2;
+ y->i_next = x;
+ }
+ else owner->ob_inlet = x;
+ return (x);
+}
+
+static void floatinlet_float(t_inlet *x, t_float f)
+{
+ *(x->i_floatslot) = f;
+}
+
+t_inlet *floatinlet_new(t_object *owner, t_float *fp)
+{
+ t_inlet *x = (t_inlet *)pd_new(floatinlet_class), *y, *y2;
+ x->i_owner = owner;
+ x->i_dest = 0;
+ x->i_symfrom = &s_float;
+ x->i_floatslot = fp;
+ x->i_next = 0;
+ if (y = owner->ob_inlet)
+ {
+ while (y2 = y->i_next) y = y2;
+ y->i_next = x;
+ }
+ else owner->ob_inlet = x;
+ return (x);
+}
+
+static void symbolinlet_symbol(t_inlet *x, t_symbol *s)
+{
+ *(x->i_symslot) = s;
+}
+
+t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp)
+{
+ t_inlet *x = (t_inlet *)pd_new(symbolinlet_class), *y, *y2;
+ x->i_owner = owner;
+ x->i_dest = 0;
+ x->i_symfrom = &s_symbol;
+ x->i_symslot = sp;
+ x->i_next = 0;
+ if (y = owner->ob_inlet)
+ {
+ while (y2 = y->i_next) y = y2;
+ y->i_next = x;
+ }
+ else owner->ob_inlet = x;
+ return (x);
+}
+
+/* ---------------------- routine to handle lists ---------------------- */
+
+ /* objects interpret lists by feeding them to the individual inlets.
+ Before you call this check that the object doesn't have a more
+ specific way to handle lists. */
+void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_atom *ap;
+ int count;
+ t_inlet *ip = ((t_object *)x)->ob_inlet;
+ if (!argc) return;
+ for (count = argc-1, ap = argv+1; ip && count--; ap++, ip = ip->i_next)
+ {
+ if (ap->a_type == A_POINTER) pd_pointer(&ip->i_pd, ap->a_w.w_gpointer);
+ else if (ap->a_type == A_FLOAT) pd_float(&ip->i_pd, ap->a_w.w_float);
+ else pd_symbol(&ip->i_pd, ap->a_w.w_symbol);
+ }
+ if (argv->a_type == A_POINTER) pd_pointer(&x->ob_pd, argv->a_w.w_gpointer);
+ else if (argv->a_type == A_FLOAT) pd_float(&x->ob_pd, argv->a_w.w_float);
+ else pd_symbol(&x->ob_pd, argv->a_w.w_symbol);
+}
+
+void obj_init(void)
+{
+ inlet_class = class_new(gensym("inlet"), 0, 0,
+ sizeof(t_inlet), CLASS_PD, 0);
+ class_addbang(inlet_class, inlet_bang);
+ class_addpointer(inlet_class, inlet_pointer);
+ class_addfloat(inlet_class, inlet_float);
+ class_addsymbol(inlet_class, inlet_symbol);
+ class_addlist(inlet_class, inlet_list);
+ class_addanything(inlet_class, inlet_anything);
+
+ pointerinlet_class = class_new(gensym("inlet"), 0, 0,
+ sizeof(t_inlet), CLASS_PD, 0);
+ class_addpointer(pointerinlet_class, pointerinlet_pointer);
+
+ floatinlet_class = class_new(gensym("inlet"), 0, 0,
+ sizeof(t_inlet), CLASS_PD, 0);
+ class_addfloat(floatinlet_class, (t_method)floatinlet_float);
+
+ symbolinlet_class = class_new(gensym("inlet"), 0, 0,
+ sizeof(t_inlet), CLASS_PD, 0);
+ class_addsymbol(symbolinlet_class, symbolinlet_symbol);
+
+}
+
+/* --------------------------- outlets ------------------------------ */
+
+static char *stacklimit, *topstack;
+#define STACKSIZE 1000000
+static int outlet_eventno;
+
+ /* set a stack limit (on each incoming event that can set off messages)
+ for the outlet functions to check to prevent stack overflow from message
+ recursion */
+void outlet_setstacklim(void)
+{
+ char c;
+ topstack = &c;
+ stacklimit = (&c) - STACKSIZE;
+ outlet_eventno++;
+}
+
+ /* get a number unique to the (clock, MIDI, GUI, etc.) event we're on */
+int sched_geteventno( void)
+{
+ return (outlet_eventno);
+}
+
+struct _outconnect
+{
+ struct _outconnect *oc_next;
+ t_pd *oc_to;
+};
+
+struct _outlet
+{
+ t_object *o_owner;
+ struct _outlet *o_next;
+ t_outconnect *o_connections;
+ t_symbol *o_sym;
+};
+
+t_outlet *outlet_new(t_object *owner, t_symbol *s)
+{
+ t_outlet *x = (t_outlet *)getbytes(sizeof(*x)), *y, *y2;
+ x->o_owner = owner;
+ x->o_next = 0;
+ if (y = owner->ob_outlet)
+ {
+ while (y2 = y->o_next) y = y2;
+ y->o_next = x;
+ }
+ else owner->ob_outlet = x;
+ x->o_connections = 0;
+ x->o_sym = s;
+ return (x);
+}
+
+static void outlet_stackerror(t_outlet *x)
+{
+ pd_error(x->o_owner, "stack overflow");
+ stacklimit = topstack;
+}
+
+void outlet_bang(t_outlet *x)
+{
+ t_outconnect *oc;
+ char c;
+ if (&c < stacklimit)
+ outlet_stackerror(x);
+ else for (oc = x->o_connections; oc; oc = oc->oc_next)
+ pd_bang(oc->oc_to);
+}
+
+void outlet_pointer(t_outlet *x, t_gpointer *gp)
+{
+ t_outconnect *oc;
+ t_gpointer gpointer;
+ char c;
+ if (&c < stacklimit)
+ outlet_stackerror(x);
+ else
+ {
+#if 0
+ gpointer_copy(gp, &gpointer);
+ for (oc = x->o_connections; oc; oc = oc->oc_next)
+ pd_pointer(oc->oc_to, &gpointer);
+ gpointer_unset(&gpointer);
+#else
+ gpointer = *gp;
+ for (oc = x->o_connections; oc; oc = oc->oc_next)
+ pd_pointer(oc->oc_to, &gpointer);
+#endif
+ }
+}
+
+void outlet_float(t_outlet *x, t_float f)
+{
+ t_outconnect *oc;
+ char c;
+ if (&c < stacklimit)
+ outlet_stackerror(x);
+ else for (oc = x->o_connections; oc; oc = oc->oc_next)
+ pd_float(oc->oc_to, f);
+}
+
+void outlet_symbol(t_outlet *x, t_symbol *s)
+{
+ t_outconnect *oc;
+ char c;
+ if (&c < stacklimit)
+ outlet_stackerror(x);
+ else for (oc = x->o_connections; oc; oc = oc->oc_next)
+ pd_symbol(oc->oc_to, s);
+}
+
+void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_outconnect *oc;
+ char c;
+ if (&c < stacklimit)
+ outlet_stackerror(x);
+ else for (oc = x->o_connections; oc; oc = oc->oc_next)
+ pd_list(oc->oc_to, s, argc, argv);
+}
+
+void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_outconnect *oc;
+ char c;
+ if (&c < stacklimit)
+ outlet_stackerror(x);
+ else for (oc = x->o_connections; oc; oc = oc->oc_next)
+ typedmess(oc->oc_to, s, argc, argv);
+}
+
+void outlet_free(t_outlet *x)
+{
+ t_object *y = x->o_owner;
+ t_outlet *x2;
+ if (y->ob_outlet == x) y->ob_outlet = x->o_next;
+ else for (x2 = y->ob_outlet; x2; x2 = x2->o_next)
+ if (x2->o_next == x)
+ {
+ x2->o_next = x->o_next;
+ break;
+ }
+ t_freebytes(x, sizeof(*x));
+}
+
+t_outconnect *obj_connect(t_object *source, int outno,
+ t_object *sink, int inno)
+{
+ t_inlet *i;
+ t_outlet *o;
+ t_pd *to;
+ t_outconnect *oc, *oc2;
+
+ for (o = source->ob_outlet; o && outno; o = o->o_next, outno--) ;
+ if (!o) return (0);
+
+ if (sink->ob_pd->c_firstin)
+ {
+ if (!inno)
+ {
+ to = &sink->ob_pd;
+ goto doit;
+ }
+ else inno--;
+ }
+ for (i = sink->ob_inlet; i && inno; i = i->i_next, inno--) ;
+ if (!i) return (0);
+ to = &i->i_pd;
+doit:
+ oc = (t_outconnect *)t_getbytes(sizeof(*oc));
+ oc->oc_next = 0;
+ oc->oc_to = to;
+ /* append it to the end of the list */
+ /* LATER we might cache the last "oc" to make this faster. */
+ if ((oc2 = o->o_connections))
+ {
+ while (oc2->oc_next) oc2 = oc2->oc_next;
+ oc2->oc_next = oc;
+ }
+ else o->o_connections = oc;
+ if (o->o_sym == &s_signal) canvas_update_dsp();
+
+ return (oc);
+}
+
+void obj_disconnect(t_object *source, int outno, t_object *sink, int inno)
+{
+ t_inlet *i;
+ t_outlet *o;
+ t_pd *to;
+ t_outconnect *oc, *oc2;
+
+ for (o = source->ob_outlet; o && outno; o = o->o_next, outno--)
+ if (!o) return;
+ if (sink->ob_pd->c_firstin)
+ {
+ if (!inno)
+ {
+ to = &sink->ob_pd;
+ goto doit;
+ }
+ else inno--;
+ }
+ for (i = sink->ob_inlet; i && inno; i = i->i_next, inno--) ;
+ if (!i) return;
+ to = &i->i_pd;
+doit:
+ if (!(oc = o->o_connections)) return;
+ if (oc->oc_to == to)
+ {
+ o->o_connections = oc->oc_next;
+ freebytes(oc, sizeof(*oc));
+ goto done;
+ }
+ while (oc2 = oc->oc_next)
+ {
+ if (oc2->oc_to == to)
+ {
+ oc->oc_next = oc2->oc_next;
+ freebytes(oc2, sizeof(*oc2));
+ goto done;
+ }
+ oc = oc2;
+ }
+done:
+ if (o->o_sym == &s_signal) canvas_update_dsp();
+}
+
+/* ------ traversal routines for code that can't see our structures ------ */
+
+int obj_noutlets(t_object *x)
+{
+ int n;
+ t_outlet *o;
+ for (o = x->ob_outlet, n = 0; o; o = o->o_next) n++;
+ return (n);
+}
+
+int obj_ninlets(t_object *x)
+{
+ int n;
+ t_inlet *i;
+ for (i = x->ob_inlet, n = 0; i; i = i->i_next) n++;
+ if (x->ob_pd->c_firstin) n++;
+ return (n);
+}
+
+t_outconnect *obj_starttraverseoutlet(t_object *x, t_outlet **op, int nout)
+{
+ t_outlet *o = x->ob_outlet;
+ while (nout-- && o) o = o->o_next;
+ *op = o;
+ if (o) return (o->o_connections);
+ else return (0);
+}
+
+t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect,
+ t_object **destp, t_inlet **inletp, int *whichp)
+{
+ t_pd *y;
+ y = lastconnect->oc_to;
+ if (ISINLET(y))
+ {
+ int n;
+ t_inlet *i = (t_inlet *)y, *i2;
+ t_object *dest = i->i_owner;
+ for (n = dest->ob_pd->c_firstin, i2 = dest->ob_inlet;
+ i2 && i2 != i; i2 = i2->i_next) n++;
+ *whichp = n;
+ *destp = dest;
+ *inletp = i;
+ }
+ else
+ {
+ *whichp = 0;
+ *inletp = 0;
+ *destp = ((t_object *)y);
+ }
+ return (lastconnect->oc_next);
+}
+
+ /* this one checks that a pd is indeed a patchable object, and returns
+ it, correctly typed, or zero if the check failed. */
+t_object *pd_checkobject(t_pd *x)
+{
+ if ((*x)->c_patchable) return ((t_object *)x);
+ else return (0);
+}
+
+ /* move an inlet or outlet to the head of the list */
+void obj_moveinletfirst(t_object *x, t_inlet *i)
+{
+ t_inlet *i2;
+ if (x->ob_inlet == i) return;
+ else for (i2 = x->ob_inlet; i2; i2 = i2->i_next)
+ if (i2->i_next == i)
+ {
+ i2->i_next = i->i_next;
+ i->i_next = x->ob_inlet;
+ x->ob_inlet = i;
+ return;
+ }
+}
+
+void obj_moveoutletfirst(t_object *x, t_outlet *o)
+{
+ t_outlet *o2;
+ if (x->ob_outlet == o) return;
+ else for (o2 = x->ob_outlet; o2; o2 = o2->o_next)
+ if (o2->o_next == o)
+ {
+ o2->o_next = o->o_next;
+ o->o_next = x->ob_outlet;
+ x->ob_outlet = o;
+ return;
+ }
+}
+
+ /* routines for DSP sorting, which are used in d_ugen.c and g_canvas.c */
+ /* LATER try to consolidate all the slightly different routines. */
+
+int obj_nsiginlets(t_object *x)
+{
+ int n;
+ t_inlet *i;
+ for (i = x->ob_inlet, n = 0; i; i = i->i_next)
+ if (i->i_symfrom == &s_signal) n++;
+ if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin) n++;
+ return (n);
+}
+
+ /* get the index, among signal inlets, of the mth inlet overall */
+int obj_siginletindex(t_object *x, int m)
+{
+ int n = 0;
+ t_inlet *i;
+ if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin)
+ {
+ if (!m--) return (0);
+ n++;
+ }
+ for (i = x->ob_inlet; i; i = i->i_next, m--)
+ if (i->i_symfrom == &s_signal)
+ {
+ if (m == 0) return (n);
+ n++;
+ }
+ return (-1);
+}
+
+int obj_nsigoutlets(t_object *x)
+{
+ int n;
+ t_outlet *o;
+ for (o = x->ob_outlet, n = 0; o; o = o->o_next)
+ if (o->o_sym == &s_signal) n++;
+ return (n);
+}
+
+int obj_sigoutletindex(t_object *x, int m)
+{
+ int n;
+ t_outlet *o2;
+ for (o2 = x->ob_outlet, n = 0; o2; o2 = o2->o_next, m--)
+ if (o2->o_sym == &s_signal)
+ {
+ if (m == 0) return (n);
+ n++;
+ }
+ return (-1);
+}
+
+int obj_issignaloutlet(t_object *x, int m)
+{
+ int n;
+ t_outlet *o2;
+ for (o2 = x->ob_outlet, n = 0; o2 && m--; o2 = o2->o_next);
+ return (o2 && (o2->o_sym == &s_signal));
+}
+
+t_sample *obj_findsignalscalar(t_object *x, int m)
+{
+ int n = 0;
+ t_inlet *i;
+ if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin)
+ {
+ if (!m--)
+ return (x->ob_pd->c_floatsignalin > 0 ?
+ (t_sample *)(((char *)x) + x->ob_pd->c_floatsignalin) : 0);
+ n++;
+ }
+ for (i = x->ob_inlet; i; i = i->i_next, m--)
+ if (i->i_symfrom == &s_signal)
+ {
+ if (m == 0)
+ return (&i->i_un.iu_floatsignalvalue);
+ n++;
+ }
+ return (0);
+}
+
+/* and these are only used in g_io.c... */
+
+int inlet_getsignalindex(t_inlet *x)
+{
+ int n = 0;
+ t_inlet *i;
+ for (i = x->i_owner->ob_inlet, n = 0; i && i != x; i = i->i_next)
+ if (i->i_symfrom == &s_signal) n++;
+ return (n);
+}
+
+int outlet_getsignalindex(t_outlet *x)
+{
+ int n = 0;
+ t_outlet *o;
+ for (o = x->o_owner->ob_outlet, n = 0; o && o != x; o = o->o_next)
+ if (o->o_sym == &s_signal) n++;
+ return (n);
+}
+
diff --git a/pd/src/m_pd.c b/pd/src/m_pd.c
new file mode 100644
index 00000000..713d65ad
--- /dev/null
+++ b/pd/src/m_pd.c
@@ -0,0 +1,296 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include <stdlib.h>
+#include "m_imp.h"
+
+ /* FIXME no out-of-memory testing yet! */
+
+t_pd *pd_new(t_class *c)
+{
+ t_pd *x;
+ if (!c)
+ bug ("pd_new: apparently called before setup routine");
+ x = (t_pd *)t_getbytes(c->c_size);
+ *x = c;
+ if (c->c_patchable)
+ {
+ ((t_object *)x)->ob_inlet = 0;
+ ((t_object *)x)->ob_outlet = 0;
+ }
+ return (x);
+}
+
+void pd_free(t_pd *x)
+{
+ t_class *c = *x;
+ if (c->c_freemethod) (*(t_gotfn)(c->c_freemethod))(x);
+ if (c->c_patchable)
+ {
+ while (((t_object *)x)->ob_outlet)
+ outlet_free(((t_object *)x)->ob_outlet);
+ while (((t_object *)x)->ob_inlet)
+ inlet_free(((t_object *)x)->ob_inlet);
+ if (((t_object *)x)->ob_binbuf)
+ binbuf_free(((t_object *)x)->ob_binbuf);
+ }
+ if (c->c_size) t_freebytes(x, c->c_size);
+}
+
+/* deal with several objects bound to the same symbol. If more than one, we
+actually bind a collection object to the symbol, which forwards messages sent
+to the symbol. */
+
+static t_class *bindlist_class;
+
+typedef struct _bindelem
+{
+ t_pd *e_who;
+ struct _bindelem *e_next;
+} t_bindelem;
+
+typedef struct _bindlist
+{
+ t_pd b_pd;
+ t_bindelem *b_list;
+} t_bindlist;
+
+static void bindlist_bang(t_bindlist *x)
+{
+ t_bindelem *e;
+ for (e = x->b_list; e; e = e->e_next)
+ pd_bang(e->e_who);
+}
+
+static void bindlist_float(t_bindlist *x, t_float f)
+{
+ t_bindelem *e;
+ for (e = x->b_list; e; e = e->e_next)
+ pd_float(e->e_who, f);
+}
+
+static void bindlist_symbol(t_bindlist *x, t_symbol *s)
+{
+ t_bindelem *e;
+ for (e = x->b_list; e; e = e->e_next)
+ pd_symbol(e->e_who, s);
+}
+
+static void bindlist_pointer(t_bindlist *x, t_gpointer *gp)
+{
+ t_bindelem *e;
+ for (e = x->b_list; e; e = e->e_next)
+ pd_pointer(e->e_who, gp);
+}
+
+static void bindlist_list(t_bindlist *x, t_symbol *s,
+ int argc, t_atom *argv)
+{
+ t_bindelem *e;
+ for (e = x->b_list; e; e = e->e_next)
+ pd_list(e->e_who, s, argc, argv);
+}
+
+static void bindlist_anything(t_bindlist *x, t_symbol *s,
+ int argc, t_atom *argv)
+{
+ t_bindelem *e;
+ for (e = x->b_list; e; e = e->e_next)
+ pd_typedmess(e->e_who, s, argc, argv);
+}
+
+void m_pd_setup(void)
+{
+ bindlist_class = class_new(gensym("bindlist"), 0, 0,
+ sizeof(t_bindlist), CLASS_PD, 0);
+ class_addbang(bindlist_class, bindlist_bang);
+ class_addfloat(bindlist_class, (t_method)bindlist_float);
+ class_addsymbol(bindlist_class, bindlist_symbol);
+ class_addpointer(bindlist_class, bindlist_pointer);
+ class_addlist(bindlist_class, bindlist_list);
+ class_addanything(bindlist_class, bindlist_anything);
+}
+
+void pd_bind(t_pd *x, t_symbol *s)
+{
+ if (s->s_thing)
+ {
+ if (*s->s_thing == bindlist_class)
+ {
+ t_bindlist *b = (t_bindlist *)s->s_thing;
+ t_bindelem *e = (t_bindelem *)getbytes(sizeof(t_bindelem));
+ e->e_next = b->b_list;
+ e->e_who = x;
+ b->b_list = e;
+ }
+ else
+ {
+ t_bindlist *b = (t_bindlist *)pd_new(bindlist_class);
+ t_bindelem *e1 = (t_bindelem *)getbytes(sizeof(t_bindelem));
+ t_bindelem *e2 = (t_bindelem *)getbytes(sizeof(t_bindelem));
+ b->b_list = e1;
+ e1->e_who = x;
+ e1->e_next = e2;
+ e2->e_who = s->s_thing;
+ e2->e_next = 0;
+ s->s_thing = &b->b_pd;
+ }
+ }
+ else s->s_thing = x;
+}
+
+void pd_unbind(t_pd *x, t_symbol *s)
+{
+ if (s->s_thing == x) s->s_thing = 0;
+ else if (s->s_thing && *s->s_thing == bindlist_class)
+ {
+ /* bindlists always have at least two elements... if the number
+ goes down to one, get rid of the bindlist and bind the symbol
+ straight to the remaining element. */
+
+ t_bindlist *b = (t_bindlist *)s->s_thing;
+ t_bindelem *e, *e2;
+ if ((e = b->b_list)->e_who == x)
+ {
+ b->b_list = e->e_next;
+ freebytes(e, sizeof(t_bindelem));
+ }
+ else for (e = b->b_list; e2 = e->e_next; e = e2)
+ if (e2->e_who == x)
+ {
+ e->e_next = e2->e_next;
+ freebytes(e2, sizeof(t_bindelem));
+ break;
+ }
+ if (!b->b_list->e_next)
+ {
+ s->s_thing = b->b_list->e_who;
+ freebytes(b->b_list, sizeof(t_bindelem));
+ pd_free(&b->b_pd);
+ }
+ }
+ else pd_error(x, "%s: couldn't unbind", s->s_name);
+}
+
+void zz(void) {}
+
+t_pd *pd_findbyclass(t_symbol *s, t_class *c)
+{
+ t_pd *x = 0;
+
+ if (!s->s_thing) return (0);
+ if (*s->s_thing == c) return (s->s_thing);
+ if (*s->s_thing == bindlist_class)
+ {
+ t_bindlist *b = (t_bindlist *)s->s_thing;
+ t_bindelem *e, *e2;
+ int warned = 0;
+ for (e = b->b_list; e; e = e->e_next)
+ if (*e->e_who == c)
+ {
+ if (x && !warned)
+ {
+ zz();
+ post("warning: %s: multiply defined", s->s_name);
+ warned = 1;
+ }
+ x = e->e_who;
+ }
+ }
+ return x;
+}
+
+/* stack for maintaining bindings for the #X symbol during nestable loads.
+*/
+
+typedef struct _gstack
+{
+ t_pd *g_what;
+ t_symbol *g_loadingabstraction;
+ struct _gstack *g_next;
+} t_gstack;
+
+static t_gstack *gstack_head = 0;
+static t_pd *lastpopped;
+static t_symbol *pd_loadingabstraction;
+
+int pd_setloadingabstraction(t_symbol *sym)
+{
+ t_gstack *foo = gstack_head;
+ for (foo = gstack_head; foo; foo = foo->g_next)
+ if (foo->g_loadingabstraction == sym)
+ return (1);
+ pd_loadingabstraction = sym;
+ return (0);
+}
+
+void pd_pushsym(t_pd *x)
+{
+ t_gstack *y = (t_gstack *)t_getbytes(sizeof(*y));
+ y->g_what = s__X.s_thing;
+ y->g_next = gstack_head;
+ y->g_loadingabstraction = pd_loadingabstraction;
+ pd_loadingabstraction = 0;
+ gstack_head = y;
+ s__X.s_thing = x;
+}
+
+void pd_popsym(t_pd *x)
+{
+ if (!gstack_head || s__X.s_thing != x) bug("gstack_pop");
+ else
+ {
+ t_gstack *headwas = gstack_head;
+ s__X.s_thing = headwas->g_what;
+ gstack_head = headwas->g_next;
+ t_freebytes(headwas, sizeof(*headwas));
+ lastpopped = x;
+ }
+}
+
+void pd_doloadbang(void)
+{
+ if (lastpopped)
+ pd_vmess(lastpopped, gensym("loadbang"), "");
+ lastpopped = 0;
+}
+
+void pd_bang(t_pd *x)
+{
+ (*(*x)->c_bangmethod)(x);
+}
+
+void pd_float(t_pd *x, t_float f)
+{
+ (*(*x)->c_floatmethod)(x, f);
+}
+
+void pd_pointer(t_pd *x, t_gpointer *gp)
+{
+ (*(*x)->c_pointermethod)(x, gp);
+}
+
+void pd_symbol(t_pd *x, t_symbol *s)
+{
+ (*(*x)->c_symbolmethod)(x, s);
+}
+
+void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv)
+{
+ (*(*x)->c_listmethod)(x, &s_list, argc, argv);
+}
+
+void mess_init(void);
+void obj_init(void);
+void conf_init(void);
+void glob_init(void);
+
+void pd_init(void)
+{
+ mess_init();
+ obj_init();
+ conf_init();
+ glob_init();
+}
+
diff --git a/pd/src/m_pd.h b/pd/src/m_pd.h
new file mode 100644
index 00000000..172bf49d
--- /dev/null
+++ b/pd/src/m_pd.h
@@ -0,0 +1,594 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifdef NT
+// #pragma warning( disable : 4091 )
+#pragma warning( disable : 4305 ) /* uncast const double to float */
+#pragma warning( disable : 4244 ) /* uncast float/int conversion etc. */
+#pragma warning( disable : 4101 ) /* unused automatic variables */
+#endif /* NT */
+
+ /* the external storage class is "extern" in UNIX; in NT it's ugly. */
+#ifdef NT
+#ifdef PD_INTERNAL
+#define EXTERN __declspec(dllexport) extern
+#else
+#define EXTERN __declspec(dllimport) extern
+#endif /* PD_INTERNAL */
+#else
+#define EXTERN extern
+#endif /* NT */
+
+ /* and depending on the compiler, hidden data structures are
+ declared differently: */
+#ifdef __GNUC__
+#define EXTERN_STRUCT struct
+#else
+#define EXTERN_STRUCT extern struct
+#endif
+
+
+#if !defined(_SIZE_T) && !defined(_SIZE_T_)
+#include <stddef.h> /* just for size_t -- how lame! */
+#endif
+
+#define MAXPDSTRING 1000 /* use this for anything you want */
+#define MAXPDARG 5 /* max number of args we can typecheck today */
+
+ /* signed and unsigned integer types the size of a pointer: */
+#ifdef __alpha__
+typedef long t_int;
+#else
+typedef int t_int;
+#endif
+
+typedef float t_float; /* a floating-point number at most the same size */
+typedef float t_floatarg; /* floating-point type for function calls */
+
+typedef struct _symbol
+{
+ char *s_name;
+ struct _class **s_thing;
+ struct _symbol *s_next;
+} t_symbol;
+
+EXTERN_STRUCT _array;
+#define t_array struct _array /* g_canvas.h */
+
+/* pointers to glist and array elements go through a "stub" which sticks
+around after the glist or array is freed. The stub itself is deleted when
+both the glist/array is gone and the refcount is zero, ensuring that no
+gpointers are pointing here. */
+
+#define GP_NONE 0 /* the stub points nowhere (has been cut off) */
+#define GP_GLIST 1 /* the stub points to a glist element */
+#define GP_ARRAY 2 /* ... or array */
+
+typedef struct _gstub
+{
+ union
+ {
+ struct _glist *gs_glist; /* glist we're in */
+ struct _array *gs_array; /* array we're in */
+ } gs_un;
+ int gs_which; /* GP_GLIST/GP_ARRAY */
+ int gs_refcount; /* number of gpointers pointing here */
+} t_gstub;
+
+typedef struct _gpointer /* pointer to a gobj in a glist */
+{
+ union
+ {
+ struct _scalar *gp_scalar; /* scalar we're in (if glist) */
+ union word *gp_w; /* raw data (if array) */
+ } gp_un;
+ int gp_valid; /* number which must match gpointee */
+ t_gstub *gp_stub; /* stub which points to glist/array */
+} t_gpointer;
+
+typedef union word
+{
+ t_float w_float;
+ t_symbol *w_symbol;
+ t_gpointer *w_gpointer;
+ t_array *w_array;
+ struct _glist *w_list;
+ int w_index;
+} t_word;
+
+typedef enum
+{
+ A_NULL,
+ A_FLOAT,
+ A_SYMBOL,
+ A_POINTER,
+ A_SEMI,
+ A_COMMA,
+ A_DEFFLOAT,
+ A_DEFSYM,
+ A_DOLLAR,
+ A_DOLLSYM,
+ A_GIMME,
+ A_CANT
+} t_atomtype;
+
+#define A_DEFSYMBOL A_DEFSYM /* better name for this */
+
+typedef struct _atom
+{
+ t_atomtype a_type;
+ union word a_w;
+} t_atom;
+
+EXTERN_STRUCT _class;
+#define t_class struct _class
+
+EXTERN_STRUCT _outlet;
+#define t_outlet struct _outlet
+
+EXTERN_STRUCT _inlet;
+#define t_inlet struct _inlet
+
+EXTERN_STRUCT _binbuf;
+#define t_binbuf struct _binbuf
+
+EXTERN_STRUCT _clock;
+#define t_clock struct _clock
+
+EXTERN_STRUCT _outconnect;
+#define t_outconnect struct _outconnect
+
+EXTERN_STRUCT _glist;
+#define t_glist struct _glist
+#define t_canvas struct _glist /* LATER lose this */
+
+typedef t_class *t_pd; /* pure datum: nothing but a class pointer */
+
+typedef struct _gobj /* a graphical object */
+{
+ t_pd g_pd; /* pure datum header (class) */
+ struct _gobj *g_next; /* next in list */
+} t_gobj;
+
+typedef struct _scalar /* a graphical object holding data */
+{
+ t_gobj sc_gobj; /* header for graphical object */
+ t_symbol *sc_template; /* template name (LATER replace with pointer) */
+ t_word sc_vec[1]; /* indeterminate-length array of words */
+} t_scalar;
+
+typedef struct _text /* patchable object - graphical, with text */
+{
+ t_gobj te_g; /* header for graphical object */
+ t_binbuf *te_binbuf; /* holder for the text */
+ t_outlet *te_outlet; /* linked list of outlets */
+ t_inlet *te_inlet; /* linked list of inlets */
+ short te_xpix; /* x&y location (within the toplevel) */
+ short te_ypix;
+ short te_width; /* requested width in chars, 0 if auto */
+ unsigned int te_type:2; /* from defs below */
+} t_text;
+
+#define T_TEXT 0 /* just a textual comment */
+#define T_OBJECT 1 /* a MAX style patchable object */
+#define T_MESSAGE 2 /* a MAX stype message */
+#define T_ATOM 3 /* a cell to display a number or symbol */
+
+#define te_pd te_g.g_pd
+
+ /* t_object is synonym for t_text (LATER unify them) */
+
+typedef struct _text t_object;
+
+#define ob_outlet te_outlet
+#define ob_inlet te_inlet
+#define ob_binbuf te_binbuf
+#define ob_pd te_g.g_pd
+#define ob_g te_g
+
+typedef void (*t_method)(void);
+typedef void *(*t_newmethod)( void);
+typedef void (*t_gotfn)(void *x, ...);
+
+/* ---------------- pre-defined objects and symbols --------------*/
+EXTERN t_pd pd_objectmaker; /* factory for creating "object" boxes */
+EXTERN t_pd pd_canvasmaker; /* factory for creating canvases */
+EXTERN t_symbol s_pointer;
+EXTERN t_symbol s_float;
+EXTERN t_symbol s_symbol;
+EXTERN t_symbol s_bang;
+EXTERN t_symbol s_list;
+EXTERN t_symbol s_anything;
+EXTERN t_symbol s_signal;
+EXTERN t_symbol s__N;
+EXTERN t_symbol s__X;
+EXTERN t_symbol s_x;
+EXTERN t_symbol s_y;
+EXTERN t_symbol s_;
+
+/* --------- prototypes from the central message system ----------- */
+EXTERN void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv);
+EXTERN void pd_forwardmess(t_pd *x, int argc, t_atom *argv);
+EXTERN t_symbol *gensym(char *s);
+EXTERN t_gotfn getfn(t_pd *x, t_symbol *s);
+EXTERN t_gotfn zgetfn(t_pd *x, t_symbol *s);
+EXTERN void nullfn(void);
+EXTERN void pd_vmess(t_pd *x, t_symbol *s, char *fmt, ...);
+#define mess0(x, s) ((*getfn((x), (s)))((x)))
+#define mess1(x, s, a) ((*getfn((x), (s)))((x), (a)))
+#define mess2(x, s, a,b) ((*getfn((x), (s)))((x), (a),(b)))
+#define mess3(x, s, a,b,c) ((*getfn((x), (s)))((x), (a),(b),(c)))
+#define mess4(x, s, a,b,c,d) ((*getfn((x), (s)))((x), (a),(b),(c),(d)))
+#define mess5(x, s, a,b,c,d,e) ((*getfn((x), (s)))((x), (a),(b),(c),(d),(e)))
+void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv);
+
+/* --------------- memory management -------------------- */
+EXTERN void *getbytes(size_t nbytes);
+EXTERN void *getzbytes(size_t nbytes);
+EXTERN void *copybytes(void *src, size_t nbytes);
+EXTERN void freebytes(void *x, size_t nbytes);
+EXTERN void *resizebytes(void *x, size_t oldsize, size_t newsize);
+
+/* -------------------- atoms ----------------------------- */
+
+#define SETSEMI(atom) ((atom)->a_type = A_SEMI, (atom)->a_w.w_index = 0)
+#define SETCOMMA(atom) ((atom)->a_type = A_COMMA, (atom)->a_w.w_index = 0)
+#define SETPOINTER(atom, gp) ((atom)->a_type = A_POINTER, \
+ (atom)->a_w.w_gpointer = (gp))
+#define SETFLOAT(atom, f) ((atom)->a_type = A_FLOAT, (atom)->a_w.w_float = (f))
+#define SETSYMBOL(atom, s) ((atom)->a_type = A_SYMBOL, \
+ (atom)->a_w.w_symbol = (s))
+#define SETDOLLAR(atom, n) ((atom)->a_type = A_DOLLAR, \
+ (atom)->a_w.w_index = (n))
+#define SETDOLLSYM(atom, s) ((atom)->a_type = A_DOLLSYM, \
+ (atom)->a_w.w_symbol= (s))
+
+EXTERN t_float atom_getfloat(t_atom *a);
+EXTERN t_int atom_getint(t_atom *a);
+EXTERN t_symbol *atom_getsymbol(t_atom *a);
+EXTERN t_symbol *atom_gensym(t_atom *a);
+EXTERN t_float atom_getfloatarg(int which, int argc, t_atom *argv);
+EXTERN t_int atom_getintarg(int which, int argc, t_atom *argv);
+EXTERN t_symbol *atom_getsymbolarg(int which, int argc, t_atom *argv);
+
+EXTERN void atom_string(t_atom *a, char *buf, unsigned int bufsize);
+
+/* ------------------ binbufs --------------- */
+
+EXTERN t_binbuf *binbuf_new(void);
+EXTERN void binbuf_free(t_binbuf *x);
+
+EXTERN void binbuf_text(t_binbuf *x, char *text, size_t size);
+EXTERN void binbuf_gettext(t_binbuf *x, char **bufp, int *lengthp);
+EXTERN void binbuf_clear(t_binbuf *x);
+EXTERN void binbuf_add(t_binbuf *x, int argc, t_atom *argv);
+EXTERN void binbuf_addv(t_binbuf *x, char *fmt, ...);
+EXTERN void binbuf_addbinbuf(t_binbuf *x, t_binbuf *y);
+EXTERN void binbuf_addsemi(t_binbuf *x);
+EXTERN void binbuf_restore(t_binbuf *x, int argc, t_atom *argv);
+EXTERN void binbuf_print(t_binbuf *x);
+EXTERN int binbuf_getnatom(t_binbuf *x);
+EXTERN t_atom *binbuf_getvec(t_binbuf *x);
+EXTERN void binbuf_eval(t_binbuf *x, t_pd *target, int argc, t_atom *argv);
+EXTERN int binbuf_read(t_binbuf *b, char *filename, char *dirname,
+ int crflag);
+EXTERN int binbuf_read_via_path(t_binbuf *b, char *filename, char *dirname,
+ int crflag);
+EXTERN int binbuf_write(t_binbuf *x, char *filename, char *dir,
+ int crflag);
+EXTERN void binbuf_evalfile(t_symbol *name, t_symbol *dir);
+
+/* ------------------ clocks --------------- */
+
+EXTERN t_clock *clock_new(void *owner, t_method fn);
+EXTERN void clock_set(t_clock *x, double systime);
+EXTERN void clock_delay(t_clock *x, double delaytime);
+EXTERN void clock_unset(t_clock *x);
+EXTERN double clock_getlogicaltime(void);
+EXTERN double clock_getsystime(void); /* OBSOLETE; use clock_getlogicaltime() */
+EXTERN double clock_gettimesince(double prevsystime);
+EXTERN double clock_getsystimeafter(double delaytime);
+EXTERN void clock_free(t_clock *x);
+
+/* ----------------- pure data ---------------- */
+EXTERN t_pd *pd_new(t_class *cls);
+EXTERN void pd_free(t_pd *x);
+EXTERN void pd_bind(t_pd *x, t_symbol *s);
+EXTERN void pd_unbind(t_pd *x, t_symbol *s);
+EXTERN t_pd *pd_findbyclass(t_symbol *s, t_class *c);
+EXTERN void pd_pushsym(t_pd *x);
+EXTERN void pd_popsym(t_pd *x);
+EXTERN t_symbol *pd_getfilename(void);
+EXTERN t_symbol *pd_getdirname(void);
+EXTERN void pd_bang(t_pd *x);
+EXTERN void pd_pointer(t_pd *x, t_gpointer *gp);
+EXTERN void pd_float(t_pd *x, t_float f);
+EXTERN void pd_symbol(t_pd *x, t_symbol *s);
+EXTERN void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv);
+EXTERN void pd_anything(t_pd *x, t_symbol *s, int argc, t_atom *argv);
+#define pd_class(x) (*(x))
+
+/* ----------------- pointers ---------------- */
+EXTERN void gpointer_init(t_gpointer *gp);
+EXTERN void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto);
+EXTERN void gpointer_unset(t_gpointer *gp);
+EXTERN int gpointer_check(const t_gpointer *gp, int headok);
+
+/* ----------------- patchable "objects" -------------- */
+EXTERN_STRUCT _inlet;
+#define t_inlet struct _inlet
+EXTERN_STRUCT _outlet;
+#define t_outlet struct _outlet
+
+EXTERN t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1,
+ t_symbol *s2);
+EXTERN t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp);
+EXTERN t_inlet *floatinlet_new(t_object *owner, t_float *fp);
+EXTERN t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp);
+EXTERN void inlet_free(t_inlet *x);
+
+EXTERN t_outlet *outlet_new(t_object *owner, t_symbol *s);
+EXTERN void outlet_bang(t_outlet *x);
+EXTERN void outlet_pointer(t_outlet *x, t_gpointer *gp);
+EXTERN void outlet_float(t_outlet *x, t_float f);
+EXTERN void outlet_symbol(t_outlet *x, t_symbol *s);
+EXTERN void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv);
+EXTERN void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv);
+EXTERN void outlet_free(t_outlet *x);
+EXTERN t_object *pd_checkobject(t_pd *x);
+
+
+/* -------------------- canvases -------------- */
+
+EXTERN void glob_setfilename(void *dummy, t_symbol *name, t_symbol *dir);
+
+EXTERN void canvas_setargs(int argc, t_atom *argv);
+EXTERN t_atom *canvas_getarg(int which);
+EXTERN t_symbol *canvas_getcurrentdir(void);
+EXTERN t_glist *canvas_getcurrent(void);
+EXTERN void canvas_makefilename(t_glist *c, char *file,
+ char *result,int resultsize);
+EXTERN t_symbol *canvas_getdir(t_glist *x);
+EXTERN int sys_fontwidth(int fontsize);
+EXTERN int sys_fontheight(int fontsize);
+EXTERN void canvas_dataproperties(t_glist *x, t_scalar *sc, t_binbuf *b);
+
+/* ---------------- widget behaviors ---------------------- */
+
+EXTERN_STRUCT _widgetbehavior;
+#define t_widgetbehavior struct _widgetbehavior
+
+EXTERN_STRUCT _parentwidgetbehavior;
+#define t_parentwidgetbehavior struct _parentwidgetbehavior
+EXTERN t_parentwidgetbehavior *pd_getparentwidget(t_pd *x);
+
+/* -------------------- classes -------------- */
+
+#define CLASS_DEFAULT 0 /* flags for new classes below */
+#define CLASS_PD 1
+#define CLASS_GOBJ 2
+#define CLASS_PATCHABLE 3
+#define CLASS_NOINLET 8
+
+#define CLASS_TYPEMASK 3
+
+
+EXTERN t_class *class_new(t_symbol *name, t_newmethod newmethod,
+ t_method freemethod, size_t size, int flags, t_atomtype arg1, ...);
+EXTERN void class_addcreator(t_newmethod newmethod, t_symbol *s,
+ t_atomtype type1, ...);
+EXTERN void class_addmethod(t_class *c, t_method fn, t_symbol *sel,
+ t_atomtype arg1, ...);
+EXTERN void class_addbang(t_class *c, t_method fn);
+EXTERN void class_addpointer(t_class *c, t_method fn);
+EXTERN void class_doaddfloat(t_class *c, t_method fn);
+EXTERN void class_addsymbol(t_class *c, t_method fn);
+EXTERN void class_addlist(t_class *c, t_method fn);
+EXTERN void class_addanything(t_class *c, t_method fn);
+EXTERN void class_sethelpsymbol(t_class *c, t_symbol *s);
+EXTERN void class_setwidget(t_class *c, t_widgetbehavior *w);
+EXTERN void class_setparentwidget(t_class *c, t_parentwidgetbehavior *w);
+EXTERN t_parentwidgetbehavior *class_parentwidget(t_class *c);
+EXTERN char *class_getname(t_class *c);
+EXTERN char *class_gethelpname(t_class *c);
+EXTERN void class_setdrawcommand(t_class *c);
+EXTERN int class_isdrawcommand(t_class *c);
+EXTERN void class_domainsignalin(t_class *c, int onset);
+#define CLASS_MAINSIGNALIN(c, type, field) \
+ class_domainsignalin(c, (char *)(&((type *)0)->field) - (char *)0)
+
+#ifndef PD_CLASS_DEF
+#define class_addbang(x, y) class_addbang((x), (t_method)(y))
+#define class_addpointer(x, y) class_addpointer((x), (t_method)(y))
+#define class_addfloat(x, y) class_doaddfloat((x), (t_method)(y))
+#define class_addsymbol(x, y) class_addsymbol((x), (t_method)(y))
+#define class_addlist(x, y) class_addlist((x), (t_method)(y))
+#define class_addanything(x, y) class_addanything((x), (t_method)(y))
+#endif
+
+/* ------------ printing --------------------------------- */
+EXTERN void post(char *fmt, ...);
+EXTERN void startpost(char *fmt, ...);
+EXTERN void poststring(char *s);
+EXTERN void postfloat(float f);
+EXTERN void postatom(int argc, t_atom *argv);
+EXTERN void endpost(void);
+EXTERN void error(char *fmt, ...);
+EXTERN void bug(char *fmt, ...);
+EXTERN void pd_error(void *object, char *fmt, ...);
+EXTERN void sys_logerror(char *object, char *s);
+EXTERN void sys_unixerror(char *object);
+EXTERN void sys_ouch(void);
+
+#ifdef __linux__
+EXTERN char* sys_get_path( void);
+#endif
+EXTERN void sys_addpath(const char* p);
+
+
+/* ------------ system interface routines ------------------- */
+EXTERN int sys_isreadablefile(const char *name);
+EXTERN void sys_bashfilename(const char *from, char *to);
+EXTERN void sys_unbashfilename(const char *from, char *to);
+EXTERN int open_via_path(const char *name, const char *ext, const char *dir,
+ char *dirresult, char **nameresult, unsigned int size, int bin);
+EXTERN int sys_geteventno(void);
+EXTERN double sys_getrealtime(void);
+
+/* --------------- signals ----------------------------------- */
+
+typedef float t_sample;
+#define MAXLOGSIG 32
+#define MAXSIGSIZE (1 << MAXLOGSIG)
+
+typedef struct _signal
+{
+ int s_n; /* number of points in the array */
+ t_sample *s_vec; /* the array */
+ float s_sr; /* sample rate */
+ int s_refcount; /* number of times used */
+ int s_isborrowed; /* whether we're going to borrow our array */
+ struct _signal *s_borrowedfrom; /* signal to borrow it from */
+ struct _signal *s_nextfree; /* next in freelist */
+ struct _signal *s_nextused; /* next in used list */
+} t_signal;
+
+
+typedef t_int *(*t_perfroutine)(t_int *args);
+
+EXTERN t_int *plus_perform(t_int *args);
+EXTERN t_int *zero_perform(t_int *args);
+EXTERN t_int *copy_perform(t_int *args);
+
+EXTERN void dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n);
+EXTERN void dsp_add_copy(t_sample *in, t_sample *out, int n);
+EXTERN void dsp_add_scalarcopy(t_sample *in, t_sample *out, int n);
+EXTERN void dsp_add_zero(t_sample *out, int n);
+
+EXTERN int sys_getblksize(void);
+EXTERN float sys_getsr(void);
+EXTERN int sys_get_inchannels(void);
+EXTERN int sys_get_outchannels(void);
+
+EXTERN void dsp_add(t_perfroutine f, int n, ...);
+EXTERN void dsp_addv(t_perfroutine f, int n, t_int *vec);
+EXTERN void pd_fft(float *buf, int npoints, int inverse);
+EXTERN int ilog2(int n);
+
+EXTERN void mayer_fht(float *fz, int n);
+EXTERN void mayer_fft(int n, float *real, float *imag);
+EXTERN void mayer_ifft(int n, float *real, float *imag);
+EXTERN void mayer_realfft(int n, float *real);
+EXTERN void mayer_realifft(int n, float *real);
+
+EXTERN float *cos_table;
+#define LOGCOSTABSIZE 9
+#define COSTABSIZE (1<<LOGCOSTABSIZE)
+
+EXTERN int canvas_suspend_dsp(void);
+EXTERN void canvas_resume_dsp(int oldstate);
+EXTERN void canvas_update_dsp(void);
+
+/* IOhannes { (up/downsampling) */
+typedef struct _resample
+{
+ int method; /* up/downsampling method ID */
+
+ t_int downsample; /* downsampling factor */
+ t_int upsample; /* upsampling factor */
+
+ t_float *s_vec; /* here we hold the resampled data */
+ int s_n;
+
+ t_float *coeffs; /* coefficients for filtering... */
+ int coefsize;
+
+ t_float *buffer; /* buffer for filtering */
+ int bufsize;
+} t_resample;
+
+EXTERN void resample_init(t_resample *x);
+EXTERN void resample_free(t_resample *x);
+
+EXTERN void resample_dsp(t_resample *x, t_sample *in, int insize, t_sample *out, int outsize, int method);
+EXTERN void resamplefrom_dsp(t_resample *x, t_sample *in, int insize, int outsize, int method);
+EXTERN void resampleto_dsp(t_resample *x, t_sample *out, int insize, int outsize, int method);
+/* } IOhannes */
+
+/* ----------------------- utility functions for signals -------------- */
+EXTERN float mtof(float);
+EXTERN float ftom(float);
+EXTERN float rmstodb(float);
+EXTERN float powtodb(float);
+EXTERN float dbtorms(float);
+EXTERN float dbtopow(float);
+
+EXTERN float q8_sqrt(float);
+EXTERN float q8_rsqrt(float);
+#ifndef N32
+EXTERN float qsqrt(float); /* old names kept for extern compatibility */
+EXTERN float qrsqrt(float);
+#endif
+/* --------------------- data --------------------------------- */
+
+ /* graphical arrays */
+EXTERN_STRUCT _garray;
+#define t_garray struct _garray
+
+EXTERN t_class *garray_class;
+EXTERN int garray_getfloatarray(t_garray *x, int *size, t_float **vec);
+EXTERN float garray_get(t_garray *x, t_symbol *s, t_int indx);
+EXTERN void garray_redraw(t_garray *x);
+EXTERN int garray_npoints(t_garray *x);
+EXTERN char *garray_vec(t_garray *x);
+EXTERN void garray_resize(t_garray *x, t_floatarg f);
+EXTERN void garray_usedindsp(t_garray *x);
+EXTERN void garray_setsaveit(t_garray *x, int saveit);
+EXTERN t_class *scalar_class;
+
+EXTERN t_float *value_get(t_symbol *s);
+EXTERN void value_release(t_symbol *s);
+EXTERN int value_getfloat(t_symbol *s, t_float *f);
+EXTERN int value_setfloat(t_symbol *s, t_float f);
+
+/* ------- GUI interface - functions to send strings to TK --------- */
+EXTERN void sys_vgui(char *fmt, ...);
+EXTERN void sys_gui(char *s);
+
+EXTERN void gfxstub_new(t_pd *owner, void *key, const char *cmd);
+EXTERN void gfxstub_deleteforkey(void *key);
+
+/*------------- Max 0.26 compatibility --------------------*/
+
+/* the following reflects the new way classes are laid out, with the class
+ pointing to the messlist and not vice versa. Externs shouldn't feel it. */
+typedef t_class *t_externclass;
+
+EXTERN void c_extern(t_externclass *cls, t_newmethod newroutine,
+ t_method freeroutine, t_symbol *name, size_t size, int tiny, \
+ t_atomtype arg1, ...);
+EXTERN void c_addmess(t_method fn, t_symbol *sel, t_atomtype arg1, ...);
+
+#define t_getbytes getbytes
+#define t_freebytes freebytes
+#define t_resizebytes resizebytes
+#define typedmess pd_typedmess
+#define vmess pd_vmess
+
+#ifdef MACOSX
+#define cabs() smerdyakov(void)
+#endif
+
+/* A definition to help gui objects straddle 0.34-0.35 changes. If this is
+defined, there is a "te_xpix" field in objects, not a "te_xpos" as before: */
+
+#define PD_USE_TE_XPIX
+
+#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus)
+}
+#endif
diff --git a/pd/src/m_sched.c b/pd/src/m_sched.c
new file mode 100644
index 00000000..514e6f8b
--- /dev/null
+++ b/pd/src/m_sched.c
@@ -0,0 +1,462 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* scheduling stuff */
+
+#include "m_imp.h"
+
+ /* LATER consider making this variable. It's now the LCM of all sample
+ rates we expect to see: 32000, 44100, 48000, 88200, 96000. */
+#define TIMEUNITPERSEC (32.*441000.)
+
+static int sys_quit;
+static double sys_time;
+static double sys_time_per_dsp_tick;
+static double sys_time_per_msec;
+
+int sys_usecsincelastsleep(void);
+int sys_sleepgrain;
+
+typedef void (*t_clockmethod)(void *client);
+
+struct _clock
+{
+ double c_settime;
+ void *c_owner;
+ t_clockmethod c_fn;
+ struct _clock *c_next;
+};
+
+t_clock *clock_setlist;
+
+#ifdef UNIX
+#include <unistd.h>
+#endif
+
+t_clock *clock_new(void *owner, t_method fn)
+{
+ t_clock *x = (t_clock *)getbytes(sizeof *x);
+ x->c_settime = -1;
+ x->c_owner = owner;
+ x->c_fn = (t_clockmethod)fn;
+ x->c_next = 0;
+ return (x);
+}
+
+void clock_unset(t_clock *x)
+{
+ if (x->c_settime >= 0)
+ {
+ if (x == clock_setlist) clock_setlist = x->c_next;
+ else
+ {
+ t_clock *x2 = clock_setlist;
+ while (x2->c_next != x) x2 = x2->c_next;
+ x2->c_next = x->c_next;
+ }
+ x->c_settime = -1;
+ }
+}
+
+ /* set the clock to call back at an absolute system time */
+void clock_set(t_clock *x, double setticks)
+{
+ if (setticks < sys_time) setticks = sys_time;
+ clock_unset(x);
+ x->c_settime = setticks;
+ if (clock_setlist && clock_setlist->c_settime <= setticks)
+ {
+ t_clock *cbefore, *cafter;
+ for (cbefore = clock_setlist, cafter = clock_setlist->c_next;
+ cbefore; cbefore = cafter, cafter = cbefore->c_next)
+ {
+ if (!cafter || cafter->c_settime > setticks)
+ {
+ cbefore->c_next = x;
+ x->c_next = cafter;
+ return;
+ }
+ }
+ }
+ else x->c_next = clock_setlist, clock_setlist = x;
+}
+
+ /* set the clock to call back after a delay in msec */
+void clock_delay(t_clock *x, double delaytime)
+{
+ clock_set(x, sys_time + sys_time_per_msec * delaytime);
+}
+
+ /* get current logical time. We don't specify what units this is in;
+ use clock_gettimesince() to measure intervals from time of this call.
+ This was previously, incorrectly named "clock_getsystime"; the old
+ name is aliased to the new one in m_pd.h. */
+double clock_getlogicaltime( void)
+{
+ return (sys_time);
+}
+ /* OBSOLETE NAME */
+double clock_getsystime( void) { return (sys_time); }
+
+ /* elapsed time in milliseconds since the given system time */
+double clock_gettimesince(double prevsystime)
+{
+ return ((sys_time - prevsystime)/sys_time_per_msec);
+}
+
+ /* what value the system clock will have after a delay */
+double clock_getsystimeafter(double delaytime)
+{
+ return (sys_time + sys_time_per_msec * delaytime);
+}
+
+void clock_free(t_clock *x)
+{
+ clock_unset(x);
+ freebytes(x, sizeof *x);
+}
+
+/* the following routines maintain a real-execution-time histogram of the
+various phases of real-time execution. */
+
+static int sys_bin[] = {0, 2, 5, 10, 20, 30, 50, 100, 1000};
+#define NBIN (sizeof(sys_bin)/sizeof(*sys_bin))
+#define NHIST 10
+static int sys_histogram[NHIST][NBIN];
+static double sys_histtime;
+static int sched_diddsp, sched_didmidi, sched_didpoll, sched_didnothing;
+
+static void sys_clearhist( void)
+{
+ unsigned int i, j;
+ for (i = 0; i < NHIST; i++)
+ for (j = 0; j < NBIN; j++) sys_histogram[i][j] = 0;
+ sys_histtime = sys_getrealtime();
+ sched_diddsp = sched_didmidi = sched_didpoll = sched_didnothing = 0;
+}
+
+void sys_printhist( void)
+{
+ unsigned int i, j;
+ for (i = 0; i < NHIST; i++)
+ {
+ int doit = 0;
+ for (j = 0; j < NBIN; j++) if (sys_histogram[i][j]) doit = 1;
+ if (doit)
+ {
+ post("%2d %8d %8d %8d %8d %8d %8d %8d %8d", i,
+ sys_histogram[i][0],
+ sys_histogram[i][1],
+ sys_histogram[i][2],
+ sys_histogram[i][3],
+ sys_histogram[i][4],
+ sys_histogram[i][5],
+ sys_histogram[i][6],
+ sys_histogram[i][7]);
+ }
+ }
+ post("dsp %d, midi %d, poll %d, nothing %d",
+ sched_diddsp, sched_didmidi, sched_didpoll, sched_didnothing);
+}
+
+static int sys_histphase;
+
+int sys_addhist(int phase)
+{
+ int i, j, phasewas = sys_histphase;
+ double newtime = sys_getrealtime();
+ int msec = (newtime - sys_histtime) * 1000.;
+ for (j = NBIN-1; j >= 0; j--)
+ {
+ if (msec >= sys_bin[j])
+ {
+ sys_histogram[phasewas][j]++;
+ break;
+ }
+ }
+ sys_histtime = newtime;
+ sys_histphase = phase;
+ return (phasewas);
+}
+
+#define NRESYNC 20
+
+typedef struct _resync
+{
+ int r_ntick;
+ int r_error;
+} t_resync;
+
+static int oss_resyncphase = 0;
+static int oss_nresync = 0;
+static t_resync oss_resync[NRESYNC];
+
+#ifdef __linux__
+void linux_audiostatus(void);
+#endif
+
+static char *(oss_errornames[]) = {
+"unknown",
+"ADC blocked",
+"DAC blocked",
+"A/D/A sync",
+"data late"
+};
+
+void glob_audiostatus(void)
+{
+ int dev, nresync, nresyncphase, i;
+#ifdef __linux__
+ linux_audiostatus();
+#endif
+ nresync = (oss_nresync >= NRESYNC ? NRESYNC : oss_nresync);
+ nresyncphase = oss_resyncphase - 1;
+ post("audio I/O error history:");
+ post("seconds ago\terror type");
+ for (i = 0; i < nresync; i++)
+ {
+ int errtype;
+ if (nresyncphase < 0)
+ nresyncphase += NRESYNC;
+ errtype = oss_resync[nresyncphase].r_error;
+ if (errtype < 0 || errtype > 4)
+ errtype = 0;
+
+ post("%9.2f\t%s",
+ (sched_diddsp - oss_resync[nresyncphase].r_ntick)
+ * ((double)DACBLKSIZE) / sys_dacsr,
+ oss_errornames[errtype]);
+ nresyncphase--;
+ }
+}
+
+static int sched_diored;
+static int sched_dioredtime;
+static int sched_meterson;
+
+void sys_log_error(int type)
+{
+ oss_resync[oss_resyncphase].r_ntick = sched_diddsp;
+ oss_resync[oss_resyncphase].r_error = type;
+ oss_nresync++;
+ if (++oss_resyncphase == NRESYNC) oss_resyncphase = 0;
+ if (type != ERR_NOTHING && !sched_diored)
+ {
+ sys_vgui("pdtk_pd_dio 1\n");
+ sched_diored = 1;
+ }
+ sched_dioredtime =
+ sched_diddsp + (int)(sys_dacsr /(double)DACBLKSIZE);
+}
+
+static int sched_lastinclip, sched_lastoutclip,
+ sched_lastindb, sched_lastoutdb;
+
+void glob_ping(t_pd *dummy);
+
+static void sched_pollformeters( void)
+{
+ int inclip, outclip, indb, outdb;
+ static int sched_nextmeterpolltime, sched_nextpingtime;
+
+ /* if there's no GUI but we're running in "realtime", here is
+ where we arrange to ping the watchdog every 2 seconds. */
+#ifdef UNIX
+ if (sys_nogui && sys_hipriority && (sched_diddsp - sched_nextpingtime > 0))
+ {
+ glob_ping(0);
+ /* ping every 2 seconds */
+ sched_nextpingtime = sched_diddsp +
+ 2 * (int)(sys_dacsr /(double)DACBLKSIZE);
+ }
+#endif
+
+ if (sched_diddsp - sched_nextmeterpolltime < 0)
+ return;
+ if (sched_diored && (sched_diddsp - sched_dioredtime > 0))
+ {
+ sys_vgui("pdtk_pd_dio 0\n");
+ sched_diored = 0;
+ }
+ if (sched_meterson)
+ {
+ float inmax, outmax;
+ sys_getmeters(&inmax, &outmax);
+ indb = 0.5 + rmstodb(inmax);
+ outdb = 0.5 + rmstodb(outmax);
+ inclip = (inmax > 0.999);
+ outclip = (outmax >= 1.0);
+ }
+ else
+ {
+ indb = outdb = 0;
+ inclip = outclip = 0;
+ }
+ if (inclip != sched_lastinclip || outclip != sched_lastoutclip
+ || indb != sched_lastindb || outdb != sched_lastoutdb)
+ {
+ sys_vgui("pdtk_pd_meters %d %d %d %d\n", indb, outdb, inclip, outclip);
+ sched_lastinclip = inclip;
+ sched_lastoutclip = outclip;
+ sched_lastindb = indb;
+ sched_lastoutdb = outdb;
+ }
+ sched_nextmeterpolltime =
+ sched_diddsp + (int)(sys_dacsr /(double)DACBLKSIZE);
+}
+
+void glob_meters(void *dummy, float f)
+{
+ if (f == 0)
+ sys_getmeters(0, 0);
+ sched_meterson = (f != 0);
+ sched_lastinclip = sched_lastoutclip = sched_lastindb = sched_lastoutdb =
+ -1;
+}
+
+#if 1
+void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv)
+{
+ if (argc) sys_clearhist();
+ else sys_printhist();
+}
+#endif
+
+void dsp_tick(void);
+
+static int m_nodacs = 0;
+
+ /* this must be called earlier than any patches are loaded */
+void m_schedsetsr( void)
+{
+ sys_time_per_dsp_tick =
+ (TIMEUNITPERSEC) * ((double)DACBLKSIZE) / sys_dacsr;
+ sys_time_per_msec =
+ TIMEUNITPERSEC / 1000.;
+}
+
+/*
+Here is Pd's "main loop." This routine dispatches clock timeouts and DSP
+"ticks" deterministically, and polls for input from MIDI and the GUI. If
+we're left idle we also poll for graphics updates; but these are considered
+lower priority than the rest.
+
+The time source is normally the audio I/O subsystem via the "sys_send_dacs()"
+call. This call returns true if samples were transferred; false means that
+the audio I/O system is still bussy with previous transfers.
+The sys_send_dacs call is OS dependent and is variously implemented in
+s_linux.c, s_nt.c, and s_sgi.c.
+*/
+
+void sys_pollmidiqueue( void);
+void sys_initmidiqueue( void);
+
+int m_scheduler(int nodacs)
+{
+ int lasttimeforward = SENDDACS_YES;
+ int idlecount = 0;
+ double lastdactime = 0;
+ sys_clearhist();
+ m_nodacs = nodacs;
+ if (sys_sleepgrain < 1000)
+ sys_sleepgrain = (sys_schedadvance >= 4000?
+ (sys_schedadvance >> 2) : 1000);
+ sys_initmidiqueue();
+ while (1)
+ {
+ int didsomething = 0;
+ int timeforward;
+
+ sys_addhist(0);
+ if (m_nodacs)
+ {
+ double elapsed = sys_getrealtime() - lastdactime;
+ static double next = 0;
+ if (elapsed > next)
+ {
+ timeforward = SENDDACS_YES;
+ next += (double)DACBLKSIZE / sys_dacsr;
+ }
+ else timeforward = SENDDACS_NO;
+ }
+ else
+ {
+ timeforward = sys_send_dacs();
+
+ /* if dacs remain "idle" for 1 sec, they're hung up. */
+ if (timeforward != 0) idlecount = 0;
+ else
+ {
+ idlecount++;
+ if (!(idlecount & 31))
+ {
+ static double idletime;
+ /* on 32nd idle, start a clock watch; every
+ 32 ensuing idles, check it */
+ if (idlecount == 32)
+ idletime = sys_getrealtime();
+ else if (sys_getrealtime() - idletime > 1.)
+ {
+ post("audio I/O stuck... closing audio\n");
+ m_nodacs = 1;
+ sys_close_audio();
+ lastdactime = sys_getrealtime();
+ }
+ }
+ }
+ }
+ sys_setmiditimediff(0, 1e-6 * sys_schedadvance);
+ lasttimeforward = timeforward;
+ sys_addhist(1);
+ if (timeforward != SENDDACS_NO)
+ {
+ /* time has moved forward. Check MIDI and clocks */
+
+ double next_sys_time = sys_time + sys_time_per_dsp_tick;
+ int countdown = 5000;
+ while (clock_setlist && clock_setlist->c_settime < next_sys_time)
+ {
+ t_clock *c = clock_setlist;
+ sys_time = c->c_settime;
+ clock_unset(clock_setlist);
+ outlet_setstacklim();
+ (*c->c_fn)(c->c_owner);
+ if (!countdown--)
+ {
+ countdown = 5000;
+ sys_pollgui();
+ }
+ }
+ sys_time = next_sys_time;
+ if (sys_quit) break;
+ dsp_tick();
+ if (timeforward != SENDDACS_SLEPT)
+ didsomething = 1;
+ sched_diddsp++;
+ }
+
+ sys_addhist(2);
+ sys_pollmidiqueue();
+ if (sys_pollgui())
+ {
+ if (!didsomething)
+ sched_didpoll++;
+ didsomething = 1;
+ }
+ sys_addhist(3);
+ /* test for idle; if so, do graphics updates. */
+ if (!didsomething)
+ {
+ sched_pollformeters();
+ sys_reportidle();
+ if (timeforward != SENDDACS_SLEPT)
+ sys_microsleep(sys_sleepgrain);
+ sys_addhist(5);
+ sched_didnothing++;
+ }
+ }
+ return (0);
+}
+
+
diff --git a/pd/src/makefile b/pd/src/makefile
new file mode 100644
index 00000000..62e5f34b
--- /dev/null
+++ b/pd/src/makefile
@@ -0,0 +1,3 @@
+all:
+ ./configure
+ make
diff --git a/pd/src/makefile.clean b/pd/src/makefile.clean
new file mode 100644
index 00000000..62e5f34b
--- /dev/null
+++ b/pd/src/makefile.clean
@@ -0,0 +1,3 @@
+all:
+ ./configure
+ make
diff --git a/pd/src/makefile.dependencies b/pd/src/makefile.dependencies
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pd/src/makefile.dependencies
diff --git a/pd/src/makefile.in b/pd/src/makefile.in
new file mode 100644
index 00000000..b7eb1104
--- /dev/null
+++ b/pd/src/makefile.in
@@ -0,0 +1,233 @@
+#
+#
+#
+
+VPATH = ../obj:./
+OBJ_DIR = ../obj
+BIN_DIR = ../bin
+PDEXEC = $(BIN_DIR)/pd
+EXT= @EXT@
+GUINAME= @GUINAME@
+
+INSTALL_PREFIX = @prefix@
+GFLAGS = -DINSTALL_PREFIX=\"$(INSTALL_PREFIX)\"
+
+# ALSA compilation
+
+SOUND_ALSA = @alsa@
+
+# RME compilation
+
+SOUND_RME = @rme@
+
+DEFINES = @DEFINES@
+MORECFLAGS = @MORECFLAGS@
+
+INCLUDE = -I.
+GINCLUDE = $(INCLUDE) @GUIFLAGS@
+GLIB = @LIBS@
+
+LDFLAGS = @LDFLAGS@
+LIB = @PDLIB@
+
+#select either the DBG and OPT compiler flags below:
+OPT_CFLAGS = @OPT_CFLAGS@
+WARN_CFLAGS = -Wall -W -Wstrict-prototypes -Werror \
+ -Wno-unused -Wno-parentheses -Wno-switch
+ARCH_CFLAGS = -DPD -DUNIX
+
+CFLAGS = $(ARCH_CFLAGS) $(WARN_CFLAGS) $(OPT_CFLAGS) $(DEFINES) $(MORECFLAGS)
+
+# you might want ALSA linked in non-shared because
+# many Linux machines don't have the ALSA shared library. To link
+# ALSA non-shared, move the # sign below.
+
+ifeq (${SOUND_ALSA},yes)
+CFLAGS += -DALSA01
+#LIB += /usr/lib/libasound.a
+#LIB += -lasound
+endif
+
+ifeq (${SOUND_ALSA},old)
+CFLAGS += -DALSA99
+#LIB += /usr/lib/libasound.a
+#LIB += -lasound
+endif
+
+ifeq (${SOUND_RME},yes)
+CFLAGS += -DRME_HAMMERFALL
+endif
+
+
+# Which system
+
+SYSTEM = $(shell uname -m)
+
+ifeq (${SYSTEM},alpha)
+#LIB += -lffm -lm
+CFLAGS += -mieee -mcpu=ev56
+endif
+
+# Which compiler
+
+ifeq (${CC},ccc)
+CFLAGS += -g3 -D__COMPAQC__ -arch host
+endif
+
+# the sources
+
+SYSSRC = @SYSSRC@
+
+SRC = g_canvas.c g_graph.c g_text.c g_rtext.c g_array.c g_template.c g_io.c \
+ g_scalar.c g_traversal.c g_guiconnect.c g_readwrite.c g_editor.c \
+ g_all_guis.c g_bang.c g_hdial.c g_hslider.c g_mycanvas.c g_numbox.c \
+ g_toggle.c g_vdial.c g_vslider.c g_vumeter.c \
+ m_pd.c m_class.c m_obj.c m_atom.c m_memory.c m_binbuf.c \
+ m_conf.c m_glob.c m_sched.c \
+ s_main.c s_inter.c s_unix.c s_file.c s_print.c \
+ s_loader.c s_path.c s_entry.c \
+ d_ugen.c d_ctl.c d_arithmetic.c d_osc.c d_filter.c d_dac.c d_misc.c \
+ d_math.c d_fft.c d_mayer_fft.c d_fftroutine.c d_array.c d_global.c \
+ d_delay.c d_resample.c \
+ x_arithmetic.c x_connective.c x_interface.c x_midi.c x_misc.c \
+ x_time.c x_acoustics.c x_net.c x_qlist.c x_gui.c d_soundfile.c \
+ $(SYSSRC)
+
+
+OBJ = $(SRC:.c=.o)
+EXTERNS = ../extra/*/*.$(EXT)
+
+GSRC = t_main.c t_tkcmd.c
+
+GOBJ = $(GSRC:.c=.o)
+
+#
+# ------------------ targets ------------------------------------
+#
+
+.PHONY: pd gui externs
+
+all: $(PDEXEC) $(BIN_DIR)/pd-watchdog $(BIN_DIR)/$(GUINAME) $(BIN_DIR)/pdsend \
+ $(BIN_DIR)/pdreceive $(BIN_DIR)/pd.tk externs
+
+bin: $(PDEXEC) $(BIN_DIR)/pd-watchdog $(BIN_DIR)/$(GUINAME) $(BIN_DIR)/pdsend \
+ $(BIN_DIR)/pdreceive $(BIN_DIR)/pd.tk
+
+$(OBJ) : %.o : %.c
+ $(CC) $(CFLAGS) $(GFLAGS) $(INCLUDE) -c -o $(OBJ_DIR)/$*.o $*.c
+
+$(GOBJ) : %.o : %.c
+ $(CC) $(CFLAGS) $(GFLAGS) $(GINCLUDE) -c -o $(OBJ_DIR)/$*.o $*.c
+
+pd: $(PDEXEC)
+
+gui: $(BIN_DIR)/$(GUINAME)
+
+pd-watchdog: $(BIN_DIR)/pd-watchdog
+
+$(BIN_DIR)/pd-watchdog: s_watchdog.c
+ cc -O2 $(STRIPFLAG) -o $(BIN_DIR)/pd-watchdog s_watchdog.c
+
+$(BIN_DIR)/pdsend: u_pdsend.c
+ cc $(CFLAGS) $(STRIPFLAG) -o $(BIN_DIR)/pdsend u_pdsend.c
+
+$(BIN_DIR)/pdreceive: u_pdreceive.c
+ cc $(CFLAGS) $(STRIPFLAG) -o $(BIN_DIR)/pdreceive u_pdreceive.c
+
+$(PDEXEC): $(OBJ)
+ cd ../obj; $(CC) $(LDFLAGS) $(DBG_CFLAGS) -o $(PDEXEC) $(OBJ) \
+ $(LIB)
+
+$(BIN_DIR)/pd-gui: $(GOBJ) $(GSRC)
+ cd ../obj; $(CC) $(INCLUDE) -o $(BIN_DIR)/$(GUINAME) $(GOBJ) \
+ $(GLIB)
+
+$(BIN_DIR)/pd.tk: u_main.tk
+ echo set pd_nt @OSNUMBER@ > $(BIN_DIR)/pd.tk
+ grep -v "set pd_nt" < u_main.tk >> $(BIN_DIR)/pd.tk
+
+#this is for Max OSX only...
+$(BIN_DIR)/pdtcl: $(GOBJ) $(GSRC)
+ cd ../obj; libtool -dynamic -o $(BIN_DIR)/pdtcl $(GOBJ) \
+ /Library/Frameworks/Tk.framework/Versions/Current/Tk \
+ /Library/Frameworks/Tcl.framework/Versions/Current/Tcl \
+ /usr/lib/libSystem.B.dylib
+
+externs:
+ cd ../extra/bonk~;make @EXTERNTARGET@
+ cd ../extra/choice;make @EXTERNTARGET@
+ cd ../extra/expr~;make @EXTERNTARGET@
+ cd ../extra/fiddle~;make @EXTERNTARGET@
+ cd ../extra/loop~;make @EXTERNTARGET@
+ cd ../extra/lrshift~;make @EXTERNTARGET@
+ cd ../extra/paf~;make @EXTERNTARGET@
+ cd ../extra/pique;make @EXTERNTARGET@
+
+INSTDIR = $(DESTDIR)/$(INSTALL_PREFIX)
+install:
+ install -d $(INSTDIR)/lib/pd/bin
+ install $(BIN_DIR)/$(GUINAME) $(INSTDIR)/lib/pd/bin/$(GUINAME)
+ install $(BIN_DIR)/pd-watchdog $(INSTDIR)/lib/pd/bin/pd-watchdog
+ install -m644 $(BIN_DIR)/pd.tk $(INSTDIR)/lib/pd/bin/pd.tk
+ install -d $(INSTDIR)/bin
+ install -m755 $(PDEXEC) $(INSTDIR)/bin/pd
+ install -m 755 $(BIN_DIR)/pdsend $(INSTDIR)/bin/pdsend
+ install -m 755 $(BIN_DIR)/pdreceive $(INSTDIR)/bin/pdreceive
+ install -d $(INSTDIR)/lib/pd/extra
+ install -d $(INSTDIR)/lib/pd/externs
+ install -m 644 $(EXTERNS) $(INSTDIR)/lib/pd/extra
+ cp -r ../doc $(INSTDIR)/lib/pd/
+ install -d $(INSTDIR)/include
+ install -m644 m_pd.h $(INSTDIR)/include/m_pd.h
+ gzip < ../man/pd.1 > $(INSTDIR)/man/man1/pd.1.gz
+ chmod 644 $(INSTDIR)/man/man1/pd.1.gz
+ gzip < ../man/pdsend.1 > $(INSTDIR)/man/man1/pdsend.1.gz
+ chmod 644 $(INSTDIR)/man/man1/pdsend.1.gz
+ gzip < ../man/pdreceive.1 > $(INSTDIR)/man/man1/pdreceive.1.gz
+ chmod 644 $(INSTDIR)/man/man1/pdreceive.1.gz
+
+local-clean:
+ -rm -f ../obj/* $(BIN_DIR)/pd $(BIN_DIR)/$(GUINAME) $(BIN_DIR)/pdsend \
+ $(BIN_DIR)/pdreceive $(BIN_DIR)/pd-watchdog m_stamp.c
+ -rm -f *~
+ -rm -f $(BIN_DIR)/pdsend $(BIN_DIR)/pdreceive
+ -(cd ../doc/6.externs; rm -f *.pd_linux)
+ -rm -f makefile.dependencies
+ touch makefile.dependencies
+ chmod 666 makefile.dependencies
+
+extra-clean:
+ -rm -f `find ../extra/ -name "*.pd_*"`
+ -rm -f tags
+
+clean: extra-clean local-clean
+
+distclean: clean
+ -rm config.cache config.log config.status makefile tags
+ echo all: > makefile
+ echo -e "\t./configure" >> makefile
+ echo -e "\tmake" >> makefile
+
+tags: $(SRC) $(GSRC); ctags *.[ch]
+
+depend:
+ $(CC) $(INCLUDE) $(CFLAGS) -M $(SRC) > makefile.dependencies
+
+uninstall:
+ -rm -r $(INSTDIR)/lib/pd
+ -rm $(INSTDIR)/bin/pd
+ -rm $(INSTDIR)/bin/pdsend
+ -rm $(INSTDIR)/bin/pdreceive
+ -rm $(INSTDIR)/include/m_pd.h
+ -rm $(INSTDIR)/man/man1/pd.1.gz
+ -rm $(INSTDIR)/man/man1/pdsend.1.gz
+ -rm $(INSTDIR)/man/man1/pdreceive.1.gz
+
+include makefile.dependencies
+
+
+
+
+
+
+
diff --git a/pd/src/makefile.irix b/pd/src/makefile.irix
new file mode 100644
index 00000000..07975f04
--- /dev/null
+++ b/pd/src/makefile.irix
@@ -0,0 +1,65 @@
+# these can be altered from the command line to create an N32 version:
+# make EXECUTABLE=../bin/pd-n32 \
+XF1="-n32 -DN32 -woff 1080,1064,1185 -Ofast=ip32" \
+XF2="-OPT:cray_ivdep=true -r10000 -OPT:roundoff=3 -OPT:IEEE_arithmetic=3" pd
+
+EXECUTABLE=../bin/pd
+XF1=-o32 -fullwarn -O2
+XF2=
+all: $(EXECUTABLE) ../bin/pd-gui ../bin/pd.tk
+
+VPATH=../obj
+
+INCLUDE = -I. -I../../../irix/tk/generic -I../../../irix/tcl/generic
+GLIB = ../tk/unix/libtk8.0.a ../tcl/unix/libtcl8.0.a -lm -lX11
+LIB = -laudio -lmd -lm
+CFLAGS = -DUNIX -DIRIX -DPD $(XF1) $(XF2)
+LDFLAGS = $(XF1) $(XF2)
+
+SYSSRC = s_sgi.c
+
+SRC = g_canvas.c g_graph.c g_text.c g_rtext.c g_array.c g_template.c g_io.c \
+ g_scalar.c g_traversal.c g_guiconnect.c g_readwrite.c g_editor.c \
+ g_all_guis.c g_bang.c g_hdial.c g_hslider.c g_mycanvas.c g_numbox.c \
+ g_toggle.c g_vdial.c g_vslider.c g_vumeter.c \
+ m_pd.c m_class.c m_obj.c m_atom.c m_memory.c m_binbuf.c \
+ m_conf.c m_glob.c m_sched.c \
+ s_main.c s_inter.c s_unix.c s_file.c s_print.c \
+ s_loader.c s_path.c s_entry.c \
+ d_ugen.c d_ctl.c d_arithmetic.c d_osc.c d_filter.c d_dac.c d_misc.c \
+ d_math.c d_fft.c d_mayer_fft.c d_fftroutine.c d_array.c d_global.c \
+ d_delay.c d_resample.c \
+ x_arithmetic.c x_connective.c x_interface.c x_midi.c x_misc.c \
+ x_time.c x_acoustics.c x_net.c x_qlist.c x_gui.c d_soundfile.c \
+ $(SYSSRC)
+
+OBJ = $(SRC:.c=.o)
+
+GSRC = t_main.c t_tkcmd.c
+
+GOBJ = $(GSRC:.c=.o)
+.PHONY: pd gui
+
+.c.o:
+ cc $(CFLAGS) $(INCLUDE) -c -o $(VPATH)/$*.o $*.c
+
+pd: $(EXECUTABLE)
+
+gui: ../bin/pd-gui
+
+$(EXECUTABLE): $(OBJ)
+ cd ../obj; cc $(LDFLAGS) -o $(EXECUTABLE) $(OBJ) \
+ $(LIB)
+
+../bin/pd-gui: $(GOBJ)
+ cd ../obj; cc $(LDFLAGS) -o ../bin/pd-gui $(GOBJ) \
+ $(GLIB) -lm -lX11
+
+../bin/pd.tk: u_main.tk; cp u_main.tk ../bin/pd.tk
+
+tags: $(SRC) $(GSRC); ctags *.[ch]
+
+depend:; cc -M $(CFLAGS) $(INCLUDE) $(SRC) > makefile.dependencies
+
+include makefile.dependencies
+
diff --git a/pd/src/makefile.nt b/pd/src/makefile.nt
new file mode 100644
index 00000000..91d34051
--- /dev/null
+++ b/pd/src/makefile.nt
@@ -0,0 +1,95 @@
+# Makefile for portaudio ASIO driver version of PD
+
+all: pd gui ..\bin\pd.tk
+
+VC = "C:\Program Files\Microsoft Visual Studio\VC98"
+#VC="\Program Files\DevStudio\Vc"
+INCLUDE = -I.\ -I..\Tcl\include -I$(VC)\include
+
+LDIR = $(VC)\lib
+
+LIB = /NODEFAULTLIB:libc /NODEFAULTLIB:oldnames /NODEFAULTLIB:kernel \
+ /NODEFAULTLIB:uuid \
+ $(LDIR)\libc.lib $(LDIR)\oldnames.lib $(LDIR)\kernel32.lib \
+ $(LDIR)\wsock32.lib $(LDIR)\winmm.lib ..\bin\pthreadVC.lib
+
+GLIB = $(LIB) ..\lib\tcl83.lib ..\lib\tk83.lib
+CFLAGS = /nologo /W3 /DNT /DPD /DPD_INTERNAL /DWIN32 /DWINDOWS /Ox
+LFLAGS = /nologo
+
+SYSSRC = s_nt.c s_portaudio.c
+
+SRC = g_canvas.c g_graph.c g_text.c g_rtext.c g_array.c g_template.c g_io.c \
+ g_scalar.c g_traversal.c g_guiconnect.c g_readwrite.c g_editor.c \
+ g_all_guis.c g_bang.c g_hdial.c g_hslider.c g_mycanvas.c g_numbox.c \
+ g_toggle.c g_vdial.c g_vslider.c g_vumeter.c \
+ m_pd.c m_class.c m_obj.c m_atom.c m_memory.c m_binbuf.c \
+ m_conf.c m_glob.c m_sched.c \
+ s_main.c s_inter.c s_unix.c s_file.c s_print.c \
+ s_loader.c s_path.c s_entry.c \
+ d_ugen.c d_ctl.c d_arithmetic.c d_osc.c d_filter.c d_dac.c d_misc.c \
+ d_math.c d_fft.c d_mayer_fft.c d_fftroutine.c d_array.c d_global.c \
+ d_delay.c d_resample.c \
+ x_arithmetic.c x_connective.c x_interface.c x_midi.c x_misc.c \
+ x_time.c x_acoustics.c x_net.c x_qlist.c x_gui.c d_soundfile.c \
+ $(SYSSRC)
+
+PADIR = ..\portaudio
+INCPA = -I$(PADIR) -I$(PADIR)\pa_common -I$(PADIR)\pablio -I..\lib\asio
+SRCPA = $(PADIR)/pa_common/pa_lib.c $(PADIR)/pa_common/pa_trace.c \
+ $(PADIR)/pablio/pablio_pd.c $(PADIR)/pablio/ringbuffer_pd.c
+SRCASIO = $(PADIR)/pa_asio/pa_asio.cpp
+
+ASIOLIB = $(LDIR)\user32.lib $(LDIR)\gdi32.lib $(LDIR)\winspool.lib $(LDIR)\comdlg32.lib \
+$(LDIR)\advapi32.lib $(LDIR)\shell32.lib $(LDIR)\ole32.lib $(LDIR)\oleaut32.lib $(LDIR)\uuid.lib \
+$(LDIR)\odbc32.lib $(LDIR)\odbccp32.lib ..\lib\asio\asiolib.lib
+
+
+PAOBJ = pa_lib.obj pa_trace.obj pablio_pd.obj ringbuffer_pd.obj pa_asio.obj
+OBJC = $(SRC:.c=.obj) $(PAOBJ)
+
+GSRC = t_main.c t_tkcmd.c
+
+GOBJ = $(GSRC:.c=.obj)
+.PHONY: pd gui
+
+ALLCF = $(CFLAGS) $(INCLUDE) $(INCASIO) $(INCPA) /D_WINDOWS
+
+.c.obj:
+ cl /c $(ALLCF) /Tc$*.c
+
+pd: ..\bin\pd.exe
+
+gui: ..\bin\pdtcl.dll
+
+..\bin\pd.exe: s_entry.obj ..\bin\pd.lib
+ link $(LFLAGS) /out:..\bin\pd.exe /INCREMENTAL:NO s_entry.obj \
+ ..\bin\pd.lib $(LIB) $(ASIOLIB)
+
+..\bin\pd.dll ..\bin\pd.lib: $(OBJC) $(OBJASIO)
+ link $(LFLAGS) /dll /export:sys_main /out:..\bin\pd.dll $(OBJC) \
+ $(OBJASIO) $(LIB) $(ASIOLIB)
+
+..\bin\pdtcl.dll: t_tkcmd.obj
+ link $(LFLAGS) /dll /export:Pdtcl_Init /out:..\bin\pdtcl.dll \
+ t_tkcmd.obj $(GLIB)
+
+..\bin\pd.tk: u_main.tk; copy u_main.tk ..\bin\pd.tk
+
+# explicit rules to compile portaudio sources:
+pa_lib.obj: $(PADIR)\pa_common\pa_lib.c
+ cl /c $(ALLCF) $(PADIR)\pa_common\pa_lib.c
+pa_trace.obj: $(PADIR)\pa_common\pa_trace.c
+ cl /c $(ALLCF) $(PADIR)\pa_common\pa_trace.c
+pablio_pd.obj: $(PADIR)\pablio\pablio_pd.c
+ cl /c $(ALLCF) $(PADIR)\pablio\pablio_pd.c
+ringbuffer_pd.obj: $(PADIR)\pablio\ringbuffer_pd.c
+ cl /c $(ALLCF) $(PADIR)\pablio\ringbuffer_pd.c
+pa_asio.obj: $(PADIR)\pa_asio\pa_asio.cpp
+ cl /c $(ALLCF) $(PADIR)\pa_asio\pa_asio.cpp
+
+# the following should also clean up "bin" but it doesn't because "bin" holds
+# precious stuff from elsewhere.
+clean:
+ del *.obj
+
diff --git a/pd/src/notes.txt b/pd/src/notes.txt
new file mode 100644
index 00000000..6a01dd0b
--- /dev/null
+++ b/pd/src/notes.txt
@@ -0,0 +1,264 @@
+---------------- dolist --------------------
+++portno wierdness in gcc 3 worked around (Burton)
+non-power-of-2 channel counts in ASIO (Olaf Matthes)
+Bug, David McCallum, Jul. 13 -- find last error crashes
+
+
+last-minute bug fixes:
+add flag to select MIDI open to use select()?
+check top-of-window problem in OSX
+denormal protection
+
+doc:
+fix readme file and recopy to web page (add to README in dist instructions)
+
+problems:
+don't draw in/outlets on gui objects in graphs
+Alsa degradation after several hours running on soundblaster
+font size should depend on subpatch/abstraction
+moving a bang toward top of window creates problem
+figure out O_NDELAY for linux audio?
+missed Thomas's multi-dialog trick???
+fix iemguis not to bash symbol names
+check what happens when going back and forth between graph-on-parent
+deal with spaces in iemgui labels and send/receive names
+David McCallum, table crashes in 98?
+font hack (pix@test.at, 30 Nov 2001
+ perl -pi -e 's/-[*%a-z0-9-]*\*[*%a-z0-9-]*/fixed/' u_main.tk
+get rid of messages causing renaming; try to prevent patches closing themselves.
+Krzysztof's qlist_next reentrancy bug
+dac~/ adc~/ block~ incompatibility
+is ALSA really checking /proc!?
+scofo reports error on reading score1.txt
+data copy/paste doesn't check templates aren't changed
+rfft~ loses nyquist bin -- see "to hell with it" comment in d_fft.c
+soundfile writing gets wrong sample rate; see /* lie */ in d_soundfile.c
+
+data:
+vget, vset traversal objects
+cursor to show (x, y) location
+better hit detection (getrect is too greedy)
+click on points of plot
+typing at drawnumbers
+fix templates to be loaded on demand and belong to a globally known patch
+test and debug list elements of templates
+sublists should display on parent if desired?
+sublists seem not to handle canvas allocation right (get.pd->pointer.pd bug)
+scalar hook to catch the mouse
+protect against "plots" going away while you drag on them
+
+features:
+Pd to open html help on windows/mac
+flag to hide array names
+??? have a way to disambiguate externs from different libs???
+put serial object in main dist (see rat@telecoma, Apr. 25; winfried May 22)
+if there's just one array, don't do stringent hit check.
+netsend separate thread
+netreceive (and netsend?) message to set port number
+delete-in-rectangle message to Pds
+pasting should look at current mouse location
+"regular" numbers/symbols to do send/receive thing ala IEMGUI
+make selecting text grab keyboard focus
+think about x and y scale preservation when changing between graph and object
+show outlines of objects even when graph is "open"
+make graph labels persistent and add to dialog
+array click protection (Krzysztof's suggestion)
+Pd support for jack audio system: http://home.t-online.de/home/pdq808/jack-patch
+offer audiooutdev 1,3 feature on Windows
+Alsa in data late should carefuly reset DAC/ADC fill&empty pointers
+increase MIDIQSIZE to at least 1024 in s_unix.c
+add nonblock to linux open calls instead of using alarm
+make a hook so objects can specify help windows to open (for scheme object)
+graph_vis() to decorate graphs when they're toplevel (parent_glist == 0)
+get graphs to expand to hold their contents
+writing FLOAT wav files
+make "table" rescalable vertically
+-compat34 flag to save files so that 0.34 can read them
+suita.chopin.edu.pl/~czaja/miXed/externs/xeq.html -- MIDI file reader
+in glist_delete, consider why this can't be just "vis 0" -- why do we need it?
+abstraction auto-reload
+closebang
+switching between dac and gettimeofday timing on dsp_start/stop
+check that -blocksize really reflects in audiobuf calc for Hammerfall
+-version to print version and exit; usage() also to print version
+NT and OSX: opening HTML files?
+message to change block~ sizes dynamically
+MIDI file reading/writing?
+makefile to have make install depend on make local.
+borrow arrow keys from IEMLIB
+pd messages to close and reopen sound driver
+Float method for random
+figure out list, message objects
+separate control over alsaindev and alsaoutdev
+pd -version
+make "import"/export use IEMLIB objects
+object to get/set table size; random; quantile
+put in something for tilde order forcing
+extensible "toolbar" so people can add external GUI objects
+text cut and paste; see XStoreBytes
+new objects: nexttick~, extend threshold~ and snapshot~
+allow spaces in paths
+gem: try XSetBorderWidth, XMoveWindow, xcopyarea (/usr/share/doc/XF*)
+dialog for audio and MIDI settings
+prepend help to help filenames and add help search path
+read/writesf~ for NT
+variable send and receive -- check how max/MSP does it?
+number boxes to darken for typing and/or received messages
+delayed updates
+invisible toplevels
+dialog to change lib flag and path
+fastedit moves
+pique~ and fiddle~ unification (notice pique filtering is different!)
+new message box look
+figure out what to do when "pd sym" conflicts with window title as in Pluton?
+
+
+LATER
+Hammerfall adapt to ALSA
++~ 0 faster than +~ -- detect scalar input; also, order forcing inputs
+bonk~ file path handling
+unify arrays and garrays
+dialog to give values of $1, ... for the canvas
+bang at end of line~, tabwrite~, etc.
+recording to part of a table
+printout to main window
+should sys_bail kill all "threads" on the way out?
+check a_valid usage
+allow backslashes (or else really disallow them)
+icon & desktop integration
+vreadsf~
+benchmarking
+flash menu when accelerator hits?
+fix edit mode menu item
+"undo"
+fancier text editing
+tools (reassigns meaning of primary click)
+get gui to notice early EOF
+rewrite t_getbytes properly
+obj_new should do a longjmp on out-of-memory
+
+--------------------- source notes --------------------------
+
+0. structure definition roadmap. First, the containment tree of things
+that can be sent messages ("pure data"). (note that t_object and t_text,
+and t_graph and t_canvas, should be unified...)
+
+------------ BFFORE 0.35: ---------
+m_pd.h t_pd anything with a class
+ t_gobj "graphic object"
+ t_text text object
+g_canvas.h
+ t_glist list of graphic objects
+g_canvas.c t_canvas Pd "document"
+
+------------ AFTER 0.35: ---------
+m_pd.h t_pd anything with a class
+ t_gobj "graphic object"
+ t_text patchable object, AKA t_object
+g_canvas.h t_glist list of graphic objects, AKA t_canvas
+
+... and other structures:
+g_canvas.h t_selection -- linked list of gobjs
+ t_editor -- editor state, allocated for visible glists
+m_imp.h t_methodentry -- method handler
+ t_widgetbehavior -- class-dependent editing behavior for gobjs
+ t_parentwidgetbehavior -- objects' behavior on parent window
+ t_class -- method definitions, instance size, flags, etc.
+
+
+1. C coding style. The source should pass most "warnings" of C compilers
+(-Wall on linux, for instance; see the makefile.) Some informalities
+are intentional, for instance the loose use of function prototypes (see
+below) and uncast conversions from longer to shorter numerical formats.
+The code doesn't respect "const" yet.
+
+1.1. Prefixes in structure elements. The names of structure elements always
+have a K&R-style prefix, as in ((t_atom)x)->a_type, where the "a_" prefix
+indicates "atom." This is intended to enhance readability (although the
+convention arose from a limitation of early C compilers.) Common prefixes are
+"w_" (word), "a_" (atom), "s_" (symbol), "ob_" (object), "te_" (text object),
+"g_" (graphical object), and "gl_" (glist, a list of graphical objects). Also,
+global symbols sometimes get prefixes, as in "s_float" (the symbol whose string
+is "float). Typedefs are prefixed by "t_". Most _private_ structures, i.e.,
+structures whose definitions appear in a ".c" file, are prefixed by "x_".
+
+1.2. Function arguments. Many functions take as their first
+argument a pointer named "x", which is a pointer to a structure suggested
+by the function prefix; e.g., canvas_dirty(x, n) where "x" points to a canvas
+(t_canvas *x).
+
+1.3. Function Prototypes. Functions which are used in at least two different
+files (besides where they originate) are prototyped in the appropriate include
+file. Functions which are provided in one file and used in one other are
+prototyped right where they are used. This is just to keep the size of the
+".h" files down for readability's sake.
+
+1.4. Whacko private terminology. Some terms are lifted from other historically
+relevant programs, notably "ugen" (which is just a tilde object; see d_ugen.c.)
+
+1.5. Spacing. Tabs are 8 spaces; indentation is 4 spaces. Indenting
+curly brackets are by themselves on their own lines, as in:
+
+ if (x)
+ {
+ x = 0;
+ }
+
+Lines should fit within 80 spaces.
+
+2. Max patch-level compatibility. "Import" and "Export" functions are
+provided which aspire to strict compatibility with 0.26 patches (ISPW version),
+but which don't get anywhere close to that yet. Where possible, features
+appearing on the Mac will comeday also be provided; for instance, the connect
+message on the Mac offers segmented patch cords; these will devolve into
+straight lines in Pd. Many, many UI objects in Opcode Max will not appear in
+Pd, at least at first.
+
+3. Compatibility with Max 0.26 "externs", i.e., source-level compatibility. Pd
+objects follow the style of 0.26 objects as closely as possible, making
+exceptions in cases where the 0.26 model is clearly deficient. These are:
+
+3.1. Anything involving the MacIntosh "Handle" data type is changed to use
+char * or void * instead.
+
+3.2. Pd passes true single-precision floating-point arguments to methods;
+Max uses double.
+Typedefs are provided:
+ t_floatarg, t_intarg for arguments passed by the message system
+ t_float, t_int for the "word" union (in atoms, for example.)
+
+3.3. Badly-named entities got name changes:
+
+ w_long --> w_int (in the "union word" structure)
+
+3.4. Many library functions are renamed and have different arguments;
+I hope to provide an include file to alias them when compiling Max externs.
+
+4. Function name prefixes.
+Many function names have prefixes which indicate what "package" they belong
+to. The exceptions are:
+ typedmess, vmess, getfn, gensym (m_class.c)
+ getbytes, freebytes, resizebytes (m_memory.c)
+ post, error, bug (s_print.c)
+which are all frequently called and which don't fit into simple categories.
+Important packages are:
+(pd-gui:) pdgui -- everything
+(pd:) pd -- functions common to all "pd" objects
+ obj -- fuctions common to all "patchable" objects ala Max
+ sys -- "system" level functions
+ binbuf -- functions manipulating binbufs
+ class -- functions manipulating classes
+ (other) -- functions common to the named Pd class
+
+5. Source file prefixes.
+PD:
+s system interface
+m message system
+g graphics stuff
+d DSP objects
+x control objects
+z other
+
+PD-GUI:
+t TK front end
+
diff --git a/pd/src/s_entry.c b/pd/src/s_entry.c
new file mode 100644
index 00000000..354512e5
--- /dev/null
+++ b/pd/src/s_entry.c
@@ -0,0 +1,10 @@
+/* In NT, this is all there is to pd; the rest sits in a "pdlib" dll so
+that externs can link back to functions defined in pd. */
+
+
+int sys_main(int argc, char **argv);
+
+int main(int argc, char **argv)
+{
+ return (sys_main(argc, argv));
+}
diff --git a/pd/src/s_file.c b/pd/src/s_file.c
new file mode 100644
index 00000000..32d2fcaa
--- /dev/null
+++ b/pd/src/s_file.c
@@ -0,0 +1,54 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/*
+ * this file contains file-handling routines.
+ */
+
+#include "m_imp.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+ /* LATER delete this? -- replaced by find_via_path() in s_path.c */
+int sys_isreadablefile(const char *s)
+{
+ struct stat statbuf;
+ int mode;
+ if (stat(s, &statbuf) < 0) return (0);
+#ifdef UNIX
+ mode = statbuf.st_mode;
+ if (S_ISDIR(mode)) return (0);
+#endif
+ return (1);
+}
+
+ /* change '/' characters to the system's native file separator */
+void sys_bashfilename(const char *from, char *to)
+{
+ char c;
+ while (c = *from++)
+ {
+#ifdef NT
+ if (c == '/') c = '\\';
+#endif
+ *to++ = c;
+ }
+ *to = 0;
+}
+
+
+ /* change the system's native file separator to '/' characters */
+void sys_unbashfilename(const char *from, char *to)
+{
+ char c;
+ while (c = *from++)
+ {
+#ifdef NT
+ if (c == '\\') c = '/';
+#endif
+ *to++ = c;
+ }
+ *to = 0;
+}
+
diff --git a/pd/src/s_freebsd.c b/pd/src/s_freebsd.c
new file mode 100644
index 00000000..4ed4241b
--- /dev/null
+++ b/pd/src/s_freebsd.c
@@ -0,0 +1,3072 @@
+/* Copyright (c) 1997-1999 Guenter Geiger, Miller Puckette, Larry Troxler,
+* Winfried Ritsch, Karl MacMillan, and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* this file implements the sys_ functions profiled in m_imp.h for
+ audio and MIDI I/O. In Linux there might be several APIs for doing the
+ audio part; right now there are three (OSS, ALSA, RME); the third is
+ for the RME 9652 driver by Ritsch (but not for the OSS compatible
+ one by Geiger; for that one, OSS should work.)
+
+ FUNCTION PREFIXES.
+ sys_ -- functions which must be exported to Pd on all platforms
+ linux_ -- linux-specific objects which don't depend on API,
+ mostly static but some exported.
+ oss_, alsa_, rme_ -- API-specific functions, all of which are
+ static.
+
+ ALSA SUPPORT. If ALSA99 is defined we support ALSA 0.5x; if ALSA01,
+ ALSA 0.9x. (the naming scheme reflects the possibility of further API
+ changes in the future...) We define "ALSA" for code relevant to both
+ APIs.
+
+ For MIDI, we only offer the OSS API; ALSA has to emulate OSS for us.
+*/
+
+/* OSS include (whether we're doing OSS audio or not we need this for MIDI) */
+
+
+/* IOhannes:::
+ * hacked this to add advanced multidevice-support
+ * 1311:forum::für::umläute:2001
+ */
+
+#include <sys/soundcard.h>
+
+#if (defined(ALSA01) || defined(ALSA99))
+#define ALSA
+#endif
+
+#ifdef ALSA99
+#include <sys/asoundlib.h>
+#endif
+#ifdef ALSA01
+#include <alsa/asoundlib.h>
+#endif
+
+#include "m_imp.h"
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <sys/mman.h>
+
+/* local function prototypes */
+
+static void linux_close_midi( void);
+
+static int oss_open_audio(int naudioindev, int *audioindev, int nchindev,
+ int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev,
+ int *choutdev, int rate); /* IOhannes */
+
+static void oss_close_audio(void);
+static int oss_send_dacs(void);
+static void oss_reportidle(void);
+
+#ifdef ALSA
+typedef int16_t t_alsa_sample16;
+typedef int32_t t_alsa_sample32;
+#define ALSA_SAMPLEWIDTH_16 sizeof(t_alsa_sample16)
+#define ALSA_SAMPLEWIDTH_32 sizeof(t_alsa_sample32)
+#define ALSA_XFERSIZE16 (signed int)(sizeof(t_alsa_sample16) * DACBLKSIZE)
+#define ALSA_XFERSIZE32 (signed int)(sizeof(t_alsa_sample32) * DACBLKSIZE)
+#define ALSA_MAXDEV 1
+#define ALSA_JITTER 1024
+#define ALSA_EXTRABUFFER 2048
+#define ALSA_DEFFRAGSIZE 64
+#define ALSA_DEFNFRAG 12
+
+#ifdef ALSA99
+typedef struct _alsa_dev
+{
+ snd_pcm_t *handle;
+ snd_pcm_channel_info_t info;
+ snd_pcm_channel_setup_t setup;
+} t_alsa_dev;
+
+t_alsa_dev alsa_device[ALSA_MAXDEV];
+static int n_alsa_dev;
+static char *alsa_buf;
+static int alsa_samplewidth;
+#endif /* ALSA99 */
+
+#ifdef ALSA01
+typedef struct _alsa_dev
+{
+ snd_pcm_t *inhandle;
+ snd_pcm_t *outhandle;
+} t_alsa_dev;
+
+t_alsa_dev alsa_device;
+static short *alsa_buf;
+static int alsa_samplewidth;
+static snd_pcm_status_t* in_status;
+static snd_pcm_status_t* out_status;
+#endif /* ALSA01 */
+
+#if 0 /* early alsa 0.9 beta dists had different names for these: */
+#define SND_PCM_ACCESS_RW_INTERLEAVED SNDRV_PCM_ACCESS_RW_INTERLEAVED
+#define SND_PCM_FORMAT_S32 SNDRV_PCM_FORMAT_S32
+#define SND_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16
+#define SND_PCM_SUBFORMAT_STD SNDRV_PCM_SUBFORMAT_STD
+#endif
+
+static int alsa_mode;
+static int alsa_open_audio(int inchans, int outchans, int rate);
+static void alsa_close_audio(void);
+static int alsa_send_dacs(void);
+static void alsa_set_params(t_alsa_dev *dev, int dir, int rate, int voices);
+static void alsa_reportidle(void);
+#endif /* ALSA */
+
+#ifdef RME_HAMMERFALL
+static int rme9652_open_audio(int inchans, int outchans, int rate);
+static void rme9652_close_audio(void);
+static int rme9652_send_dacs(void);
+static void rme9652_reportidle(void);
+#endif /* RME_HAMMERFALL */
+
+/* Defines */
+#define DEBUG(x) x
+#define DEBUG2(x) {x;}
+
+#define OSS_MAXCHPERDEV 32 /* max channels per OSS device */
+#define OSS_MAXDEV 4 /* maximum number of input or output devices */
+#define OSS_DEFFRAGSIZE 256 /* default log fragment size (frames) */
+#define OSS_DEFAUDIOBUF 40000 /* default audiobuffer, microseconds */
+#define OSS_DEFAULTCH 2
+#define RME_DEFAULTCH 8 /* need this even if RME undefined */
+typedef int16_t t_oss_int16;
+typedef int32_t t_oss_int32;
+#define OSS_MAXSAMPLEWIDTH sizeof(t_oss_int32)
+#define OSS_BYTESPERCHAN(width) (DACBLKSIZE * (width))
+#define OSS_XFERSAMPS(chans) (DACBLKSIZE* (chans))
+#define OSS_XFERSIZE(chans, width) (DACBLKSIZE * (chans) * (width))
+
+#ifdef RME_HAMMERFALL
+typedef int32_t t_rme_sample;
+#define RME_SAMPLEWIDTH sizeof(t_rme_sample)
+#define RME_BYTESPERCHAN (DACBLKSIZE * RME_SAMPLEWIDTH)
+#endif /* RME_HAMMERFALL */
+
+/* GLOBALS */
+static int linux_whichapi = API_OSS;
+static int linux_inchannels;
+static int linux_outchannels;
+static int linux_advance_samples; /* scheduler advance in samples */
+static int linux_meters; /* true if we're metering */
+static float linux_inmax; /* max input amplitude */
+static float linux_outmax; /* max output amplitude */
+static int linux_fragsize = 0; /* for block mode; block size (sample frames) */
+static int linux_nfragment = 0; /* ... and number of blocks. */
+
+#ifdef ALSA99
+static int alsa_devno = 1;
+#endif
+#ifdef ALSA01
+static char alsa_devname[512] = "hw:0,0";
+static int alsa_use_plugin = 0;
+#endif
+
+/* our device handles */
+
+typedef struct _oss_dev
+{
+ int d_fd;
+ unsigned int d_space; /* bytes available for writing/reading */
+ int d_bufsize; /* total buffer size in blocks for this device */
+ int d_dropcount; /* # of buffers to drop for resync (output only) */
+ unsigned int d_nchannels; /* number of channels for this device */
+ unsigned int d_bytespersamp; /* bytes per sample (2 for 16 bit, 4 for 32) */
+} t_oss_dev;
+
+static t_oss_dev linux_dacs[OSS_MAXDEV];
+static t_oss_dev linux_adcs[OSS_MAXDEV];
+static int linux_noutdevs = 0;
+static int linux_nindevs = 0;
+
+ /* exported variables */
+int sys_schedadvance = OSS_DEFAUDIOBUF; /* scheduler advance in microsecs */
+float sys_dacsr;
+int sys_hipriority = 0;
+t_sample *sys_soundout;
+t_sample *sys_soundin;
+
+ /* OSS-specific private variables */
+static int oss_blockmode = 1; /* flag to use "blockmode" */
+static char ossdsp[] = "/dev/dsp%d";
+
+#ifndef INT32_MAX
+#define INT32_MAX 0x7fffffff
+#endif
+#define CLIP32(x) (((x)>INT32_MAX)?INT32_MAX:((x) < -INT32_MAX)?-INT32_MAX:(x))
+
+
+/* ------------- private routines for all APIS ------------------- */
+
+static void linux_flush_all_underflows_to_zero(void)
+{
+/*
+ TODO: Implement similar thing for linux (GGeiger)
+
+ One day we will figure this out, I hope, because it
+ costs CPU time dearly on Intel - LT
+ */
+ /* union fpc_csr f;
+ f.fc_word = get_fpc_csr();
+ f.fc_struct.flush = 1;
+ set_fpc_csr(f.fc_word);
+ */
+}
+
+ /* set sample rate and channels. Must set sample rate before "configuring"
+ any devices so we know scheduler advance in samples. */
+
+static void linux_setsr(int sr)
+{
+ sys_dacsr = sr;
+ linux_advance_samples = (sys_schedadvance * sys_dacsr) / (1000000.);
+ if (linux_advance_samples < 3 * DACBLKSIZE)
+ linux_advance_samples = 3 * DACBLKSIZE;
+}
+
+static void linux_setch(int chin, int chout)
+{
+ int nblk;
+ int inbytes = chin * (DACBLKSIZE*sizeof(float));
+ int outbytes = chout * (DACBLKSIZE*sizeof(float));
+
+ linux_inchannels = chin;
+ linux_outchannels = chout;
+ if (sys_soundin)
+ free(sys_soundin);
+ sys_soundin = (t_float *)malloc(inbytes);
+ memset(sys_soundin, 0, inbytes);
+
+ if (sys_soundout)
+ free(sys_soundout);
+ sys_soundout = (t_float *)malloc(outbytes);
+ memset(sys_soundout, 0, outbytes);
+
+ if (sys_verbose)
+ post("input channels = %d, output channels = %d",
+ linux_inchannels, linux_outchannels);
+}
+
+/* ---------------- MIDI routines -------------------------- */
+
+static int oss_nmidiin;
+static int oss_midiinfd[MAXMIDIINDEV];
+static int oss_nmidiout;
+static int oss_midioutfd[MAXMIDIOUTDEV];
+
+static void oss_midiout(int fd, int n)
+{
+ char b = n;
+ if ((write(fd, (char *) &b, 1)) != 1)
+ perror("midi write");
+}
+
+#define O_MIDIFLAG O_NDELAY
+
+void linux_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec)
+{
+ int i;
+ for (i = 0; i < nmidiout; i++)
+ oss_midioutfd[i] = -1;
+ for (i = 0, oss_nmidiin = 0; i < nmidiin; i++)
+ {
+ int fd = -1, j, outdevindex = -1;
+ char namebuf[80];
+ int devno = midiinvec[i];
+
+ for (j = 0; j < nmidiout; j++)
+ if (midioutvec[j] == midiinvec[i])
+ outdevindex = j;
+
+ /* try to open the device for read/write. */
+ if (devno == 1 && fd < 0 && outdevindex >= 0)
+ {
+ sys_setalarm(1000000);
+ fd = open("/dev/midi", O_RDWR | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr,
+ "device 1: tried /dev/midi READ/WRITE; returned %d\n", fd);
+ if (outdevindex >= 0 && fd >= 0)
+ oss_midioutfd[outdevindex] = fd;
+ }
+ if (fd < 0 && outdevindex >= 0)
+ {
+ sys_setalarm(1000000);
+ sprintf(namebuf, "/dev/midi%2.2d", devno-1);
+ fd = open(namebuf, O_RDWR | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr,
+ "device %d: tried %s READ/WRITE; returned %d\n",
+ devno, namebuf, fd);
+ if (outdevindex >= 0 && fd >= 0)
+ oss_midioutfd[outdevindex] = fd;
+ }
+ if (fd < 0 && outdevindex >= 0)
+ {
+ sys_setalarm(1000000);
+ sprintf(namebuf, "/dev/midi%d", devno-1);
+ fd = open(namebuf, O_RDWR | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr, "device %d: tried %s READ/WRITE; returned %d\n",
+ devno, namebuf, fd);
+ if (outdevindex >= 0 && fd >= 0)
+ oss_midioutfd[outdevindex] = fd;
+ }
+ if (devno == 1 && fd < 0)
+ {
+ sys_setalarm(1000000);
+ fd = open("/dev/midi", O_RDONLY | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr,
+ "device 1: tried /dev/midi READONLY; returned %d\n", fd);
+ }
+ if (fd < 0)
+ {
+ sys_setalarm(1000000);
+ sprintf(namebuf, "/dev/midi%2.2d", devno-1);
+ fd = open(namebuf, O_RDONLY | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr, "device %d: tried %s READONLY; returned %d\n",
+ devno, namebuf, fd);
+ }
+ if (fd < 0)
+ {
+ sys_setalarm(1000000);
+ sprintf(namebuf, "/dev/midi%d", devno-1);
+ fd = open(namebuf, O_RDONLY | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr, "device %d: tried %s READONLY; returned %d\n",
+ devno, namebuf, fd);
+ }
+ if (fd >= 0)
+ oss_midiinfd[oss_nmidiin++] = fd;
+ else post("couldn't open MIDI input device %d", devno);
+ }
+ for (i = 0, oss_nmidiout = 0; i < nmidiout; i++)
+ {
+ int fd = oss_midioutfd[i];
+ char namebuf[80];
+ int devno = midioutvec[i];
+ if (devno == 1 && fd < 0)
+ {
+ sys_setalarm(1000000);
+ fd = open("/dev/midi", O_WRONLY | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr,
+ "device 1: tried /dev/midi WRITEONLY; returned %d\n", fd);
+ }
+ if (fd < 0)
+ {
+ sys_setalarm(1000000);
+ sprintf(namebuf, "/dev/midi%2.2d", devno-1);
+ fd = open(namebuf, O_WRONLY | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr, "device %d: tried %s WRITEONLY; returned %d\n",
+ devno, namebuf, fd);
+ }
+ if (fd < 0)
+ {
+ sys_setalarm(1000000);
+ sprintf(namebuf, "/dev/midi%d", devno-1);
+ fd = open(namebuf, O_WRONLY | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr, "device %d: tried %s WRITEONLY; returned %d\n",
+ devno, namebuf, fd);
+ }
+ if (fd >= 0)
+ oss_midioutfd[oss_nmidiout++] = fd;
+ else post("couldn't open MIDI output device %d", devno);
+ }
+
+ if (oss_nmidiin < nmidiin || oss_nmidiout < nmidiout || sys_verbose)
+ post("opened %d MIDI input device(s) and %d MIDI output device(s).",
+ oss_nmidiin, oss_nmidiout);
+}
+
+#define md_msglen(x) (((x)<0xC0)?2:((x)<0xE0)?1:((x)<0xF0)?2:\
+ ((x)==0xF2)?2:((x)<0xF4)?1:0)
+
+void sys_putmidimess(int portno, int a, int b, int c)
+{
+ if (portno >= 0 && portno < oss_nmidiout)
+ {
+ switch (md_msglen(a))
+ {
+ case 2:
+ oss_midiout(oss_midioutfd[portno],a);
+ oss_midiout(oss_midioutfd[portno],b);
+ oss_midiout(oss_midioutfd[portno],c);
+ return;
+ case 1:
+ oss_midiout(oss_midioutfd[portno],a);
+ oss_midiout(oss_midioutfd[portno],b);
+ return;
+ case 0:
+ oss_midiout(oss_midioutfd[portno],a);
+ return;
+ };
+ }
+}
+
+void sys_putmidibyte(int portno, int byte)
+{
+ if (portno >= 0 && portno < oss_nmidiout)
+ oss_midiout(oss_midioutfd[portno], byte);
+}
+
+#if 0 /* this is the "select" version which doesn't work with OSS
+ driver for emu10k1 (it doesn't implement select.) */
+void sys_poll_midi(void)
+{
+ int i, throttle = 100;
+ struct timeval timout;
+ int did = 1, maxfd = 0;
+ while (did)
+ {
+ fd_set readset, writeset, exceptset;
+ did = 0;
+ if (throttle-- < 0)
+ break;
+ timout.tv_sec = 0;
+ timout.tv_usec = 0;
+
+ FD_ZERO(&writeset);
+ FD_ZERO(&readset);
+ FD_ZERO(&exceptset);
+ for (i = 0; i < oss_nmidiin; i++)
+ {
+ if (oss_midiinfd[i] > maxfd)
+ maxfd = oss_midiinfd[i];
+ FD_SET(oss_midiinfd[i], &readset);
+ }
+ select(maxfd+1, &readset, &writeset, &exceptset, &timout);
+ for (i = 0; i < oss_nmidiin; i++)
+ if (FD_ISSET(oss_midiinfd[i], &readset))
+ {
+ char c;
+ int ret = read(oss_midiinfd[i], &c, 1);
+ if (ret <= 0)
+ fprintf(stderr, "Midi read error\n");
+ else sys_midibytein(i, (c & 0xff));
+ did = 1;
+ }
+ }
+}
+#else
+
+ /* this version uses the asynchronous "read()" ... */
+void sys_poll_midi(void)
+{
+ int i, throttle = 100;
+ struct timeval timout;
+ int did = 1, maxfd = 0;
+ while (did)
+ {
+ fd_set readset, writeset, exceptset;
+ did = 0;
+ if (throttle-- < 0)
+ break;
+ for (i = 0; i < oss_nmidiin; i++)
+ {
+ char c;
+ int ret = read(oss_midiinfd[i], &c, 1);
+ if (ret < 0)
+ {
+ if (errno != EAGAIN)
+ perror("MIDI");
+ }
+ else if (ret != 0)
+ {
+ sys_midibytein(i, (c & 0xff));
+ did = 1;
+ }
+ }
+ }
+}
+#endif
+
+void linux_close_midi()
+{
+ int i;
+ for (i = 0; i < oss_nmidiin; i++)
+ close(oss_midiinfd[i]);
+ for (i = 0; i < oss_nmidiout; i++)
+ close(oss_midioutfd[i]);
+ oss_nmidiin = oss_nmidiout = 0;
+}
+
+#define MAXAUDIODEV 4
+#define DEFAULTINDEV 1
+#define DEFAULTOUTDEV 1
+
+/* ----------------------- public routines ----------------------- */
+void sys_listdevs( void)
+{
+ post("device listing not implemented in Linux yet\n");
+}
+
+void sys_open_audio(int naudioindev, int *audioindev, int nchindev,
+ int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev,
+ int *choutdev, int rate)
+{ /* IOhannes */
+ int i, *ip;
+ int defaultchannels =
+ (linux_whichapi == API_RME ? RME_DEFAULTCH : OSS_DEFAULTCH);
+ if (rate < 1)
+ rate=44100;
+
+ if (naudioindev == -1)
+ { /* not set */
+ if (nchindev==-1)
+ {
+ nchindev=1;
+ chindev[0]=defaultchannels;
+ naudioindev=1;
+ audioindev[0] = DEFAULTINDEV;
+ }
+ else
+ {
+ for (i = 0; i < MAXAUDIODEV; i++)
+ audioindev[i]=i+1;
+ naudioindev = nchindev;
+ }
+ }
+ else
+ {
+ if (nchindev == -1)
+ {
+ nchindev = naudioindev;
+ for (i = 0; i < naudioindev; i++)
+ chindev[i] = defaultchannels;
+ }
+ else if (nchindev > naudioindev)
+ {
+ for (i = naudioindev; i < nchindev; i++)
+ {
+ if (i == 0)
+ audioindev[0] = DEFAULTINDEV;
+ else audioindev[i] = audioindev[i-1] + 1;
+ }
+ naudioindev = nchindev;
+ }
+ else if (nchindev < naudioindev)
+ {
+ for (i = nchindev; i < naudioindev; i++)
+ {
+ if (i == 0)
+ chindev[0] = defaultchannels;
+ else chindev[i] = chindev[i-1];
+ }
+ naudioindev = nchindev;
+ }
+ }
+
+ if (naudiooutdev == -1)
+ { /* not set */
+ if (nchoutdev==-1)
+ {
+ nchoutdev=1;
+ choutdev[0]=defaultchannels;
+ naudiooutdev=1;
+ audiooutdev[0] = DEFAULTOUTDEV;
+ }
+ else
+ {
+ for (i = 0; i < MAXAUDIODEV; i++)
+ audiooutdev[i] = i+1;
+ naudiooutdev = nchoutdev;
+ }
+ }
+ else
+ {
+ if (nchoutdev == -1)
+ {
+ nchoutdev = naudiooutdev;
+ for (i = 0; i < naudiooutdev; i++)
+ choutdev[i] = defaultchannels;
+ }
+ else if (nchoutdev > naudiooutdev)
+ {
+ for (i = naudiooutdev; i < nchoutdev; i++)
+ {
+ if (i == 0)
+ audiooutdev[0] = DEFAULTOUTDEV;
+ else audiooutdev[i] = audiooutdev[i-1] + 1;
+ }
+ naudiooutdev = nchoutdev;
+ }
+ else if (nchoutdev < naudiooutdev)
+ {
+ for (i = nchoutdev; i < naudiooutdev; i++)
+ {
+ if (i == 0)
+ choutdev[0] = defaultchannels;
+ else choutdev[i] = choutdev[i-1];
+ }
+ naudiooutdev = nchoutdev;
+ }
+ }
+
+ linux_flush_all_underflows_to_zero();
+#ifdef ALSA
+ if (linux_whichapi == API_ALSA)
+ alsa_open_audio((naudioindev > 0 ? chindev[0] : 0),
+ (naudiooutdev > 0 ? choutdev[0] : 0), rate);
+ else
+#endif
+#ifdef RME_HAMMERFALL
+ if (linux_whichapi == API_RME)
+ rme9652_open_audio((naudioindev > 0 ? chindev[0] : 0),
+ (naudiooutdev > 0 ? choutdev[0] : 0), rate);
+ else
+#endif
+ oss_open_audio(naudioindev, audioindev, nchindev, chindev,
+ naudiooutdev, audiooutdev, nchoutdev, choutdev, rate);
+}
+
+void sys_close_audio(void)
+{
+ /* set timeout to avoid hanging close() call */
+
+ sys_setalarm(1000000);
+
+#ifdef ALSA
+ if (linux_whichapi == API_ALSA)
+ alsa_close_audio();
+ else
+#endif
+#ifdef RME_HAMMERFALL
+ if (linux_whichapi == API_RME)
+ rme9652_close_audio();
+ else
+#endif
+ oss_close_audio();
+
+ sys_setalarm(0);
+}
+
+void sys_open_midi(int nmidiin, int *midiinvec,
+ int nmidiout, int *midioutvec)
+{
+ linux_open_midi(nmidiin, midiinvec, nmidiout, midioutvec);
+}
+
+void sys_close_midi( void)
+{
+ sys_setalarm(1000000);
+ linux_close_midi();
+ sys_setalarm(0);
+}
+
+int sys_send_dacs(void)
+{
+ if (linux_meters)
+ {
+ int i, n;
+ float maxsamp;
+ for (i = 0, n = linux_inchannels * DACBLKSIZE, maxsamp = linux_inmax;
+ i < n; i++)
+ {
+ float f = sys_soundin[i];
+ if (f > maxsamp) maxsamp = f;
+ else if (-f > maxsamp) maxsamp = -f;
+ }
+ linux_inmax = maxsamp;
+ for (i = 0, n = linux_outchannels * DACBLKSIZE, maxsamp = linux_outmax;
+ i < n; i++)
+ {
+ float f = sys_soundout[i];
+ if (f > maxsamp) maxsamp = f;
+ else if (-f > maxsamp) maxsamp = -f;
+ }
+ linux_outmax = maxsamp;
+ }
+#ifdef ALSA
+ if (linux_whichapi == API_ALSA)
+ return alsa_send_dacs();
+#endif
+#ifdef RME_HAMMERFALL
+ if (linux_whichapi == API_RME)
+ return rme9652_send_dacs();
+#endif
+ return oss_send_dacs();
+}
+
+float sys_getsr(void)
+{
+ return (sys_dacsr);
+}
+
+int sys_get_outchannels(void)
+{
+ return (linux_outchannels);
+}
+
+int sys_get_inchannels(void)
+{
+ return (linux_inchannels);
+}
+
+void sys_audiobuf(int n)
+{
+ /* set the size, in milliseconds, of the audio FIFO */
+ if (n < 5) n = 5;
+ else if (n > 5000) n = 5000;
+ sys_schedadvance = n * 1000;
+}
+
+void sys_getmeters(float *inmax, float *outmax)
+{
+ if (inmax)
+ {
+ linux_meters = 1;
+ *inmax = linux_inmax;
+ *outmax = linux_outmax;
+ }
+ else
+ linux_meters = 0;
+ linux_inmax = linux_outmax = 0;
+}
+
+void sys_reportidle(void)
+{
+}
+
+void sys_set_priority(int higher)
+{
+ struct sched_param par;
+ int p1 ,p2, p3;
+#ifdef _POSIX_PRIORITY_SCHEDULING
+
+ p1 = sched_get_priority_min(SCHED_FIFO);
+ p2 = sched_get_priority_max(SCHED_FIFO);
+ p3 = (higher ? p2 - 1 : p2 - 3);
+ par.sched_priority = p3;
+
+ if (sched_setscheduler(0,SCHED_FIFO,&par) != -1)
+ fprintf(stderr, "priority %d scheduling enabled.\n", p3);
+#endif
+
+#ifdef _POSIX_MEMLOCK
+ if (mlockall(MCL_FUTURE) != -1)
+ fprintf(stderr, "memory locking enabled.\n");
+#endif
+}
+
+/* ------------ linux-specific command-line flags -------------- */
+
+void linux_setfrags(int n)
+{
+ linux_nfragment = n;
+ oss_blockmode = 1;
+}
+
+void linux_setfragsize(int n)
+{
+ if (n < 1)
+ n = 1;
+ linux_fragsize = n;
+ oss_blockmode = 1;
+}
+
+void linux_streammode( void)
+{
+ oss_blockmode = 0;
+}
+
+void linux_set_sound_api(int which)
+{
+ linux_whichapi = which;
+ if (sys_verbose)
+ post("linux_whichapi %d", linux_whichapi);
+}
+
+#ifdef ALSA99
+void linux_alsa_devno(int devno)
+{
+ alsa_devno = devno;
+}
+
+#endif
+
+#ifdef ALSA01
+void linux_alsa_devname(char *devname)
+{
+ strncpy(alsa_devname, devname, 511);
+}
+
+void linux_alsa_use_plugin(int t)
+{
+ if (t == 1)
+ alsa_use_plugin = 1;
+ else
+ alsa_use_plugin = 0;
+}
+
+#endif
+
+/* -------------- Audio I/O using the OSS API ------------------ */
+
+typedef struct _multidev {
+ int fd;
+ int channels;
+ int format;
+} t_multidev;
+
+int oss_reset(int fd) {
+ int err;
+ if ((err = ioctl(fd,SNDCTL_DSP_RESET)) < 0)
+ error("OSS: Could not reset");
+ return err;
+}
+
+ /* The AFMT_S32_BLOCKED format is not defined in standard linux kernels
+ but is proposed by Guenter Geiger to support extending OSS to handle
+ 32 bit sample. This is user in Geiger's OSS driver for RME Hammerfall.
+ I'm not clear why this isn't called AFMT_S32_[SLN]E... */
+
+#ifndef AFMT_S32_BLOCKED
+#define AFMT_S32_BLOCKED 0x0000400
+#endif
+
+void oss_configure(t_oss_dev *dev, int srate, int dac, int skipblocksize)
+{ /* IOhannes */
+ int orig, param, nblk, fd = dev->d_fd, wantformat;
+ int nchannels = dev->d_nchannels;
+ int advwas = sys_schedadvance;
+
+ audio_buf_info ainfo;
+
+ /* IOhannes :
+ * pd is very likely to crash if different formats are used on
+ multiple soundcards
+ */
+
+ /* set resolution - first try 4 byte samples */
+ if ((ioctl(fd,SNDCTL_DSP_GETFMTS,&param) >= 0) &&
+ (param & AFMT_S32_BLOCKED))
+ {
+ wantformat = AFMT_S32_BLOCKED;
+ dev->d_bytespersamp = 4;
+ }
+ else
+ {
+/* FreeBSD's soundcard.h does not define AFMT_S16_NE */
+ wantformat = AFMT_S16_BE;
+ dev->d_bytespersamp = 2;
+ }
+ param = wantformat;
+
+ if (sys_verbose)
+ post("bytes per sample = %d", dev->d_bytespersamp);
+ if (ioctl(fd, SNDCTL_DSP_SETFMT, &param) == -1)
+ fprintf(stderr,"OSS: Could not set DSP format\n");
+ else if (wantformat != param)
+ fprintf(stderr,"OSS: DSP format: wanted %d, got %d\n",
+ wantformat, param);
+
+ /* sample rate */
+ orig = param = srate;
+ if (ioctl(fd, SNDCTL_DSP_SPEED, &param) == -1)
+ fprintf(stderr,"OSS: Could not set sampling rate for device\n");
+ else if( orig != param )
+ fprintf(stderr,"OSS: sampling rate: wanted %d, got %d\n",
+ orig, param );
+
+ if (oss_blockmode && !skipblocksize)
+ {
+ int fragbytes, logfragsize, nfragment;
+ /* setting fragment count and size. */
+ if (linux_nfragment) /* if nfrags specified, take literally */
+ {
+ nfragment = linux_nfragment;
+ if (!linux_fragsize)
+ linux_fragsize = OSS_DEFFRAGSIZE;
+ sys_schedadvance = ((nfragment * linux_fragsize) * 1.e6)
+ / (float)srate;
+ linux_setsr(srate);
+ }
+ else
+ {
+ if (!linux_fragsize)
+ {
+ linux_fragsize = OSS_DEFFRAGSIZE;
+ while (linux_fragsize > DACBLKSIZE
+ && linux_fragsize * 4 > linux_advance_samples)
+ linux_fragsize = linux_fragsize/2;
+ }
+ /* post("adv_samples %d", linux_advance_samples); */
+ nfragment = (sys_schedadvance * (44100. * 1.e-6)) / linux_fragsize;
+ }
+ fragbytes = linux_fragsize * (dev->d_bytespersamp * nchannels);
+ logfragsize = ilog2(fragbytes);
+
+ if (fragbytes != (1 << logfragsize))
+ post("warning: OSS takes only power of 2 blocksize; using %d",
+ (1 << logfragsize)/(dev->d_bytespersamp * nchannels));
+ if (sys_verbose)
+ post("setting nfrags = %d, fragsize %d\n", nfragment, fragbytes);
+
+ param = orig = (nfragment<<16) + logfragsize;
+ if (ioctl(fd,SNDCTL_DSP_SETFRAGMENT, &param) == -1)
+ error("OSS: Could not set or read fragment size\n");
+ if (param != orig)
+ {
+ nfragment = ((param >> 16) & 0xffff);
+ logfragsize = (param & 0xffff);
+ post("warning: actual fragments %d, blocksize %d",
+ nfragment, (1 << logfragsize));
+ }
+ if (sys_verbose)
+ post("audiobuffer set to %d msec", (int)(0.001 * sys_schedadvance));
+ }
+
+ if (dac)
+ {
+ /* use "free space" to learn the buffer size. Normally you
+ should set this to your own desired value; but this seems not
+ to be implemented uniformly across different sound cards. LATER
+ we should figure out what to do if the requested scheduler advance
+ is greater than this buffer size; for now, we just print something
+ out. */
+
+ int defect;
+ if (ioctl(fd, SOUND_PCM_GETOSPACE,&ainfo) < 0)
+ fprintf(stderr,"OSS: ioctl on output device failed");
+ dev->d_bufsize = ainfo.bytes;
+
+ defect = linux_advance_samples * (dev->d_bytespersamp * nchannels)
+ - dev->d_bufsize - OSS_XFERSIZE(nchannels, dev->d_bytespersamp);
+ if (defect > 0)
+ {
+ if (sys_verbose || defect > (dev->d_bufsize >> 2))
+ fprintf(stderr,
+ "OSS: requested audio buffer size %d limited to %d\n",
+ linux_advance_samples * (dev->d_bytespersamp * nchannels),
+ dev->d_bufsize);
+ linux_advance_samples =
+ (dev->d_bufsize - OSS_XFERSAMPS(nchannels)) /
+ (dev->d_bytespersamp *nchannels);
+ }
+ }
+}
+
+static int oss_setchannels(int fd, int wantchannels, char *devname)
+{ /* IOhannes */
+ int param = wantchannels;
+
+ while (param>1) {
+ int save = param;
+ if (ioctl(fd, SNDCTL_DSP_CHANNELS, &param) == -1) {
+ error("OSS: SNDCTL_DSP_CHANNELS failed %s",devname);
+ } else {
+ if (param == save) return (param);
+ }
+ param=save-1;
+ }
+
+ return (0);
+}
+
+int oss_open_audio(int nindev, int *indev, int nchin, int *chin,
+ int noutdev, int *outdev, int nchout, int *chout, int rate)
+{ /* IOhannes */
+ int capabilities = 0;
+ int inchannels = 0, outchannels = 0;
+ char devname[20];
+ int n, i, fd;
+ char buf[OSS_MAXSAMPLEWIDTH * DACBLKSIZE * OSS_MAXCHPERDEV];
+ int num_devs = 0;
+ int wantmore=0;
+ int spread = 0;
+ audio_buf_info ainfo;
+
+ linux_nindevs = linux_noutdevs = 0;
+
+ /* set logical sample rate amd calculate linux_advance_samples. */
+ linux_setsr(rate);
+
+ /* mark input devices unopened */
+ for (i = 0; i < OSS_MAXDEV; i++)
+ linux_adcs[i].d_fd = -1;
+
+ /* open output devices */
+ wantmore=0;
+ if (noutdev < 0 || nindev < 0)
+ bug("linux_open_audio");
+
+ for (n = 0; n < noutdev; n++)
+ {
+ int gotchans, j, inindex = -1;
+ int thisdevice=outdev[n];
+ int wantchannels = (nchout>n) ? chout[n] : wantmore;
+ fd = -1;
+ if (!wantchannels)
+ goto end_out_loop;
+
+ if (thisdevice > 1)
+ sprintf(devname, "/dev/dsp%d", thisdevice-1);
+ else sprintf(devname, "/dev/dsp");
+
+ /* search for input request for same device. Succeed only
+ if the number of channels matches. */
+ for (j = 0; j < nindev; j++)
+ if (indev[j] == thisdevice && chin[j] == wantchannels)
+ inindex = j;
+
+ /* if the same device is requested for input and output,
+ try to open it read/write */
+ if (inindex >= 0)
+ {
+ sys_setalarm(1000000);
+ if ((fd = open(devname, O_RDWR)) == -1)
+ {
+ post("%s (read/write): %s", devname, strerror(errno));
+ post("(now will try write-only...)");
+ }
+ else
+ {
+ if (sys_verbose)
+ post("opened %s for reading and writing\n", devname);
+ linux_adcs[inindex].d_fd = fd;
+ }
+ }
+ /* if that didn't happen or if it failed, try write-only */
+ if (fd == -1)
+ {
+ sys_setalarm(1000000);
+ if ((fd = open(devname, O_WRONLY)) == -1)
+ {
+ post("%s (writeonly): %s",
+ devname, strerror(errno));
+ break;
+ }
+ if (sys_verbose)
+ post("opened %s for writing only\n", devname);
+ }
+ if (ioctl(fd, SNDCTL_DSP_GETCAPS, &capabilities) == -1)
+ error("OSS: SNDCTL_DSP_GETCAPS failed %s", devname);
+
+ gotchans = oss_setchannels(fd,
+ (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels,
+ devname);
+
+ if (sys_verbose)
+ post("opened audio output on %s; got %d channels",
+ devname, gotchans);
+
+ if (gotchans < 2)
+ {
+ /* can't even do stereo? just give up. */
+ close(fd);
+ }
+ else
+ {
+ linux_dacs[linux_noutdevs].d_nchannels = gotchans;
+ linux_dacs[linux_noutdevs].d_fd = fd;
+ oss_configure(linux_dacs+linux_noutdevs, rate, 1, 0);
+
+ linux_noutdevs++;
+ outchannels += gotchans;
+ if (inindex >= 0)
+ {
+ linux_adcs[inindex].d_nchannels = gotchans;
+ chin[inindex] = gotchans;
+ }
+ }
+ /* LATER think about spreading large numbers of channels over
+ various dsp's and vice-versa */
+ wantmore = wantchannels - gotchans;
+ end_out_loop: ;
+ }
+
+ /* open input devices */
+ wantmore = 0;
+ if (nindev==-1)
+ nindev=4; /* spread channels over default-devices */
+ for (n = 0; n < nindev; n++)
+ {
+ int gotchans=0;
+ int thisdevice=indev[n];
+ int wantchannels = (nchin>n)?chin[n]:wantmore;
+ int alreadyopened = 0;
+ if (!wantchannels)
+ goto end_in_loop;
+
+ if (thisdevice > 1)
+ sprintf(devname, "/dev/dsp%d", thisdevice - 1);
+ else sprintf(devname, "/dev/dsp");
+
+ sys_setalarm(1000000);
+
+ /* perhaps it's already open from the above? */
+ if (linux_dacs[n].d_fd >= 0)
+ {
+ fd = linux_dacs[n].d_fd;
+ alreadyopened = 1;
+ }
+ else
+ {
+ /* otherwise try to open it here. */
+ if ((fd = open(devname, O_RDONLY)) == -1)
+ {
+ post("%s (readonly): %s", devname, strerror(errno));
+ goto end_in_loop;
+ }
+ if (sys_verbose)
+ post("opened %s for reading only\n", devname);
+ }
+ linux_adcs[linux_nindevs].d_fd = fd;
+ gotchans = oss_setchannels(fd,
+ (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels,
+ devname);
+ if (sys_verbose)
+ post("opened audio input device %s; got %d channels",
+ devname, gotchans);
+
+ if (gotchans < 1)
+ {
+ close(fd);
+ goto end_in_loop;
+ }
+
+ linux_adcs[linux_nindevs].d_nchannels = gotchans;
+
+ oss_configure(linux_adcs+linux_nindevs, rate, 0, alreadyopened);
+
+ inchannels += gotchans;
+ linux_nindevs++;
+
+ wantmore = wantchannels-gotchans;
+ /* LATER think about spreading large numbers of channels over
+ various dsp's and vice-versa */
+ end_in_loop: ;
+ }
+
+ linux_setch(inchannels, outchannels);
+
+ /* We have to do a read to start the engine. This is
+ necessary because sys_send_dacs waits until the input
+ buffer is filled and only reads on a filled buffer.
+ This is good, because it's a way to make sure that we
+ will not block. But I wonder why we only have to read
+ from one of the devices and not all of them??? */
+
+ if (linux_nindevs)
+ {
+ if (sys_verbose)
+ fprintf(stderr,("OSS: issuing first ADC 'read' ... "));
+ read(linux_adcs[0].d_fd, buf,
+ linux_adcs[0].d_bytespersamp *
+ linux_adcs[0].d_nchannels * DACBLKSIZE);
+ if (sys_verbose)
+ fprintf(stderr, "...done.\n");
+ }
+ sys_setalarm(0);
+ return (0);
+}
+
+void oss_close_audio( void)
+{
+ int i;
+ for (i=0;i<linux_nindevs;i++)
+ close(linux_adcs[i].d_fd);
+
+ for (i=0;i<linux_noutdevs;i++)
+ close(linux_dacs[i].d_fd);
+
+ linux_nindevs = linux_noutdevs = 0;
+}
+
+static int linux_dacs_write(int fd,void* buf,long bytes)
+{
+ return write(fd, buf, bytes);
+}
+
+static int linux_adcs_read(int fd,void* buf,long bytes)
+{
+ return read(fd, buf, bytes);
+}
+
+ /* query audio devices for "available" data size. */
+static void oss_calcspace(void)
+{
+ int dev;
+ audio_buf_info ainfo;
+ for (dev=0; dev < linux_noutdevs; dev++)
+ {
+ if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0)
+ fprintf(stderr,"OSS: ioctl on output device %d failed",dev);
+ linux_dacs[dev].d_space = ainfo.bytes;
+ }
+
+ for (dev = 0; dev < linux_nindevs; dev++)
+ {
+ if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE,&ainfo) < 0)
+ fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed",
+ dev, linux_adcs[dev].d_fd);
+ linux_adcs[dev].d_space = ainfo.bytes;
+ }
+}
+
+void linux_audiostatus(void)
+{
+ int dev;
+ if (!oss_blockmode)
+ {
+ oss_calcspace();
+ for (dev=0; dev < linux_noutdevs; dev++)
+ fprintf(stderr, "dac %d space %d\n", dev, linux_dacs[dev].d_space);
+
+ for (dev = 0; dev < linux_nindevs; dev++)
+ fprintf(stderr, "adc %d space %d\n", dev, linux_adcs[dev].d_space);
+
+ }
+}
+
+/* this call resyncs audio output and input which will cause discontinuities
+in audio output and/or input. */
+
+static void oss_doresync( void)
+{
+ int dev, zeroed = 0, wantsize;
+ char buf[OSS_MAXSAMPLEWIDTH * DACBLKSIZE * OSS_MAXCHPERDEV];
+ audio_buf_info ainfo;
+
+ /* 1. if any input devices are ahead (have more than 1 buffer stored),
+ drop one or more buffers worth */
+ for (dev = 0; dev < linux_nindevs; dev++)
+ {
+ if (linux_adcs[dev].d_space == 0)
+ {
+ linux_adcs_read(linux_adcs[dev].d_fd, buf,
+ OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
+ linux_adcs[dev].d_bytespersamp));
+ }
+ else while (linux_adcs[dev].d_space >
+ OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
+ linux_adcs[dev].d_bytespersamp))
+ {
+ linux_adcs_read(linux_adcs[dev].d_fd, buf,
+ OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
+ linux_adcs[dev].d_bytespersamp));
+ if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE, &ainfo) < 0)
+ {
+ fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed",
+ dev, linux_adcs[dev].d_fd);
+ break;
+ }
+ linux_adcs[dev].d_space = ainfo.bytes;
+ }
+ }
+
+ /* 2. if any output devices are behind, feed them zeros to catch them
+ up */
+ for (dev = 0; dev < linux_noutdevs; dev++)
+ {
+ while (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize -
+ linux_advance_samples * (linux_dacs[dev].d_nchannels *
+ linux_dacs[dev].d_bytespersamp))
+ {
+ if (!zeroed)
+ {
+ unsigned int i;
+ for (i = 0; i < OSS_XFERSAMPS(linux_dacs[dev].d_nchannels);
+ i++)
+ buf[i] = 0;
+ zeroed = 1;
+ }
+ linux_dacs_write(linux_dacs[dev].d_fd, buf,
+ OSS_XFERSIZE(linux_dacs[dev].d_nchannels,
+ linux_dacs[dev].d_bytespersamp));
+ if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0)
+ {
+ fprintf(stderr, "OSS: ioctl on output device %d, fd %d failed",
+ dev, linux_dacs[dev].d_fd);
+ break;
+ }
+ linux_dacs[dev].d_space = ainfo.bytes;
+ }
+ }
+ /* 3. if any DAC devices are too far ahead, plan to drop the
+ number of frames which will let the others catch up. */
+ for (dev = 0; dev < linux_noutdevs; dev++)
+ {
+ if (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize -
+ (linux_advance_samples - 1) * linux_dacs[dev].d_nchannels *
+ linux_dacs[dev].d_bytespersamp)
+ {
+ linux_dacs[dev].d_dropcount = linux_advance_samples - 1 -
+ (linux_dacs[dev].d_space - linux_dacs[dev].d_bufsize) /
+ (linux_dacs[dev].d_nchannels *
+ linux_dacs[dev].d_bytespersamp) ;
+ }
+ else linux_dacs[dev].d_dropcount = 0;
+ }
+}
+
+int oss_send_dacs(void)
+{
+ float *fp1, *fp2;
+ long fill;
+ int i, j, dev, rtnval = SENDDACS_YES;
+ char buf[OSS_MAXSAMPLEWIDTH * DACBLKSIZE * OSS_MAXCHPERDEV];
+ t_oss_int16 *sp;
+ t_oss_int32 *lp;
+ /* the maximum number of samples we should have in the ADC buffer */
+ int idle = 0;
+ int thischan;
+ double timeref, timenow;
+
+ if (!linux_nindevs && !linux_noutdevs)
+ return (SENDDACS_NO);
+
+ if (!oss_blockmode)
+ {
+ /* determine whether we're idle. This is true if either (1)
+ some input device has less than one buffer to read or (2) some
+ output device has fewer than (linux_advance_samples) blocks buffered
+ already. */
+ oss_calcspace();
+
+ for (dev=0; dev < linux_noutdevs; dev++)
+ if (linux_dacs[dev].d_dropcount ||
+ (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space >
+ linux_advance_samples * linux_dacs[dev].d_bytespersamp *
+ linux_dacs[dev].d_nchannels))
+ idle = 1;
+ for (dev=0; dev < linux_nindevs; dev++)
+ if (linux_adcs[dev].d_space <
+ OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
+ linux_adcs[dev].d_bytespersamp))
+ idle = 1;
+ }
+
+ if (idle && !oss_blockmode)
+ {
+ /* sometimes---rarely---when the ADC available-byte-count is
+ zero, it's genuine, but usually it's because we're so
+ late that the ADC has overrun its entire kernel buffer. We
+ distinguish between the two by waiting 2 msec and asking again.
+ There should be an error flag we could check instead; look for this
+ someday... */
+ for (dev = 0;dev < linux_nindevs; dev++)
+ if (linux_adcs[dev].d_space == 0)
+ {
+ audio_buf_info ainfo;
+ sys_microsleep(2000);
+ oss_calcspace();
+ if (linux_adcs[dev].d_space != 0) continue;
+
+ /* here's the bad case. Give up and resync. */
+ sys_log_error(ERR_DATALATE);
+ oss_doresync();
+ return (SENDDACS_NO);
+ }
+ /* check for slippage between devices, either because
+ data got lost in the driver from a previous late condition, or
+ because the devices aren't synced. When we're idle, no
+ input device should have more than one buffer readable and
+ no output device should have less than linux_advance_samples-1
+ */
+
+ for (dev=0; dev < linux_noutdevs; dev++)
+ if (!linux_dacs[dev].d_dropcount &&
+ (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space <
+ (linux_advance_samples - 2) *
+ (linux_dacs[dev].d_bytespersamp *
+ linux_dacs[dev].d_nchannels)))
+ goto badsync;
+ for (dev=0; dev < linux_nindevs; dev++)
+ if (linux_adcs[dev].d_space > 3 *
+ OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
+ linux_adcs[dev].d_bytespersamp))
+ goto badsync;
+
+ /* return zero to tell the scheduler we're idle. */
+ return (SENDDACS_NO);
+ badsync:
+ sys_log_error(ERR_RESYNC);
+ oss_doresync();
+ return (SENDDACS_NO);
+
+ }
+
+ /* do output */
+
+ timeref = sys_getrealtime();
+ for (dev=0, thischan = 0; dev < linux_noutdevs; dev++)
+ {
+ int nchannels = linux_dacs[dev].d_nchannels;
+ if (linux_dacs[dev].d_dropcount)
+ linux_dacs[dev].d_dropcount--;
+ else
+ {
+ if (linux_dacs[dev].d_bytespersamp == 4)
+ {
+ for (i = DACBLKSIZE * nchannels, fp1 = sys_soundout +
+ DACBLKSIZE*thischan,
+ lp = (t_oss_int32 *)buf; i--; fp1++, lp++)
+ {
+ float f = *fp1 * 2147483648.;
+ *lp = (f >= 2147483647. ? 2147483647. :
+ (f < -2147483648. ? -2147483648. : f));
+ }
+ }
+ else
+ {
+ for (i = DACBLKSIZE, fp1 = sys_soundout +
+ DACBLKSIZE*thischan,
+ sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels)
+ {
+ for (j=0, fp2 = fp1; j<nchannels; j++, fp2 += DACBLKSIZE)
+ {
+ int s = *fp2 * 32767.;
+ if (s > 32767) s = 32767;
+ else if (s < -32767) s = -32767;
+ sp[j] = s;
+ }
+ }
+ }
+ linux_dacs_write(linux_dacs[dev].d_fd, buf,
+ OSS_XFERSIZE(nchannels, linux_dacs[dev].d_bytespersamp));
+ if ((timenow = sys_getrealtime()) - timeref > 0.002)
+ {
+ if (!oss_blockmode)
+ sys_log_error(ERR_DACSLEPT);
+ else rtnval = SENDDACS_SLEPT;
+ }
+ timeref = timenow;
+ }
+ thischan += nchannels;
+ }
+ memset(sys_soundout, 0,
+ linux_outchannels * (sizeof(float) * DACBLKSIZE));
+
+ /* do input */
+
+ for (dev = 0, thischan = 0; dev < linux_nindevs; dev++)
+ {
+ int nchannels = linux_adcs[dev].d_nchannels;
+ linux_adcs_read(linux_adcs[dev].d_fd, buf,
+ OSS_XFERSIZE(nchannels, linux_adcs[dev].d_bytespersamp));
+
+ if ((timenow = sys_getrealtime()) - timeref > 0.002)
+ {
+ if (!oss_blockmode)
+ sys_log_error(ERR_ADCSLEPT);
+ else
+ rtnval = SENDDACS_SLEPT;
+ }
+ timeref = timenow;
+
+ if (linux_adcs[dev].d_bytespersamp == 4)
+ {
+ for (i = DACBLKSIZE*nchannels,
+ fp1 = sys_soundin + thischan*DACBLKSIZE,
+ lp = (t_oss_int32 *)buf; i--; fp1++, lp++)
+ {
+ *fp1 = ((float)(*lp))*(float)(1./2147483648.);
+ }
+ }
+ else
+ {
+ for (i = DACBLKSIZE,fp1 = sys_soundin + thischan*DACBLKSIZE,
+ sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels)
+ {
+ for (j=0;j<linux_inchannels;j++)
+ fp1[j*DACBLKSIZE] = (float)sp[j]*(float)3.051850e-05;
+ }
+ }
+ thischan += nchannels;
+ }
+ if (thischan != linux_inchannels)
+ bug("inchannels");
+ return (rtnval);
+}
+
+/* ----------------- audio I/O using the ALSA native API ---------------- */
+
+#ifdef ALSA
+static void alsa_checkversion( void)
+{
+ char snox[512];
+ int fd, nbytes;
+ if ((fd = open("/proc/asound/version", 0)) < 0 ||
+ (nbytes = read(fd, snox, 511)) < 1)
+ {
+ perror("cannot check Alsa version -- /proc/asound/version");
+ return;
+ }
+ snox[nbytes] = 0;
+#ifdef ALSA99
+ if (!strstr(snox, "Version 0.5"))
+ {
+ fprintf(stderr,
+"warning: Pd compiled for Alsa version 0.5 appears to be incompatible with\n\
+the installed version of ALSA. Here is what I found in /proc/asound/version:\n"
+ );
+ fprintf(stderr, "%s", snox);
+ }
+#else
+ if (!strstr(snox, "Version 0.9"))
+ {
+ fprintf(stderr,
+"warning: Pd compiled for Alsa version 0.9 appears to be incompatible with\n\
+the installed version of ALSA. Here is what I found in /proc/asound/version:\n"
+ );
+ fprintf(stderr, "%s", snox);
+ }
+#endif
+}
+#endif
+
+#ifdef ALSA99
+static int alsa_open_audio(int wantinchans, int wantoutchans,
+ int srate)
+{
+ int dir, voices, bsize;
+ int err, id, rate, i;
+ char *cardname;
+ snd_ctl_hw_info_t hwinfo;
+ snd_pcm_info_t pcminfo;
+ snd_pcm_channel_info_t channelinfo;
+ snd_ctl_t *handle;
+ snd_pcm_sync_t sync;
+
+ linux_inchannels = 0;
+ linux_outchannels = 0;
+
+ rate = 44100;
+ alsa_samplewidth = 4; /* first try 4 byte samples */
+
+ if (!wantinchans && !wantoutchans)
+ return (1);
+
+ alsa_checkversion();
+ if (sys_verbose)
+ {
+ if ((err = snd_card_get_longname(alsa_devno-1, &cardname)) < 0)
+ {
+ fprintf(stderr, "PD-ALSA: unable to get name of card number %d\n",
+ alsa_devno);
+ return 1;
+ }
+ fprintf(stderr, "PD-ALSA: using card %s\n", cardname);
+ free(cardname);
+ }
+
+ if ((err = snd_ctl_open(&handle, alsa_devno-1)) < 0)
+ {
+ fprintf(stderr, "PD-ALSA: unable to open control: %s\n",
+ snd_strerror(err));
+ return 1;
+ }
+
+ if ((err = snd_ctl_hw_info(handle, &hwinfo)) < 0)
+ {
+ fprintf(stderr, "PD-ALSA: unable to open get info: %s\n",
+ snd_strerror(err));
+ return 1;
+ }
+ if (hwinfo.pcmdevs < 1)
+ {
+ fprintf(stderr, "PD-ALSA: device %d doesn't support PCM\n",
+ alsa_devno);
+ snd_ctl_close(handle);
+ return 1;
+ }
+
+ if ((err = snd_ctl_pcm_info(handle, 0, &pcminfo)) < 0)
+ {
+ fprintf(stderr, "PD-ALSA: unable to open get pcm info: %s\n",
+ snd_strerror(err));
+ snd_ctl_close(handle);
+ return (1);
+ }
+ snd_ctl_close(handle);
+
+ /* find out if opening for input, output, or both and check that the
+ device can handle it. */
+ if (wantinchans && wantoutchans)
+ {
+ if (!(pcminfo.flags & SND_PCM_INFO_DUPLEX))
+ {
+ fprintf(stderr, "PD-ALSA: device is not full duplex\n");
+ return (1);
+ }
+ dir = SND_PCM_OPEN_DUPLEX;
+ }
+ else if (wantoutchans)
+ {
+ if (!(pcminfo.flags & SND_PCM_INFO_PLAYBACK))
+ {
+ fprintf(stderr, "PD-ALSA: device is not full duplex\n");
+ return (1);
+ }
+ dir = SND_PCM_OPEN_PLAYBACK;
+ }
+ else
+ {
+ if (!(pcminfo.flags & SND_PCM_INFO_CAPTURE))
+ {
+ fprintf(stderr, "PD-ALSA: device is not full duplex\n");
+ return (1);
+ }
+ dir = SND_PCM_OPEN_CAPTURE;
+ }
+
+ /* try to open the device */
+ if ((err = snd_pcm_open(&alsa_device[0].handle, alsa_devno-1, 0, dir)) < 0)
+ {
+ fprintf(stderr, "PD-ALSA: error opening device: %s\n",
+ snd_strerror(err));
+ return (1);
+ }
+ /* get information from the handle */
+ if (wantinchans)
+ {
+ channelinfo.channel = SND_PCM_CHANNEL_CAPTURE;
+ channelinfo.subdevice = 0;
+ if ((err = snd_pcm_channel_info(alsa_device[0].handle, &channelinfo))
+ < 0)
+ {
+ fprintf(stderr, "PD-ALSA: snd_pcm_channel_info (input): %s\n",
+ snd_strerror(err));
+ return (1);
+ }
+ if (sys_verbose)
+ post("input channels supported: %d-%d\n",
+ channelinfo.min_voices, channelinfo.max_voices);
+
+ if (wantinchans < channelinfo.min_voices)
+ post("increasing input channels to minimum of %d\n",
+ wantinchans = channelinfo.min_voices);
+ if (wantinchans > channelinfo.max_voices)
+ post("decreasing input channels to maximum of %d\n",
+ wantinchans = channelinfo.max_voices);
+ if (alsa_samplewidth == 4 &&
+ !(channelinfo.formats & (1<<SND_PCM_SFMT_S32_LE)))
+ {
+ fprintf(stderr,
+ "PD_ALSA: input doesn't support 32-bit samples; using 16\n");
+ alsa_samplewidth = 2;
+ }
+ if (alsa_samplewidth == 2 &&
+ !(channelinfo.formats & (1<<SND_PCM_SFMT_S16_LE)))
+ {
+ fprintf(stderr,
+ "PD_ALSA: can't find 4 or 2 byte format; giving up\n");
+ return (1);
+ }
+ }
+
+ if (wantoutchans)
+ {
+ channelinfo.channel = SND_PCM_CHANNEL_PLAYBACK;
+ channelinfo.subdevice = 0;
+ if ((err = snd_pcm_channel_info(alsa_device[0].handle, &channelinfo))
+ < 0)
+ {
+ fprintf(stderr, "PD-ALSA: snd_pcm_channel_info (output): %s\n",
+ snd_strerror(err));
+ return (1);
+ }
+ if (sys_verbose)
+ post("output channels supported: %d-%d\n",
+ channelinfo.min_voices, channelinfo.max_voices);
+ if (wantoutchans < channelinfo.min_voices)
+ post("increasing output channels to minimum of %d\n",
+ wantoutchans = channelinfo.min_voices);
+ if (wantoutchans > channelinfo.max_voices)
+ post("decreasing output channels to maximum of %d\n",
+ wantoutchans = channelinfo.max_voices);
+ if (alsa_samplewidth == 4 &&
+ !(channelinfo.formats & (1<<SND_PCM_SFMT_S32_LE)))
+ {
+ fprintf(stderr,
+ "PD_ALSA: output doesn't support 32-bit samples; using 16\n");
+ alsa_samplewidth = 2;
+ }
+ if (alsa_samplewidth == 2 &&
+ !(channelinfo.formats & (1<<SND_PCM_SFMT_S16_LE)))
+ {
+ fprintf(stderr,
+ "PD_ALSA: can't find 4 or 2 byte format; giving up\n");
+ return (1);
+ }
+ }
+
+ linux_setsr(rate);
+ linux_setch(wantinchans, wantoutchans);
+
+ if (wantinchans)
+ alsa_set_params(&alsa_device[0], SND_PCM_CHANNEL_CAPTURE,
+ srate, wantinchans);
+ if (wantoutchans)
+ alsa_set_params(&alsa_device[0], SND_PCM_CHANNEL_PLAYBACK,
+ srate, wantoutchans);
+
+ n_alsa_dev = 1;
+
+ /* check that all is as we think it should be */
+ for (i = 0; i < n_alsa_dev; i++)
+ {
+ /* We need to handle if the rate is not the same for all
+ * devices. For now just hope. */
+ rate = alsa_device[i].setup.format.rate;
+
+ /* It turns out that this checking does not work on all of my cards
+ * - in full duplex on my trident 4dwave the setup on the capture channel
+ * shows a sampling rate of 0. This is not true on my ess solo1. Checking
+ * the dac last helps the problem. All of this needs to be much smarter
+ * anyway (last minute hack). A warning above is all I have time for.
+ */
+ if (rate != srate)
+ {
+ post("PD-ALSA: unable to obtain rate %i using %i", srate, rate);
+ post("PD-ALSA: (despite this warning Pd might still work.)");
+ }
+ }
+ bsize = alsa_samplewidth *
+ (linux_inchannels > linux_outchannels ? linux_inchannels :
+ linux_outchannels) * DACBLKSIZE;
+ alsa_buf = malloc(bsize);
+ if (!alsa_buf)
+ return (1);
+ memset(alsa_buf, 0, bsize);
+ return 0;
+}
+
+void alsa_set_params(t_alsa_dev *dev, int dir, int rate, int voices)
+{
+ int err;
+ struct snd_pcm_channel_params params;
+
+ memset(&dev->info, 0, sizeof(dev->info));
+ dev->info.channel = dir;
+ if ((err = snd_pcm_channel_info(dev->handle, &dev->info) < 0))
+ {
+ fprintf(stderr, "PD-ALSA: error getting channel info: %s\n",
+ snd_strerror(err));
+ }
+ memset(&params, 0, sizeof(params));
+ params.format.interleave = 1; /* may do non-interleaved later */
+ /* format is 2 or 4 bytes per sample depending on what was possible */
+ params.format.format =
+ (alsa_samplewidth == 4 ? SND_PCM_SFMT_S32_LE : SND_PCM_SFMT_S16_LE);
+
+ /*will check this further down -just try for now*/
+ params.format.rate = rate;
+ params.format.voices = voices;
+ params.start_mode = SND_PCM_START_GO; /* seems most reliable */
+ /*do not stop at overrun/underrun*/
+ params.stop_mode = SND_PCM_STOP_ROLLOVER;
+
+ params.channel = dir; /* playback|capture */
+ params.buf.stream.queue_size =
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * voices;
+ params.buf.stream.fill = SND_PCM_FILL_SILENCE_WHOLE;
+ params.mode = SND_PCM_MODE_STREAM;
+
+ if ((err = snd_pcm_channel_params(dev->handle, &params)) < 0)
+ {
+ printf("PD-ALSA: error setting parameters %s", snd_strerror(err));
+ }
+
+ /* This should clear the buffers but does not. There is often noise at
+ startup that sounds like crap left in the buffers - maybe in the lib
+ instead of the driver? Some solution needs to be found.
+ */
+
+ if ((err = snd_pcm_channel_prepare(dev->handle, dir)) < 0)
+ {
+ printf("PD-ALSA: error preparing channel %s", snd_strerror(err));
+ }
+ dev->setup.channel = dir;
+
+ if ((err = snd_pcm_channel_setup(dev->handle, &dev->setup)) < 0)
+ {
+ printf("PD-ALSA: error getting setup %s", snd_strerror(err));
+ }
+ /* for some reason, if you don't writesomething before starting the
+ converters we get trash on startup */
+ if (dir == SND_PCM_CHANNEL_PLAYBACK)
+ {
+ char foo[1024];
+ int xxx = 1024 - (1024 % (linux_outchannels * alsa_samplewidth));
+ int i, r;
+ for (i = 0; i < xxx; i++)
+ foo[i] = 0;
+ if ((r = snd_pcm_write(dev->handle, foo, xxx)) < xxx)
+ fprintf(stderr, "alsa_write: %s\n", snd_strerror(errno));
+ }
+ snd_pcm_channel_go(dev->handle, dir);
+}
+
+void alsa_close_audio(void)
+{
+ int i;
+ for(i = 0; i < n_alsa_dev; i++)
+ snd_pcm_close(alsa_device[i].handle);
+}
+
+/* #define DEBUG_ALSA_XFER */
+
+int alsa_send_dacs(void)
+{
+ static int16_t *sp;
+ t_sample *fp, *fp1, *fp2;
+ int i, j, k, err, devno = 0;
+ int inputcount = 0, outputcount = 0, inputlate = 0, outputlate = 0;
+ int result;
+ snd_pcm_channel_status_t stat;
+ static int callno = 0;
+ static int xferno = 0;
+ int countwas = 0;
+ double timelast;
+ static double timenow;
+ int inchannels = linux_inchannels;
+ int outchannels = linux_outchannels;
+ int inbytesperframe = inchannels * alsa_samplewidth;
+ int outbytesperframe = outchannels * alsa_samplewidth;
+ int intransfersize = DACBLKSIZE * inbytesperframe;
+ int outtransfersize = DACBLKSIZE * outbytesperframe;
+ int alsaerror;
+ int loggederror = 0;
+
+ if (!inchannels && !outchannels)
+ return (SENDDACS_NO);
+ timelast = timenow;
+ timenow = sys_getrealtime();
+
+#ifdef DEBUG_ALSA_XFER
+ if (timenow - timelast > 0.050)
+ fprintf(stderr, "(%d)",
+ (int)(1000 * (timenow - timelast))), fflush(stderr);
+#endif
+
+ callno++;
+ /* get input and output channel status */
+ if (inchannels > 0)
+ {
+ devno = 0;
+ stat.channel = SND_PCM_CHANNEL_CAPTURE;
+ if (alsaerror = snd_pcm_channel_status(alsa_device[devno].handle,
+ &stat))
+ {
+ fprintf(stderr, "snd_pcm_channel_status (input): %s\n",
+ snd_strerror(alsaerror));
+ return (SENDDACS_NO);
+ }
+ inputcount = stat.count;
+ inputlate = (stat.underrun > 0 || stat.overrun > 0);
+ }
+ if (outchannels > 0)
+ {
+ devno = 0;
+ stat.channel = SND_PCM_CHANNEL_PLAYBACK;
+ if (alsaerror = snd_pcm_channel_status(alsa_device[devno].handle,
+ &stat))
+ {
+ fprintf(stderr, "snd_pcm_channel_status (output): %s\n",
+ snd_strerror(alsaerror));
+ return (SENDDACS_NO);
+ }
+ outputcount = stat.count;
+ outputlate = (stat.underrun > 0 || stat.overrun > 0);
+ }
+
+ /* check if input not ready */
+ if (inputcount < intransfersize)
+ {
+ /* fprintf(stderr, "no adc; count %d, free %d, call %d, xfer %d\n",
+ stat.count,
+ stat.free,
+ callno, xferno); */
+ if (outchannels > 0)
+ {
+ /* if there's no input but output is hungry, feed output. */
+ while (outputcount < (linux_advance_samples + ALSA_JITTER)
+ * outbytesperframe)
+ {
+ if (!loggederror)
+ sys_log_error(ERR_RESYNC), loggederror = 1;
+ memset(alsa_buf, 0, outtransfersize);
+ result = snd_pcm_write(alsa_device[devno].handle,
+ alsa_buf, outtransfersize);
+ if (result < outtransfersize)
+ {
+#ifdef DEBUG_ALSA_XFER
+ if (result >= 0 || errno == EAGAIN)
+ fprintf(stderr, "ALSA: write returned %d of %d\n",
+ result, outtransfersize);
+ else fprintf(stderr, "ALSA: write: %s\n",
+ snd_strerror(errno));
+ fprintf(stderr,
+ "inputcount %d, outputcount %d, outbufsize %d\n",
+ inputcount, outputcount,
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * outchannels);
+#endif
+ return (SENDDACS_NO);
+ }
+ stat.channel = SND_PCM_CHANNEL_PLAYBACK;
+ if (alsaerror =
+ snd_pcm_channel_status(alsa_device[devno].handle,
+ &stat))
+ {
+ fprintf(stderr, "snd_pcm_channel_status (output): %s\n",
+ snd_strerror(alsaerror));
+ return (SENDDACS_NO);
+ }
+ outputcount = stat.count;
+ }
+ }
+
+ return SENDDACS_NO;
+ }
+
+ /* if output buffer has at least linux_advance_samples in it, we're
+ not ready for this batch. */
+ if (outputcount > linux_advance_samples * outbytesperframe)
+ {
+ if (inchannels > 0)
+ {
+ while (inputcount > (DACBLKSIZE + ALSA_JITTER) * outbytesperframe)
+ {
+ if (!loggederror)
+ sys_log_error(ERR_RESYNC), loggederror = 1;
+ devno = 0;
+ result = snd_pcm_read(alsa_device[devno].handle, alsa_buf,
+ intransfersize);
+ if (result < intransfersize)
+ {
+#ifdef DEBUG_ALSA_XFER
+ if (result < 0)
+ fprintf(stderr,
+ "snd_pcm_read %d %d: %s\n",
+ callno, xferno, snd_strerror(errno));
+ else fprintf(stderr,
+ "snd_pcm_read %d %d returned only %d\n",
+ callno, xferno, result);
+ fprintf(stderr,
+ "inputcount %d, outputcount %d, inbufsize %d\n",
+ inputcount, outputcount,
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * inchannels);
+#endif
+ return (SENDDACS_NO);
+ }
+ devno = 0;
+ stat.channel = SND_PCM_CHANNEL_CAPTURE;
+ if (alsaerror =
+ snd_pcm_channel_status(alsa_device[devno].handle,
+ &stat))
+ {
+ fprintf(stderr, "snd_pcm_channel_status (input): %s\n",
+ snd_strerror(alsaerror));
+ return (SENDDACS_NO);
+ }
+ inputcount = stat.count;
+ inputlate = (stat.underrun > 0 || stat.overrun > 0);
+ }
+ return (SENDDACS_NO);
+ }
+ }
+ if (sys_getrealtime() - timenow > 0.002)
+ {
+#ifdef DEBUG_ALSA_XFER
+ fprintf(stderr, "check %d took %d msec\n",
+ callno, (int)(1000 * (timenow - timelast))), fflush(stderr);
+#endif
+ sys_log_error(ERR_DACSLEPT);
+ timenow = sys_getrealtime();
+ }
+ if (inputlate || outputlate)
+ sys_log_error(ERR_DATALATE);
+
+ /* do output */
+ /* this "for" loop won't work for more than one device. */
+ for (devno = 0, fp = sys_soundout; devno < (outchannels > 0); devno++,
+ fp += 128)
+ {
+ if (alsa_samplewidth == 4)
+ {
+ for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--;
+ j += outchannels, fp2++)
+ {
+ float s1 = *fp2 * INT32_MAX;
+ ((t_alsa_sample32 *)alsa_buf)[j] = CLIP32(s1);
+ }
+ }
+ }
+ else
+ {
+ for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--;
+ j += outchannels, fp2++)
+ {
+ int s = *fp2 * 32767.;
+ if (s > 32767)
+ s = 32767;
+ else if (s < -32767)
+ s = -32767;
+ ((t_alsa_sample16 *)alsa_buf)[j] = s;
+ }
+ }
+ }
+
+ result = snd_pcm_write(alsa_device[devno].handle, alsa_buf,
+ outtransfersize);
+ if (result < outtransfersize)
+ {
+#ifdef DEBUG_ALSA_XFER
+ if (result >= 0 || errno == EAGAIN)
+ fprintf(stderr, "ALSA: write returned %d of %d\n",
+ result, outtransfersize);
+ else fprintf(stderr, "ALSA: write: %s\n",
+ snd_strerror(errno));
+ fprintf(stderr,
+ "inputcount %d, outputcount %d, outbufsize %d\n",
+ inputcount, outputcount,
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * outchannels);
+#endif
+ sys_log_error(ERR_DACSLEPT);
+ return (SENDDACS_NO);
+ }
+ }
+ /* zero out the output buffer */
+ memset(sys_soundout, 0, DACBLKSIZE * sizeof(*sys_soundout) *
+ linux_outchannels);
+ if (sys_getrealtime() - timenow > 0.002)
+ {
+#if DEBUG_ALSA_XFER
+ fprintf(stderr, "output %d took %d msec\n",
+ callno, (int)(1000 * (timenow - timelast))), fflush(stderr);
+#endif
+ timenow = sys_getrealtime();
+ sys_log_error(ERR_DACSLEPT);
+ }
+
+ /* do input */
+ for (devno = 0, fp = sys_soundin; devno < (linux_inchannels > 0); devno++,
+ fp += 128)
+ {
+ result = snd_pcm_read(alsa_device[devno].handle, alsa_buf,
+ intransfersize);
+ if (result < intransfersize)
+ {
+#ifdef DEBUG_ALSA_XFER
+ if (result < 0)
+ fprintf(stderr,
+ "snd_pcm_read %d %d: %s\n",
+ callno, xferno, snd_strerror(errno));
+ else fprintf(stderr,
+ "snd_pcm_read %d %d returned only %d\n",
+ callno, xferno, result);
+ fprintf(stderr,
+ "inputcount %d, outputcount %d, inbufsize %d\n",
+ inputcount, outputcount,
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * inchannels);
+#endif
+ sys_log_error(ERR_ADCSLEPT);
+ return (SENDDACS_NO);
+ }
+ if (alsa_samplewidth == 4)
+ {
+ for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--;
+ j += inchannels, fp2++)
+ *fp2 = (float) ((t_alsa_sample32 *)alsa_buf)[j]
+ * (1./ INT32_MAX);
+ }
+ }
+ else
+ {
+ for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; j += inchannels, fp2++)
+ *fp2 = (float) ((t_alsa_sample16 *)alsa_buf)[j]
+ * 3.051850e-05;
+ }
+ }
+ }
+ xferno++;
+ if (sys_getrealtime() - timenow > 0.002)
+ {
+#ifdef DEBUG_ALSA_XFER
+ fprintf(stderr, "routine took %d msec\n",
+ (int)(1000 * (sys_getrealtime() - timenow)));
+#endif
+ sys_log_error(ERR_ADCSLEPT);
+ }
+ return SENDDACS_YES;
+}
+
+#endif /* ALSA99 */
+
+/* support for ALSA pcmv2 api by Karl MacMillan<karlmac@peabody.jhu.edu> */
+
+#ifdef ALSA01
+
+static void check_error(int err, const char *why)
+{
+ if (err < 0)
+ fprintf(stderr, "%s: %s\n", why, snd_strerror(err));
+}
+
+static int alsa_open_audio(int wantinchans, int wantoutchans, int srate)
+{
+ int err, inchans = 0, outchans = 0, subunitdir;
+ char devname[512];
+ snd_pcm_hw_params_t* hw_params;
+ snd_pcm_sw_params_t* sw_params;
+ snd_output_t* out;
+ int frag_size = (linux_fragsize ? linux_fragsize : ALSA_DEFFRAGSIZE);
+ int nfrags, i;
+ short* tmp_buf;
+ unsigned int tmp_uint;
+ int advwas = sys_schedadvance;
+
+ if (linux_nfragment)
+ {
+ nfrags = linux_nfragment;
+ sys_schedadvance = (frag_size * linux_nfragment * 1.0e6) / srate;
+ }
+ else nfrags = sys_schedadvance * (float)srate / (1e6 * frag_size);
+
+ if (sys_verbose || (sys_schedadvance != advwas))
+ post("audio buffer set to %d", (int)(0.001 * sys_schedadvance));
+ if (wantinchans || wantoutchans)
+ alsa_checkversion();
+ if (wantinchans)
+ {
+ err = snd_pcm_open(&alsa_device.inhandle, alsa_devname,
+ SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
+
+ check_error(err, "snd_pcm_open (input)");
+ if (err < 0)
+ inchans = 0;
+ else
+ {
+ inchans = wantinchans;
+ snd_pcm_nonblock(alsa_device.inhandle, 1);
+ }
+ }
+ if (wantoutchans)
+ {
+ err = snd_pcm_open(&alsa_device.outhandle, alsa_devname,
+ SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+
+ check_error(err, "snd_pcm_open (output)");
+ if (err < 0)
+ outchans = 0;
+ else
+ {
+ outchans = wantoutchans;
+ snd_pcm_nonblock(alsa_device.outhandle, 1);
+ }
+ }
+ if (inchans)
+ {
+ if (sys_verbose)
+ post("opening sound input...");
+ err = snd_pcm_hw_params_malloc(&hw_params);
+ check_error(err, "snd_pcm_hw_params_malloc (input)");
+
+ // get the default params
+ err = snd_pcm_hw_params_any(alsa_device.inhandle, hw_params);
+ check_error(err, "snd_pcm_hw_params_any (input)");
+ // set interleaved access - FIXME deal with other access types
+ err = snd_pcm_hw_params_set_access(alsa_device.inhandle, hw_params,
+ SND_PCM_ACCESS_RW_INTERLEAVED);
+ check_error(err, "snd_pcm_hw_params_set_access (input)");
+ // Try to set 32 bit format first
+ err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params,
+ SND_PCM_FORMAT_S32);
+ if (err < 0)
+ {
+ /* fprintf(stderr,
+ "PD-ALSA: 32 bit format not available - using 16\n"); */
+ err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params,
+ SND_PCM_FORMAT_S16);
+ check_error(err, "snd_pcm_hw_params_set_format (input)");
+ alsa_samplewidth = 2;
+ }
+ else
+ {
+ alsa_samplewidth = 4;
+ }
+ post("Sample width set to %d bytes", alsa_samplewidth);
+ // set the subformat
+ err = snd_pcm_hw_params_set_subformat(alsa_device.inhandle, hw_params,
+ SND_PCM_SUBFORMAT_STD);
+ check_error(err, "snd_pcm_hw_params_set_subformat (input)");
+ // set the number of channels
+ tmp_uint = inchans;
+ err = snd_pcm_hw_params_set_channels_min(alsa_device.inhandle,
+ hw_params, &tmp_uint);
+ check_error(err, "snd_pcm_hw_params_set_channels (input)");
+ if (tmp_uint != (unsigned)inchans)
+ post("ALSA: set input channels to %d", tmp_uint);
+ inchans = tmp_uint;
+ // set the sampling rate
+ err = snd_pcm_hw_params_set_rate_min(alsa_device.inhandle, hw_params,
+ &srate, 0);
+ check_error(err, "snd_pcm_hw_params_set_rate_min (input)");
+#if 0
+ err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir);
+ post("input sample rate %d", err);
+#endif
+ // set the period - ie frag size
+ // post("fragsize a %d", frag_size);
+
+ /* LATER try this to get a recommended period size...
+ right now, it trips an assertion failure in ALSA lib */
+#if 0
+ post("input period was %d, min %d, max %d\n",
+ snd_pcm_hw_params_get_period_size(hw_params, 0),
+ snd_pcm_hw_params_get_period_size_min(hw_params, 0),
+ snd_pcm_hw_params_get_period_size_max(hw_params, 0));
+#endif
+ err = snd_pcm_hw_params_set_period_size_near(alsa_device.inhandle,
+ hw_params,
+ (snd_pcm_uframes_t)
+ frag_size, 0);
+ check_error(err, "snd_pcm_hw_params_set_period_size_near (input)");
+ // post("fragsize b %d", frag_size);
+ // set the number of periods - ie numfrags
+ // post("nfrags a %d", nfrags);
+ err = snd_pcm_hw_params_set_periods_near(alsa_device.inhandle,
+ hw_params, nfrags, 0);
+ check_error(err, "snd_pcm_hw_params_set_periods_near (input)");
+ // set the buffer size
+ err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.inhandle,
+ hw_params, nfrags * frag_size);
+ check_error(err, "snd_pcm_hw_params_set_buffer_size_near (input)");
+
+ err = snd_pcm_hw_params(alsa_device.inhandle, hw_params);
+ check_error(err, "snd_pcm_hw_params (input)");
+
+ snd_pcm_hw_params_free(hw_params);
+
+ err = snd_pcm_sw_params_malloc(&sw_params);
+ check_error(err, "snd_pcm_sw_params_malloc (input)");
+ err = snd_pcm_sw_params_current(alsa_device.inhandle, sw_params);
+ check_error(err, "snd_pcm_sw_params_current (input)");
+#if 1
+ err = snd_pcm_sw_params_set_start_mode(alsa_device.inhandle, sw_params,
+ SND_PCM_START_EXPLICIT);
+ check_error(err, "snd_pcm_sw_params_set_start_mode (input)");
+ err = snd_pcm_sw_params_set_xrun_mode(alsa_device.inhandle, sw_params,
+ SND_PCM_XRUN_NONE);
+ check_error(err, "snd_pcm_sw_params_set_xrun_mode (input)");
+#else
+ err = snd_pcm_sw_params_set_start_threshold(alsa_device.inhandle,
+ sw_params, nfrags * frag_size);
+ check_error(err, "snd_pcm_sw_params_set_start_threshold (input)");
+ err = snd_pcm_sw_params_set_stop_threshold(alsa_device.inhandle,
+ sw_params, 1);
+ check_error(err, "snd_pcm_sw_params_set_stop_threshold (input)");
+#endif
+
+ err = snd_pcm_sw_params_set_avail_min(alsa_device.inhandle, sw_params,
+ frag_size);
+ check_error(err, "snd_pcm_sw_params_set_avail_min (input)");
+ err = snd_pcm_sw_params(alsa_device.inhandle, sw_params);
+ check_error(err, "snd_pcm_sw_params (input)");
+
+ snd_pcm_sw_params_free(sw_params);
+
+ snd_output_stdio_attach(&out, stderr, 0);
+#if 0
+ if (sys_verbose)
+ {
+ snd_pcm_dump_hw_setup(alsa_device.inhandle, out);
+ snd_pcm_dump_sw_setup(alsa_device.inhandle, out);
+ }
+#endif
+ }
+
+ if (outchans)
+ {
+ int foo;
+ if (sys_verbose)
+ post("opening sound output...");
+ err = snd_pcm_hw_params_malloc(&hw_params);
+ check_error(err, "snd_pcm_sw_params (output)");
+
+ // get the default params
+ err = snd_pcm_hw_params_any(alsa_device.outhandle, hw_params);
+ check_error(err, "snd_pcm_hw_params_any (output)");
+ // set interleaved access - FIXME deal with other access types
+ err = snd_pcm_hw_params_set_access(alsa_device.outhandle, hw_params,
+ SND_PCM_ACCESS_RW_INTERLEAVED);
+ check_error(err, "snd_pcm_hw_params_set_access (output)");
+ // Try to set 32 bit format first
+ err = snd_pcm_hw_params_set_format(alsa_device.outhandle, hw_params,
+ SND_PCM_FORMAT_S32);
+ if (err < 0)
+ {
+ err = snd_pcm_hw_params_set_format(alsa_device.outhandle,
+ hw_params,SND_PCM_FORMAT_S16);
+ check_error(err, "snd_pcm_hw_params_set_format (output)");
+ /* fprintf(stderr,
+ "PD-ALSA: 32 bit format not available - using 16\n"); */
+ alsa_samplewidth = 2;
+ }
+ else
+ {
+ alsa_samplewidth = 4;
+ }
+ // set the subformat
+ err = snd_pcm_hw_params_set_subformat(alsa_device.outhandle, hw_params,
+ SND_PCM_SUBFORMAT_STD);
+ check_error(err, "snd_pcm_hw_params_set_subformat (output)");
+ // set the number of channels
+ tmp_uint = outchans;
+ err = snd_pcm_hw_params_set_channels_min(alsa_device.outhandle,
+ hw_params, &tmp_uint);
+ check_error(err, "snd_pcm_hw_params_set_channels (output)");
+ if (tmp_uint != (unsigned)outchans)
+ post("alsa: set output channels to %d", tmp_uint);
+ outchans = tmp_uint;
+ // set the sampling rate
+ err = snd_pcm_hw_params_set_rate_min(alsa_device.outhandle, hw_params,
+ &srate, 0);
+ check_error(err, "snd_pcm_hw_params_set_rate_min (output)");
+#if 0
+ err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir);
+ post("output sample rate %d", err);
+#endif
+ // set the period - ie frag size
+#if 0
+ post("output period was %d, min %d, max %d\n",
+ snd_pcm_hw_params_get_period_size(hw_params, 0),
+ snd_pcm_hw_params_get_period_size_min(hw_params, 0),
+ snd_pcm_hw_params_get_period_size_max(hw_params, 0));
+#endif
+ // post("fragsize c %d", frag_size);
+ err = snd_pcm_hw_params_set_period_size_near(alsa_device.outhandle,
+ hw_params,
+ (snd_pcm_uframes_t)
+ frag_size, 0);
+ // post("fragsize d %d", frag_size);
+ check_error(err, "snd_pcm_hw_params_set_period_size_near (output)");
+ // set the number of periods - ie numfrags
+ err = snd_pcm_hw_params_set_periods_near(alsa_device.outhandle,
+ hw_params, nfrags, 0);
+ check_error(err, "snd_pcm_hw_params_set_periods_near (output)");
+ // set the buffer size
+ err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.outhandle,
+ hw_params, nfrags * frag_size);
+
+ check_error(err, "snd_pcm_hw_params_set_buffer_size_near (output)");
+
+ err = snd_pcm_hw_params(alsa_device.outhandle, hw_params);
+ check_error(err, "snd_pcm_hw_params (output)");
+
+ snd_pcm_hw_params_free(hw_params);
+
+ err = snd_pcm_sw_params_malloc(&sw_params);
+ check_error(err, "snd_pcm_sw_params_malloc (output)");
+ err = snd_pcm_sw_params_current(alsa_device.outhandle, sw_params);
+ check_error(err, "snd_pcm_sw_params_current (output)");
+#if 1
+ err = snd_pcm_sw_params_set_start_mode(alsa_device.outhandle,
+ sw_params,
+ SND_PCM_START_EXPLICIT);
+ check_error(err, "snd_pcm_sw_params_set_start_mode (output)");
+ err = snd_pcm_sw_params_set_xrun_mode(alsa_device.outhandle, sw_params,
+ SND_PCM_XRUN_NONE);
+ check_error(err, "snd_pcm_sw_params_set_xrun_mode (output)");
+#else
+ err = snd_pcm_sw_params_set_start_threshold(alsa_device.inhandle,
+ sw_params, nfrags * frag_size);
+ check_error(err, "snd_pcm_sw_params_set_start_threshold (output)");
+ err = snd_pcm_sw_params_set_stop_threshold(alsa_device.inhandle,
+ sw_params, 1);
+ check_error(err, "snd_pcm_sw_params_set_stop_threshold (output)");
+#endif
+
+ err = snd_pcm_sw_params_set_avail_min(alsa_device.outhandle, sw_params,
+ frag_size);
+ check_error(err, "snd_pcm_sw_params_set_avail_min (output)");
+ err = snd_pcm_sw_params(alsa_device.outhandle, sw_params);
+ check_error(err, "snd_pcm_sw_params (output)");
+
+ snd_pcm_sw_params_free(sw_params);
+
+ snd_output_stdio_attach(&out, stderr, 0);
+#if 0
+ if (sys_verbose)
+ {
+ snd_pcm_dump_hw_setup(alsa_device.outhandle, out);
+ snd_pcm_dump_sw_setup(alsa_device.outhandle, out);
+ }
+#endif
+ }
+
+ linux_setsr(srate);
+ linux_setch(inchans, outchans);
+
+ if (inchans)
+ snd_pcm_prepare(alsa_device.inhandle);
+ if (outchans)
+ snd_pcm_prepare(alsa_device.outhandle);
+
+ // if duplex we can link the channels so they start together
+ if (inchans && outchans)
+ snd_pcm_link(alsa_device.inhandle, alsa_device.outhandle);
+
+ // set up the buffer
+ if (outchans > inchans)
+ alsa_buf = (short *)calloc(sizeof(char) * alsa_samplewidth, DACBLKSIZE
+ * outchans);
+ else
+ alsa_buf = (short *)calloc(sizeof(char) * alsa_samplewidth, DACBLKSIZE
+ * inchans);
+ // fill the buffer with silence
+ if (outchans)
+ {
+ i = nfrags + 1;
+ while (i--)
+ snd_pcm_writei(alsa_device.outhandle, alsa_buf, frag_size);
+ }
+
+ // set up the status variables
+ err = snd_pcm_status_malloc(&in_status);
+ check_error(err, "snd_pcm_status_malloc");
+ err = snd_pcm_status_malloc(&out_status);
+ check_error(err, "snd_pcm_status_malloc");
+
+ // start the device
+#if 1
+ if (outchans)
+ {
+ err = snd_pcm_start(alsa_device.outhandle);
+ check_error(err, "snd_pcm_start");
+ }
+ else if (inchans)
+ {
+ err = snd_pcm_start(alsa_device.inhandle);
+ check_error(err, "snd_pcm_start");
+ }
+#endif
+
+ return 0;
+}
+
+void alsa_close_audio(void)
+{
+ int err;
+ if (linux_inchannels)
+ {
+ err = snd_pcm_close(alsa_device.inhandle);
+ check_error(err, "snd_pcm_close (input)");
+ }
+ if (linux_outchannels)
+ {
+ err = snd_pcm_close(alsa_device.outhandle);
+ check_error(err, "snd_pcm_close (output)");
+ }
+}
+
+// #define DEBUG_ALSA_XFER
+
+int alsa_send_dacs(void)
+{
+ static int16_t *sp;
+ static int xferno = 0;
+ static int callno = 0;
+ static double timenow;
+ double timelast;
+ t_sample *fp, *fp1, *fp2;
+ int i, j, k, err, devno = 0;
+ int inputcount = 0, outputcount = 0, inputlate = 0, outputlate = 0;
+ int result;
+ int inchannels = linux_inchannels;
+ int outchannels = linux_outchannels;
+ unsigned int intransfersize = DACBLKSIZE;
+ unsigned int outtransfersize = DACBLKSIZE;
+
+ // get the status
+ if (!inchannels && !outchannels)
+ {
+ return SENDDACS_NO;
+ }
+
+ timelast = timenow;
+ timenow = sys_getrealtime();
+
+#ifdef DEBUG_ALSA_XFER
+ if (timenow - timelast > 0.050)
+ fprintf(stderr, "(%d)",
+ (int)(1000 * (timenow - timelast))), fflush(stderr);
+#endif
+
+ callno++;
+
+ if (inchannels)
+ {
+ snd_pcm_status(alsa_device.inhandle, in_status);
+ if (snd_pcm_status_get_avail(in_status) < intransfersize)
+ return SENDDACS_NO;
+ }
+ if (outchannels)
+ {
+ snd_pcm_status(alsa_device.outhandle, out_status);
+ if (snd_pcm_status_get_avail(out_status) < outtransfersize)
+ return SENDDACS_NO;
+ }
+
+ /* do output */
+ if (outchannels)
+ {
+ fp = sys_soundout;
+ if (alsa_samplewidth == 4)
+ {
+ for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--;
+ j += outchannels, fp2++)
+ {
+ float s1 = *fp2 * INT32_MAX;
+ ((t_alsa_sample32 *)alsa_buf)[j] = CLIP32(s1);
+ }
+ }
+ }
+ else
+ {
+ for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--;
+ j += outchannels, fp2++)
+ {
+ int s = *fp2 * 32767.;
+ if (s > 32767)
+ s = 32767;
+ else if (s < -32767)
+ s = -32767;
+ ((t_alsa_sample16 *)alsa_buf)[j] = s;
+ }
+ }
+ }
+
+ result = snd_pcm_writei(alsa_device.outhandle, alsa_buf,
+ outtransfersize);
+ if (result != (int)outtransfersize)
+ {
+ #ifdef DEBUG_ALSA_XFER
+ if (result >= 0 || errno == EAGAIN)
+ fprintf(stderr, "ALSA: write returned %d of %d\n",
+ result, outtransfersize);
+ else fprintf(stderr, "ALSA: write: %s\n",
+ snd_strerror(errno));
+ fprintf(stderr,
+ "inputcount %d, outputcount %d, outbufsize %d\n",
+ inputcount, outputcount,
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * outchannels);
+ #endif
+ sys_log_error(ERR_DACSLEPT);
+ return (SENDDACS_NO);
+ }
+
+ /* zero out the output buffer */
+ memset(sys_soundout, 0, DACBLKSIZE * sizeof(*sys_soundout) *
+ linux_outchannels);
+ if (sys_getrealtime() - timenow > 0.002)
+ {
+ #ifdef DEBUG_ALSA_XFER
+ fprintf(stderr, "output %d took %d msec\n",
+ callno, (int)(1000 * (timenow - timelast))), fflush(stderr);
+ #endif
+ timenow = sys_getrealtime();
+ sys_log_error(ERR_DACSLEPT);
+ }
+ }
+ /* do input */
+ if (linux_inchannels)
+ {
+ result = snd_pcm_readi(alsa_device.inhandle, alsa_buf, intransfersize);
+ if (result < (int)intransfersize)
+ {
+#ifdef DEBUG_ALSA_XFER
+ if (result < 0)
+ fprintf(stderr,
+ "snd_pcm_read %d %d: %s\n",
+ callno, xferno, snd_strerror(errno));
+ else fprintf(stderr,
+ "snd_pcm_read %d %d returned only %d\n",
+ callno, xferno, result);
+ fprintf(stderr,
+ "inputcount %d, outputcount %d, inbufsize %d\n",
+ inputcount, outputcount,
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * inchannels);
+#endif
+ sys_log_error(ERR_ADCSLEPT);
+ return (SENDDACS_NO);
+ }
+ fp = sys_soundin;
+ if (alsa_samplewidth == 4)
+ {
+ for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--;
+ j += inchannels, fp2++)
+ *fp2 = (float) ((t_alsa_sample32 *)alsa_buf)[j]
+ * (1./ INT32_MAX);
+ }
+ }
+ else
+ {
+ for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; j += inchannels,
+ fp2++)
+ *fp2 = (float) ((t_alsa_sample16 *)alsa_buf)[j]
+ * 3.051850e-05;
+ }
+ }
+ }
+ xferno++;
+ if (sys_getrealtime() - timenow > 0.002)
+ {
+#ifdef DEBUG_ALSA_XFER
+ fprintf(stderr, "routine took %d msec\n",
+ (int)(1000 * (sys_getrealtime() - timenow)));
+#endif
+ sys_log_error(ERR_ADCSLEPT);
+ }
+ return SENDDACS_YES;
+}
+
+void alsa_resync( void)
+{
+ int i, result;
+ memset(alsa_buf, 0,
+ sizeof(char) * alsa_samplewidth * DACBLKSIZE * linux_outchannels);
+ for (i = 0; i < 100; i++)
+ {
+ result = snd_pcm_writei(alsa_device.outhandle, alsa_buf,
+ DACBLKSIZE);
+ if (result != (int)DACBLKSIZE)
+ break;
+ }
+ post("%d written", i);
+}
+
+
+#endif /* ALSA01 */
+
+/***************************************************
+ * Code using the RME_9652 API
+ */
+
+ /*
+ trying native device for future use of native memory map:
+ because of busmaster if you dont use the dac, you dont need
+ CPU Power und also no nearly no CPU-Power is used in device
+
+ since always all DAs and ADs are synced (else they wouldnt work)
+ we use linux_dacs[0], linux_adcs[0]
+ */
+
+#ifdef RME_HAMMERFALL
+
+#define RME9652_MAX_CHANNELS 26
+
+#define RME9652_CH_PER_NATIVE_DEVICE 1
+
+static int rme9652_dac_devices[RME9652_MAX_CHANNELS];
+static int rme9652_adc_devices[RME9652_MAX_CHANNELS];
+
+static char rme9652_dsp_dac[] = "/dev/rme9652/C0da%d";
+static char rme9652_dsp_adc[] = "/dev/rme9652/C0ad%d";
+
+static int num_of_rme9652_dac = 0;
+static int num_of_rme9652_adc = 0;
+
+static int rme_soundindevonset = 1;
+static int rme_soundoutdevonset = 1;
+
+void rme_soundindev(int which)
+{
+ rme_soundindevonset = which;
+}
+
+void rme_soundoutdev(int which)
+{
+ rme_soundoutdevonset = which;
+}
+
+void rme9652_configure(int dev, int fd,int srate, int dac) {
+ int orig, param, nblk;
+ audio_buf_info ainfo;
+ orig = param = srate;
+
+ /* samplerate */
+
+ fprintf(stderr,"RME9652: configuring %d, fd=%d, sr=%d\n, dac=%d\n",
+ dev,fd,srate,dac);
+
+ if (ioctl(fd,SNDCTL_DSP_SPEED,&param) == -1)
+ fprintf(stderr,"RME9652: Could not set sampling rate for device\n");
+ else if( orig != param )
+ fprintf(stderr,"RME9652: sampling rate: wanted %d, got %d\n",
+ orig, param );
+
+ // setting the correct samplerate (could be different than expected)
+ srate = param;
+
+
+ /* setting resolution */
+
+ /* use ctrlpanel to change, experiment, channels 1 */
+
+ orig = param = AFMT_S16_BE;
+ if (ioctl(fd,SNDCTL_DSP_SETFMT,&param) == -1)
+ fprintf(stderr,"RME9652: Could not set DSP format\n");
+ else if( orig != param )
+ fprintf(stderr,"RME9652: DSP format: wanted %d, got %d\n",orig, param );
+
+ /* setting channels */
+ orig = param = RME9652_CH_PER_NATIVE_DEVICE;
+
+ if (ioctl(fd,SNDCTL_DSP_CHANNELS,&param) == -1)
+ fprintf(stderr,"RME9652: Could not set channels\n");
+ else if( orig != param )
+ fprintf(stderr,"RME9652: num channels: wanted %d, got %d\n",orig, param );
+
+ if (dac)
+ {
+
+ /* use "free space" to learn the buffer size. Normally you
+ should set this to your own desired value; but this seems not
+ to be implemented uniformly across different sound cards. LATER
+ we should figure out what to do if the requested scheduler advance
+ is greater than this buffer size; for now, we just print something
+ out. */
+
+ if( ioctl(linux_dacs[0].d_fd, SOUND_PCM_GETOSPACE,&ainfo) < 0 )
+ fprintf(stderr,"RME: ioctl on output device %d failed",dev);
+
+ linux_dacs[0].d_bufsize = ainfo.bytes;
+
+ fprintf(stderr,"RME: ioctl SOUND_PCM_GETOSPACE says %d buffsize\n",
+ linux_dacs[0].d_bufsize);
+
+
+ if (linux_advance_samples * (RME_SAMPLEWIDTH *
+ RME9652_CH_PER_NATIVE_DEVICE)
+ > linux_dacs[0].d_bufsize - RME_BYTESPERCHAN)
+ {
+ fprintf(stderr,
+ "RME: requested audio buffer size %d limited to %d\n",
+ linux_advance_samples
+ * (RME_SAMPLEWIDTH * RME9652_CH_PER_NATIVE_DEVICE),
+ linux_dacs[0].d_bufsize);
+ linux_advance_samples =
+ (linux_dacs[0].d_bufsize - RME_BYTESPERCHAN)
+ / (RME_SAMPLEWIDTH *RME9652_CH_PER_NATIVE_DEVICE);
+ }
+ }
+}
+
+
+int rme9652_open_audio(int inchans, int outchans,int srate)
+{
+ int orig;
+ int tmp;
+ int inchannels = 0,outchannels = 0;
+ char devname[20];
+ int i;
+ char buf[RME_SAMPLEWIDTH*RME9652_CH_PER_NATIVE_DEVICE*DACBLKSIZE];
+ int num_devs = 0;
+ audio_buf_info ainfo;
+
+ linux_nindevs = linux_noutdevs = 0;
+
+ if (sys_verbose)
+ post("RME open");
+ /* First check if we can */
+ /* open the write ports */
+
+ for (num_devs=0; outchannels < outchans; num_devs++)
+ {
+ int channels = RME9652_CH_PER_NATIVE_DEVICE;
+
+ sprintf(devname, rme9652_dsp_dac, num_devs + rme_soundoutdevonset);
+ if ((tmp = open(devname,O_WRONLY)) == -1)
+ {
+ DEBUG(fprintf(stderr,"RME9652: failed to open %s writeonly\n",
+ devname);)
+ break;
+ }
+ DEBUG(fprintf(stderr,"RME9652: out device Nr. %d (%d) on %s\n",
+ linux_noutdevs+1,tmp,devname);)
+
+ if (outchans > outchannels)
+ {
+ rme9652_dac_devices[linux_noutdevs] = tmp;
+ linux_noutdevs++;
+ outchannels += channels;
+ }
+ else close(tmp);
+ }
+ if( linux_noutdevs > 0)
+ linux_dacs[0].d_fd = rme9652_dac_devices[0];
+
+ /* Second check if we can */
+ /* open the read ports */
+
+ for (num_devs=0; inchannels < inchans; num_devs++)
+ {
+ int channels = RME9652_CH_PER_NATIVE_DEVICE;
+
+ sprintf(devname, rme9652_dsp_adc, num_devs+rme_soundindevonset);
+
+ if ((tmp = open(devname,O_RDONLY)) == -1)
+ {
+ DEBUG(fprintf(stderr,"RME9652: failed to open %s readonly\n",
+ devname);)
+ break;
+ }
+ DEBUG(fprintf(stderr,"RME9652: in device Nr. %d (%d) on %s\n",
+ linux_nindevs+1,tmp,devname);)
+
+ if (inchans > inchannels)
+ {
+ rme9652_adc_devices[linux_nindevs] = tmp;
+ linux_nindevs++;
+ inchannels += channels;
+ }
+ else
+ close(tmp);
+ }
+ if(linux_nindevs > 0)
+ linux_adcs[0].d_fd = rme9652_adc_devices[0];
+
+ /* configure soundcards */
+
+ rme9652_configure(0, linux_adcs[0].d_fd,srate, 0);
+ rme9652_configure(0, linux_dacs[0].d_fd,srate, 1);
+
+ /* We have to do a read to start the engine. This is
+ necessary because sys_send_dacs waits until the input
+ buffer is filled and only reads on a filled buffer.
+ This is good, because it's a way to make sure that we
+ will not block */
+
+ if (linux_nindevs)
+ {
+ fprintf(stderr,("RME9652: starting read engine ... "));
+
+
+ for (num_devs=0; num_devs < linux_nindevs; num_devs++)
+ read(rme9652_adc_devices[num_devs],
+ buf, RME_SAMPLEWIDTH* RME9652_CH_PER_NATIVE_DEVICE*
+ DACBLKSIZE);
+
+
+ for (num_devs=0; num_devs < linux_noutdevs; num_devs++)
+ write(rme9652_dac_devices[num_devs],
+ buf, RME_SAMPLEWIDTH* RME9652_CH_PER_NATIVE_DEVICE*
+ DACBLKSIZE);
+
+ if(linux_noutdevs)
+ ioctl(rme9652_dac_devices[0],SNDCTL_DSP_SYNC);
+
+ fprintf(stderr,"done\n");
+ }
+
+ linux_setsr(srate);
+ linux_setch(linux_nindevs, linux_noutdevs);
+
+ num_of_rme9652_dac = linux_noutdevs;
+ num_of_rme9652_adc = linux_nindevs;
+
+ if(linux_noutdevs)linux_noutdevs=1;
+ if(linux_nindevs)linux_nindevs=1;
+
+ /* trick RME9652 behaves as one device fromread write pointers */
+ return (0);
+}
+
+void rme9652_close_audio( void)
+{
+ int i;
+ for (i=0;i<num_of_rme9652_dac;i++)
+ close(rme9652_dac_devices[i]);
+
+ for (i=0;i<num_of_rme9652_adc;i++)
+ close(rme9652_adc_devices[i]);
+}
+
+
+/* query audio devices for "available" data size. */
+/* not needed because oss_calcspace does the same */
+static int rme9652_calcspace(void)
+{
+ audio_buf_info ainfo;
+
+
+ /* one for all */
+
+ if (ioctl(linux_dacs[0].d_fd, SOUND_PCM_GETOSPACE,&ainfo) < 0)
+ fprintf(stderr,
+ "RME9652: calc ioctl SOUND_PCM_GETOSPACE on output device fd %d failed\n",
+ linux_dacs[0].d_fd);
+ linux_dacs[0].d_space = ainfo.bytes;
+
+ if (ioctl(linux_adcs[0].d_fd, SOUND_PCM_GETISPACE,&ainfo) < 0)
+ fprintf(stderr,
+ "RME9652: calc ioctl SOUND_PCM_GETISPACE on input device fd %d failed\n",
+ rme9652_adc_devices[0]);
+
+ linux_adcs[0].d_space = ainfo.bytes;
+
+ return 1;
+}
+
+/* this call resyncs audio output and input which will cause discontinuities
+in audio output and/or input. */
+
+static void rme9652_doresync( void)
+{
+ if(linux_noutdevs)
+ ioctl(rme9652_dac_devices[0],SNDCTL_DSP_SYNC);
+}
+
+static int mycount =0;
+
+int rme9652_send_dacs(void)
+{
+ float *fp;
+ long fill;
+ int i, j, dev;
+ /* the maximum number of samples we should have in the ADC buffer */
+ t_rme_sample buf[RME9652_CH_PER_NATIVE_DEVICE*DACBLKSIZE], *sp;
+
+ double timeref, timenow;
+
+ mycount++;
+
+ if (!linux_nindevs && !linux_noutdevs) return (0);
+
+ rme9652_calcspace();
+
+ /* do output */
+
+ timeref = sys_getrealtime();
+
+ if(linux_noutdevs){
+
+ if (linux_dacs[0].d_dropcount)
+ linux_dacs[0].d_dropcount--;
+ else{
+ /* fprintf(stderr,"output %d\n", linux_outchannels);*/
+
+ for(j=0;j<linux_outchannels;j++){
+
+ t_rme_sample *a,*b,*c,*d;
+ float *fp1,*fp2,*fp3,*fp4;
+
+ fp1 = sys_soundout + j*DACBLKSIZE-4;
+ fp2 = fp1 + 1;
+ fp3 = fp1 + 2;
+ fp4 = fp1 + 3;
+ a = buf-4;
+ b=a+1;
+ c=a+2;
+ d=a+3;
+
+ for (i = DACBLKSIZE>>2;i--;)
+ {
+ float s1 = *(fp1+=4) * INT32_MAX;
+ float s2 = *(fp2+=4) * INT32_MAX;
+ float s3 = *(fp3+=4) * INT32_MAX;
+ float s4 = *(fp4+=4) * INT32_MAX;
+
+ *(a+=4) = CLIP32(s1);
+ *(b+=4) = CLIP32(s2);
+ *(c+=4) = CLIP32(s3);
+ *(d+=4) = CLIP32(s4);
+ }
+
+ linux_dacs_write(rme9652_dac_devices[j],buf,RME_BYTESPERCHAN);
+ }
+ }
+
+ if ((timenow = sys_getrealtime()) - timeref > 0.02)
+ sys_log_error(ERR_DACSLEPT);
+ timeref = timenow;
+ }
+
+ memset(sys_soundout, 0,
+ linux_outchannels * (sizeof(float) * DACBLKSIZE));
+
+ /* do input */
+
+ if(linux_nindevs) {
+
+ for(j=0;j<linux_inchannels;j++){
+
+ linux_adcs_read(rme9652_adc_devices[j], buf, RME_BYTESPERCHAN);
+
+ if ((timenow = sys_getrealtime()) - timeref > 0.02)
+ sys_log_error(ERR_ADCSLEPT);
+ timeref = timenow;
+ {
+ t_rme_sample *a,*b,*c,*d;
+ float *fp1,*fp2,*fp3,*fp4;
+
+ fp1 = sys_soundin + j*DACBLKSIZE-4;
+ fp2 = fp1 + 1;
+ fp3 = fp1 + 2;
+ fp4 = fp1 + 3;
+ a = buf-4;
+ b=a+1;
+ c=a+2;
+ d=a+3;
+
+ for (i = (DACBLKSIZE>>2);i--;)
+ {
+ *(fp1+=4) = *(a+=4) * (float)(1./INT32_MAX);
+ *(fp2+=4) = *(b+=4) * (float)(1./INT32_MAX);
+ *(fp3+=4) = *(c+=4) * (float)(1./INT32_MAX);
+ *(fp4+=4) = *(d+=4) * (float)(1./INT32_MAX);
+ }
+ }
+ }
+ }
+ /* fprintf(stderr,"ready \n");*/
+
+ return (1);
+}
+
+#endif /* RME_HAMMERFALL */
diff --git a/pd/src/s_inter.c b/pd/src/s_inter.c
new file mode 100644
index 00000000..b9f52d68
--- /dev/null
+++ b/pd/src/s_inter.c
@@ -0,0 +1,794 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* Pd side of the Pd/Pd-gui interface. */
+
+#include "m_imp.h"
+#ifdef UNIX
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#endif
+#ifdef HAVE_BSTRING_H
+#include <bstring.h>
+#endif
+#ifdef NT
+#include <io.h>
+#include <fcntl.h>
+#include <process.h>
+#include <winsock.h>
+typedef int pid_t;
+#define EADDRINUSE WSAEADDRINUSE
+#endif
+#include <stdarg.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef MACOSX
+#include <sys/types.h>
+#include <sys/stat.h>
+#else
+#include <stdlib.h>
+#endif
+
+extern char pd_version[];
+
+typedef struct _fdpoll
+{
+ int fdp_fd;
+ t_fdpollfn fdp_fn;
+ void *fdp_ptr;
+} t_fdpoll;
+
+#define INBUFSIZE 4096
+
+struct _socketreceiver
+{
+ char *sr_inbuf;
+ int sr_inhead;
+ int sr_intail;
+ void *sr_owner;
+ int sr_udp;
+ t_socketnotifier sr_notifier;
+ t_socketreceivefn sr_socketreceivefn;
+};
+
+static int sys_nfdpoll;
+static t_fdpoll *sys_fdpoll;
+static int sys_maxfd;
+static int sys_guisock;
+
+static t_binbuf *inbinbuf;
+static t_socketreceiver *sys_socketreceiver;
+extern int sys_addhist(int phase);
+
+void sys_sockerror(char *s)
+{
+#ifdef NT
+ int err = WSAGetLastError();
+ if (err == 10054) return;
+ else if (err == 10044)
+ {
+ fprintf(stderr,
+ "Warning: you might not have TCP/IP \"networking\" turned on\n");
+ fprintf(stderr, "which is needed for Pd to talk to its GUI layer.\n");
+ }
+#endif
+#ifdef UNIX
+ int err = errno;
+#endif
+ fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err);
+}
+
+void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr)
+{
+ int nfd = sys_nfdpoll;
+ int size = nfd * sizeof(t_fdpoll);
+ t_fdpoll *fp;
+ sys_fdpoll = (t_fdpoll *)t_resizebytes(sys_fdpoll, size,
+ size + sizeof(t_fdpoll));
+ fp = sys_fdpoll + nfd;
+ fp->fdp_fd = fd;
+ fp->fdp_fn = fn;
+ fp->fdp_ptr = ptr;
+ sys_nfdpoll = nfd + 1;
+ if (fd >= sys_maxfd) sys_maxfd = fd + 1;
+}
+
+void sys_rmpollfn(int fd)
+{
+ int nfd = sys_nfdpoll;
+ int i, size = nfd * sizeof(t_fdpoll);
+ t_fdpoll *fp;
+ for (i = nfd, fp = sys_fdpoll; i--; fp++)
+ {
+ if (fp->fdp_fd == fd)
+ {
+ while (i--)
+ {
+ fp[0] = fp[1];
+ fp++;
+ }
+ sys_fdpoll = (t_fdpoll *)t_resizebytes(sys_fdpoll, size,
+ size - sizeof(t_fdpoll));
+ sys_nfdpoll = nfd - 1;
+ return;
+ }
+ }
+ post("warning: %d removed from poll list but not found", fd);
+}
+
+static int sys_domicrosleep(int microsec, int pollem)
+{
+ struct timeval timout;
+ int i, didsomething = 0;
+ t_fdpoll *fp;
+ timout.tv_sec = 0;
+ timout.tv_usec = microsec;
+ if (pollem)
+ {
+ fd_set readset, writeset, exceptset;
+ FD_ZERO(&writeset);
+ FD_ZERO(&readset);
+ FD_ZERO(&exceptset);
+ for (fp = sys_fdpoll, i = sys_nfdpoll; i--; fp++)
+ FD_SET(fp->fdp_fd, &readset);
+ select(sys_maxfd+1, &readset, &writeset, &exceptset, &timout);
+ for (i = 0; i < sys_nfdpoll; i++)
+ if (FD_ISSET(sys_fdpoll[i].fdp_fd, &readset))
+ {
+ (*sys_fdpoll[i].fdp_fn)(sys_fdpoll[i].fdp_ptr, sys_fdpoll[i].fdp_fd);
+ didsomething = 1;
+ }
+ return (didsomething);
+ }
+ else
+ {
+ select(0, 0, 0, 0, &timout);
+ return (0);
+ }
+}
+
+void sys_microsleep(int microsec)
+{
+ sys_domicrosleep(microsec, 1);
+}
+
+t_socketreceiver *socketreceiver_new(void *owner, t_socketnotifier notifier,
+ t_socketreceivefn socketreceivefn, int udp)
+{
+ t_socketreceiver *x = (t_socketreceiver *)getbytes(sizeof(*x));
+ x->sr_inhead = x->sr_intail = 0;
+ x->sr_owner = owner;
+ x->sr_notifier = notifier;
+ x->sr_socketreceivefn = socketreceivefn;
+ x->sr_udp = udp;
+ if (!(x->sr_inbuf = malloc(INBUFSIZE))) bug("t_socketreceiver");;
+ return (x);
+}
+
+void socketreceiver_free(t_socketreceiver *x)
+{
+ free(x->sr_inbuf);
+ freebytes(x, sizeof(*x));
+}
+
+ /* this is in a separately called subroutine so that the buffer isn't
+ sitting on the stack while the messages are getting passed. */
+static int socketreceiver_doread(t_socketreceiver *x)
+{
+ char messbuf[INBUFSIZE], *bp = messbuf;
+ int indx;
+ int inhead = x->sr_inhead;
+ int intail = x->sr_intail;
+ char *inbuf = x->sr_inbuf;
+ if (intail == inhead) return (0);
+ for (indx = intail; indx != inhead; indx = (indx+1)&(INBUFSIZE-1))
+ {
+ /* if we hit a semi that isn't preceeded by a \, it's a message
+ boundary. LATER we should deal with the possibility that the
+ preceeding \ might itself be escaped! */
+ char c = *bp++ = inbuf[indx];
+ if (c == ';' && (!indx || inbuf[indx-1] != '\\'))
+ {
+ intail = (indx+1)&(INBUFSIZE-1);
+ binbuf_text(inbinbuf, messbuf, bp - messbuf);
+ if (sys_debuglevel & DEBUG_MESSDOWN)
+ {
+ write(2, messbuf, bp - messbuf);
+ write(2, "\n", 1);
+ }
+ x->sr_inhead = inhead;
+ x->sr_intail = intail;
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static void socketreceiver_getudp(t_socketreceiver *x, int fd)
+{
+ char buf[INBUFSIZE+1];
+ int ret = recv(fd, buf, INBUFSIZE, 0);
+ if (ret < 0)
+ {
+ sys_sockerror("recv");
+ sys_rmpollfn(fd);
+ sys_closesocket(fd);
+ }
+ else if (ret > 0)
+ {
+ buf[ret] = 0;
+#if 0
+ post("%s", buf);
+#endif
+ if (buf[ret-1] != '\n')
+ {
+#if 0
+ buf[ret] = 0;
+ error("dropped bad buffer %s\n", buf);
+#endif
+ }
+ else
+ {
+ char *semi = strchr(buf, ';');
+ if (semi)
+ *semi = 0;
+ binbuf_text(inbinbuf, buf, strlen(buf));
+ outlet_setstacklim();
+ if (x->sr_socketreceivefn)
+ (*x->sr_socketreceivefn)(x->sr_owner, inbinbuf);
+ else bug("socketreceiver_getudp");
+ }
+ }
+}
+
+void socketreceiver_read(t_socketreceiver *x, int fd)
+{
+ if (x->sr_udp) /* UDP ("datagram") socket protocol */
+ socketreceiver_getudp(x, fd);
+ else /* TCP ("streaming") socket protocol */
+ {
+ char *semi;
+ int readto =
+ (x->sr_inhead >= x->sr_intail ? INBUFSIZE : x->sr_intail-1);
+ int ret;
+
+ /* the input buffer might be full. If so, drop the whole thing */
+ if (readto == x->sr_inhead)
+ {
+ fprintf(stderr, "pd: dropped message from gui\n");
+ x->sr_inhead = x->sr_intail = 0;
+ readto = INBUFSIZE;
+ }
+ else
+ {
+ ret = recv(fd, x->sr_inbuf + x->sr_inhead,
+ readto - x->sr_inhead, 0);
+ if (ret < 0)
+ {
+ sys_sockerror("recv");
+ if (x == sys_socketreceiver) sys_bail(1);
+ else
+ {
+ if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner);
+ sys_rmpollfn(fd);
+ sys_closesocket(fd);
+ }
+ }
+ else if (ret == 0)
+ {
+ if (x == sys_socketreceiver)
+ {
+ fprintf(stderr, "pd: exiting\n");
+ sys_bail(0);
+ }
+ else
+ {
+ post("EOF on socket %d\n", fd);
+ if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner);
+ sys_rmpollfn(fd);
+ sys_closesocket(fd);
+ }
+ }
+ else
+ {
+ x->sr_inhead += ret;
+ if (x->sr_inhead >= INBUFSIZE) x->sr_inhead = 0;
+ while (socketreceiver_doread(x))
+ {
+ outlet_setstacklim();
+ if (x->sr_socketreceivefn)
+ (*x->sr_socketreceivefn)(x->sr_owner, inbinbuf);
+ else binbuf_eval(inbinbuf, 0, 0, 0);
+ }
+ }
+ }
+ }
+}
+
+void sys_closesocket(int fd)
+{
+#ifdef UNIX
+ close(fd);
+#endif
+#ifdef NT
+ closesocket(fd);
+#endif
+}
+
+
+void sys_gui(char *s)
+{
+ int length = strlen(s), written = 0, res, histwas = sys_addhist(4);
+ if (sys_debuglevel & DEBUG_MESSUP)
+ fprintf(stderr, "%s", s);
+ if (sys_nogui)
+ return;
+ while (1)
+ {
+ res = send(sys_guisock, s + written, length, 0);
+ if (res < 0)
+ {
+ perror("pd output pipe");
+ sys_bail(1);
+ }
+ else
+ {
+ written += res;
+ if (written >= length)
+ break;
+ }
+ }
+ sys_addhist(histwas);
+}
+
+/* LATER should do a bounds check -- but how do you get printf to do that?
+ See also rtext_senditup() in this regard */
+
+void sys_vgui(char *fmt, ...)
+{
+ int result, i;
+ char buf[2048];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ sys_gui(buf);
+ va_end(ap);
+}
+
+
+#define FIRSTPORTNUM 5400
+
+/* -------------- signal handling for UNIX -------------- */
+
+#ifdef UNIX
+typedef void (*sighandler_t)(int);
+
+static void sys_signal(int signo, sighandler_t sigfun)
+{
+ struct sigaction action;
+ action.sa_flags = 0;
+ action.sa_handler = sigfun;
+ memset(&action.sa_mask, 0, sizeof(action.sa_mask));
+#ifdef __linux__
+ action.sa_restorer = 0;
+#endif
+ if (sigaction(signo, &action, 0) < 0)
+ perror("sigaction");
+}
+
+static void sys_exithandler(int n)
+{
+ static int trouble = 0;
+ if (!trouble)
+ {
+ trouble = 1;
+ fprintf(stderr, "Pd: signal %d\n", n);
+ sys_bail(1);
+
+ }
+ else _exit(1);
+}
+
+static void sys_alarmhandler(int n)
+{
+ fprintf(stderr, "Pd: system call timed out\n");
+}
+
+static void sys_huphandler(int n)
+{
+ struct timeval timout;
+ timout.tv_sec = 0;
+ timout.tv_usec = 30000;
+ select(1, 0, 0, 0, &timout);
+}
+
+void sys_setalarm(int microsec)
+{
+ struct itimerval gonzo;
+#if 0
+ fprintf(stderr, "timer %d\n", microsec);
+#endif
+ gonzo.it_interval.tv_sec = 0;
+ gonzo.it_interval.tv_usec = 0;
+ gonzo.it_value.tv_sec = 0;
+ gonzo.it_value.tv_usec = microsec;
+ if (microsec)
+ sys_signal(SIGALRM, sys_alarmhandler);
+ else sys_signal(SIGALRM, SIG_IGN);
+ setitimer(ITIMER_REAL, &gonzo, 0);
+}
+
+#endif
+
+static int sys_watchfd;
+
+void glob_ping(t_pd *dummy)
+{
+ if (write(sys_watchfd, "\n", 1) < 1)
+ {
+ fprintf(stderr, "pd: watchdog process died\n");
+ sys_bail(1);
+ }
+}
+
+static int defaultfontshit[] = {
+ 8, 5, 9, 10, 6, 10, 12, 7, 13, 14, 9, 17, 16, 10, 19, 24, 15, 28,
+ 24, 15, 28};
+
+int sys_startgui(const char *guidir)
+{
+ pid_t childpid;
+ char cmdbuf[4*MAXPDSTRING];
+ struct sockaddr_in server;
+ int msgsock;
+ char buf[15];
+ int len = sizeof(server);
+ int ntry = 0, portno = FIRSTPORTNUM;
+ int xsock = -1;
+#ifdef NT
+ short version = MAKEWORD(2, 0);
+ WSADATA nobby;
+#endif
+#ifdef UNIX
+ int stdinpipe[2];
+#endif
+ /* create an empty FD poll list */
+ sys_fdpoll = (t_fdpoll *)t_getbytes(0);
+ sys_nfdpoll = 0;
+ inbinbuf = binbuf_new();
+
+#ifdef UNIX
+ signal(SIGHUP, sys_huphandler);
+ signal(SIGINT, sys_exithandler);
+ signal(SIGQUIT, sys_exithandler);
+ signal(SIGILL, sys_exithandler);
+ signal(SIGIOT, sys_exithandler);
+ signal(SIGFPE, SIG_IGN);
+ /* signal(SIGILL, sys_exithandler);
+ signal(SIGBUS, sys_exithandler);
+ signal(SIGSEGV, sys_exithandler); */
+ signal(SIGPIPE, sys_exithandler);
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+#ifdef __linux__
+ signal(SIGSTKFLT, sys_exithandler);
+#endif
+#endif
+#ifdef NT
+ if (WSAStartup(version, &nobby)) sys_sockerror("WSAstartup");
+#endif
+
+ if (sys_nogui)
+ {
+ /* fake the GUI's message giving cwd and font sizes; then
+ skip starting the GUI up. */
+ t_atom zz[19];
+ int i;
+#ifdef NT
+ if (GetCurrentDirectory(MAXPDSTRING, cmdbuf) == 0)
+ strcpy(cmdbuf, ".");
+#endif
+#ifdef UNIX
+ if (!getcwd(cmdbuf, MAXPDSTRING))
+ strcpy(cmdbuf, ".");
+
+#endif
+ SETSYMBOL(zz, gensym(cmdbuf));
+ for (i = 1; i < 22; i++)
+ SETFLOAT(zz + i, defaultfontshit[i-1]);
+ glob_initfromgui(0, 0, 22, zz);
+ }
+ else
+ {
+#ifdef NT
+ char scriptbuf[MAXPDSTRING+30], wishbuf[MAXPDSTRING+30], portbuf[80];
+ int spawnret;
+
+#endif
+ int intarg;
+
+ /* create a socket */
+ xsock = socket(AF_INET, SOCK_STREAM, 0);
+ if (xsock < 0) sys_sockerror("socket");
+#if 0
+ intarg = 0;
+ if (setsockopt(xsock, SOL_SOCKET, SO_SNDBUF,
+ &intarg, sizeof(intarg)) < 0)
+ post("setsockopt (SO_RCVBUF) failed\n");
+ intarg = 0;
+ if (setsockopt(xsock, SOL_SOCKET, SO_RCVBUF,
+ &intarg, sizeof(intarg)) < 0)
+ post("setsockopt (SO_RCVBUF) failed\n");
+#endif
+ intarg = 1;
+ if (setsockopt(xsock, IPPROTO_TCP, TCP_NODELAY,
+ &intarg, sizeof(intarg)) < 0)
+ post("setsockopt (TCP_NODELAY) failed\n");
+
+
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_ANY;
+
+ /* assign server port number */
+ server.sin_port = htons((unsigned short)portno);
+
+ /* name the socket */
+ while (bind(xsock, (struct sockaddr *)&server, sizeof(server)) < 0)
+ {
+#ifdef NT
+ int err = WSAGetLastError();
+#endif
+#ifdef UNIX
+ int err = errno;
+#endif
+ if ((ntry++ > 20) || (err != EADDRINUSE))
+ {
+ perror("bind");
+ fprintf(stderr,
+ "Pd needs your machine to be configured with\n");
+ fprintf(stderr,
+ "'networking' turned on (see Pd's html doc for details.)\n");
+ exit(1);
+ }
+ portno++;
+ server.sin_port = htons((unsigned short)(portno));
+ }
+
+ if (sys_verbose) fprintf(stderr, "port %d\n", portno);
+
+ sys_socketreceiver = socketreceiver_new(0, 0, 0, 0);
+
+#ifdef UNIX
+ childpid = fork();
+ if (childpid < 0)
+ {
+ if (errno) perror("sys_startgui");
+ else fprintf(stderr, "sys_startgui failed\n");
+ return (1);
+ }
+ else if (!childpid) /* we're the child */
+ {
+ seteuid(getuid()); /* lose setuid priveliges */
+#ifndef MACOSX
+ /* the wish process in Unix will make a wish shell and
+ read/write standard in and out unless we close the
+ file descriptors. Somehow this doesn't make the MAC OSX
+ version of Wish happy...*/
+ if (pipe(stdinpipe) < 0)
+ sys_sockerror("pipe");
+ else
+ {
+ if (stdinpipe[0] != 0)
+ {
+ close (0);
+ dup2(stdinpipe[0], 0);
+ close(stdinpipe[0]);
+ }
+ }
+#endif
+ if (!sys_guicmd)
+ {
+#ifdef MACOSX
+ char *homedir = getenv("HOME"), filename[250];
+ struct stat statbuf;
+ if (!homedir || strlen(homedir) > 150)
+ goto nexttry;
+ sprintf(filename,
+ "%s/Applications/Wish shell.app/Contents/MacOS/Wish Shell",
+ homedir);
+
+ if (stat(filename, &statbuf) >= 0)
+ goto foundit;
+ nexttry:
+ strcpy(filename,
+ "/Applications/Wish Shell.app/Contents/MacOS/Wish Shell");
+ foundit:
+ sprintf(cmdbuf, "\"%s\" %s/pd.tk %d\n", filename, guidir, portno);
+#else
+ sprintf(cmdbuf,
+"TCL_LIBRARY=\"%s/tcl/library\" TK_LIBRARY=\"%s/tk/library\" \
+ \"%s/pd-gui\" %d\n",
+ sys_libdir->s_name, sys_libdir->s_name, guidir, portno);
+#endif
+ sys_guicmd = cmdbuf;
+ }
+ if (sys_verbose) fprintf(stderr, "%s", sys_guicmd);
+ execl("/bin/sh", "sh", "-c", sys_guicmd, 0);
+ perror("pd: exec");
+ _exit(1);
+ }
+#endif /* UNIX */
+
+#ifdef NT
+ /* in NT land "guipath" is unused; we just do everything from
+ the libdir. */
+ fprintf(stderr, "%s\n", sys_libdir->s_name);
+
+ strcpy(scriptbuf, "\"");
+ strcat(scriptbuf, sys_libdir->s_name);
+ strcat(scriptbuf, "/bin/pd.tk\"");
+ sys_bashfilename(scriptbuf, scriptbuf);
+
+ sprintf(portbuf, "%d", portno);
+
+ strcpy(wishbuf, sys_libdir->s_name);
+ strcat(wishbuf, "/bin/wish83.exe");
+ sys_bashfilename(wishbuf, wishbuf);
+
+ spawnret = _spawnl(P_NOWAIT, wishbuf, "wish83", scriptbuf, portbuf, 0);
+ if (spawnret < 0)
+ {
+ perror("spawnl");
+ fprintf(stderr, "%s: couldn't load TCL\n", wishbuf);
+ exit(1);
+ }
+
+#endif /* NT */
+ }
+
+#ifdef UNIX
+ /* now that we've spun off the child process we can promote
+ our process's priority, if we happen to be root. */
+ if (sys_hipriority)
+ {
+ if (!getuid() || !geteuid())
+ {
+ /* To prevent lockup, we fork off a watchdog process with
+ higher real-time priority than ours. The GUI has to send
+ a stream of ping messages to the watchdog THROUGH the Pd
+ process which has to pick them up from the GUI and forward
+ them. If any of these things aren't happening the watchdog
+ starts sending "stop" and "cont" signals to the Pd process
+ to make it timeshare with the rest of the system. (Version
+ 0.33P2 : if there's no GUI, the watchdog pinging is done
+ from the scheduler idle routine in this process instead.) */
+
+ int pipe9[2], watchpid;
+ if (pipe(pipe9) < 0)
+ {
+ seteuid(getuid()); /* lose setuid priveliges */
+ sys_sockerror("pipe");
+ return (1);
+ }
+ watchpid = fork();
+ if (watchpid < 0)
+ {
+ seteuid(getuid()); /* lose setuid priveliges */
+ if (errno)
+ perror("sys_startgui");
+ else fprintf(stderr, "sys_startgui failed\n");
+ return (1);
+ }
+ else if (!watchpid) /* we're the child */
+ {
+ sys_set_priority(1);
+ seteuid(getuid()); /* lose setuid priveliges */
+ if (pipe9[1] != 0)
+ {
+ dup2(pipe9[0], 0);
+ close(pipe9[0]);
+ }
+ close(pipe9[1]);
+
+ sprintf(cmdbuf, "%s/pd-watchdog\n", guidir);
+ if (sys_verbose) fprintf(stderr, "%s", cmdbuf);
+ execl("/bin/sh", "sh", "-c", cmdbuf, 0);
+ perror("pd: exec");
+ _exit(1);
+ }
+ else /* we're the parent */
+ {
+ sys_set_priority(0);
+ seteuid(getuid()); /* lose setuid priveliges */
+ close(pipe9[0]);
+ sys_watchfd = pipe9[1];
+ /* We also have to start the ping loop in the GUI;
+ this is done later when the socket is open. */
+ }
+ }
+ else
+ {
+ post("realtime setting failed because not root\n");
+ sys_hipriority = 0;
+ }
+ }
+
+ seteuid(getuid()); /* lose setuid priveliges */
+#endif /* UNIX */
+
+#ifdef NT
+ if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))
+ fprintf(stderr, "pd: couldn't set high priority class\n");
+#endif
+
+ if (!sys_nogui)
+ {
+ if (sys_verbose)
+ fprintf(stderr, "Waiting for connection request... \n");
+ if (listen(xsock, 5) < 0) sys_sockerror("listen");
+
+ sys_guisock = accept(xsock, (struct sockaddr *) &server, &len);
+#ifdef OOPS
+ close(xsock);
+#endif
+ if (sys_guisock < 0) sys_sockerror("accept");
+ sys_addpollfn(sys_guisock, (t_fdpollfn)socketreceiver_read,
+ sys_socketreceiver);
+
+ if (sys_verbose)
+ fprintf(stderr, "... connected\n");
+
+ /* here is where we start the pinging. */
+ if (sys_hipriority)
+ sys_gui("pdtk_watchdog\n");
+
+ sys_vgui("pdtk_pd_startup {%s}\n", pd_version);
+ }
+ return (0);
+
+}
+
+
+static int sys_poll_togui(void)
+{
+ /* LATER use this to flush output buffer to gui */
+ return (0);
+}
+
+int sys_pollgui(void)
+{
+ return (sys_domicrosleep(0, 1) || sys_poll_togui());
+}
+
+/* LATER try to save dirty documents */
+void sys_bail(int n)
+{
+ static int reentered = 0;
+ if (!reentered)
+ {
+ reentered = 1;
+ sys_close_audio();
+ sys_close_midi();
+ }
+ _exit(n);
+}
+
+void glob_quit(void *dummy)
+{
+ sys_vgui("exit\n");
+ if (!sys_nogui)
+ close(sys_guisock);
+ sys_bail(0);
+}
+
diff --git a/pd/src/s_linux.c b/pd/src/s_linux.c
new file mode 100644
index 00000000..5c394674
--- /dev/null
+++ b/pd/src/s_linux.c
@@ -0,0 +1,3087 @@
+/* Copyright (c) 1997-1999 Guenter Geiger, Miller Puckette, Larry Troxler,
+* Winfried Ritsch, Karl MacMillan, and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* this file implements the sys_ functions profiled in m_imp.h for
+ audio and MIDI I/O. In Linux there might be several APIs for doing the
+ audio part; right now there are three (OSS, ALSA, RME); the third is
+ for the RME 9652 driver by Ritsch (but not for the OSS compatible
+ one by Geiger; for that one, OSS should work.)
+
+ FUNCTION PREFIXES.
+ sys_ -- functions which must be exported to Pd on all platforms
+ linux_ -- linux-specific objects which don't depend on API,
+ mostly static but some exported.
+ oss_, alsa_, rme_ -- API-specific functions, all of which are
+ static.
+
+ ALSA SUPPORT. If ALSA99 is defined we support ALSA 0.5x; if ALSA01,
+ ALSA 0.9x. (the naming scheme reflects the possibility of further API
+ changes in the future...) We define "ALSA" for code relevant to both
+ APIs.
+
+ For MIDI, we only offer the OSS API; ALSA has to emulate OSS for us.
+*/
+
+/* OSS include (whether we're doing OSS audio or not we need this for MIDI) */
+
+
+/* IOhannes:::
+ * hacked this to add advanced multidevice-support
+ * 1311:forum::für::umläute:2001
+ */
+
+#include <linux/soundcard.h>
+
+#if (defined(ALSA01) || defined(ALSA99))
+#define ALSA
+#endif
+
+#ifdef ALSA99
+#include <sys/asoundlib.h>
+#endif
+#ifdef ALSA01
+#include <alsa/asoundlib.h>
+#endif
+
+#include "m_imp.h"
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <sys/mman.h>
+
+/* local function prototypes */
+
+static void linux_close_midi( void);
+
+static int oss_open_audio(int naudioindev, int *audioindev, int nchindev,
+ int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev,
+ int *choutdev, int rate); /* IOhannes */
+
+static void oss_close_audio(void);
+static int oss_send_dacs(void);
+static void oss_reportidle(void);
+
+#ifdef ALSA
+typedef int16_t t_alsa_sample16;
+typedef int32_t t_alsa_sample32;
+#define ALSA_SAMPLEWIDTH_16 sizeof(t_alsa_sample16)
+#define ALSA_SAMPLEWIDTH_32 sizeof(t_alsa_sample32)
+#define ALSA_XFERSIZE16 (signed int)(sizeof(t_alsa_sample16) * DACBLKSIZE)
+#define ALSA_XFERSIZE32 (signed int)(sizeof(t_alsa_sample32) * DACBLKSIZE)
+#define ALSA_MAXDEV 1
+#define ALSA_JITTER 1024
+#define ALSA_EXTRABUFFER 2048
+#define ALSA_DEFFRAGSIZE 64
+#define ALSA_DEFNFRAG 12
+
+#ifdef ALSA99
+typedef struct _alsa_dev
+{
+ snd_pcm_t *handle;
+ snd_pcm_channel_info_t info;
+ snd_pcm_channel_setup_t setup;
+} t_alsa_dev;
+
+t_alsa_dev alsa_device[ALSA_MAXDEV];
+static int n_alsa_dev;
+static char *alsa_buf;
+static int alsa_samplewidth;
+#endif /* ALSA99 */
+
+#ifdef ALSA01
+typedef struct _alsa_dev
+{
+ snd_pcm_t *inhandle;
+ snd_pcm_t *outhandle;
+} t_alsa_dev;
+
+t_alsa_dev alsa_device;
+static short *alsa_buf;
+static int alsa_samplewidth;
+static snd_pcm_status_t* in_status;
+static snd_pcm_status_t* out_status;
+#endif /* ALSA01 */
+
+#if 0 /* early alsa 0.9 beta dists had different names for these: */
+#define SND_PCM_ACCESS_RW_INTERLEAVED SNDRV_PCM_ACCESS_RW_INTERLEAVED
+#define SND_PCM_FORMAT_S32 SNDRV_PCM_FORMAT_S32
+#define SND_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16
+#define SND_PCM_SUBFORMAT_STD SNDRV_PCM_SUBFORMAT_STD
+#endif
+
+static int alsa_mode;
+static int alsa_open_audio(int inchans, int outchans, int rate);
+static void alsa_close_audio(void);
+static int alsa_send_dacs(void);
+static void alsa_set_params(t_alsa_dev *dev, int dir, int rate, int voices);
+static void alsa_reportidle(void);
+#endif /* ALSA */
+
+#ifdef RME_HAMMERFALL
+static int rme9652_open_audio(int inchans, int outchans, int rate);
+static void rme9652_close_audio(void);
+static int rme9652_send_dacs(void);
+static void rme9652_reportidle(void);
+#endif /* RME_HAMMERFALL */
+
+/* Defines */
+#define DEBUG(x) x
+#define DEBUG2(x) {x;}
+
+#define OSS_MAXCHPERDEV 32 /* max channels per OSS device */
+#define OSS_MAXDEV 4 /* maximum number of input or output devices */
+#define OSS_DEFFRAGSIZE 256 /* default log fragment size (frames) */
+#define OSS_DEFAUDIOBUF 40000 /* default audiobuffer, microseconds */
+#define OSS_DEFAULTCH 2
+#define RME_DEFAULTCH 8 /* need this even if RME undefined */
+typedef int16_t t_oss_int16;
+typedef int32_t t_oss_int32;
+#define OSS_MAXSAMPLEWIDTH sizeof(t_oss_int32)
+#define OSS_BYTESPERCHAN(width) (DACBLKSIZE * (width))
+#define OSS_XFERSAMPS(chans) (DACBLKSIZE* (chans))
+#define OSS_XFERSIZE(chans, width) (DACBLKSIZE * (chans) * (width))
+
+#ifdef RME_HAMMERFALL
+typedef int32_t t_rme_sample;
+#define RME_SAMPLEWIDTH sizeof(t_rme_sample)
+#define RME_BYTESPERCHAN (DACBLKSIZE * RME_SAMPLEWIDTH)
+#endif /* RME_HAMMERFALL */
+
+/* GLOBALS */
+static int linux_whichapi = API_OSS;
+static int linux_inchannels;
+static int linux_outchannels;
+static int linux_advance_samples; /* scheduler advance in samples */
+static int linux_meters; /* true if we're metering */
+static float linux_inmax; /* max input amplitude */
+static float linux_outmax; /* max output amplitude */
+static int linux_fragsize = 0; /* for block mode; block size (sample frames) */
+static int linux_nfragment = 0; /* ... and number of blocks. */
+
+#ifdef ALSA99
+static int alsa_devno = 1;
+#endif
+#ifdef ALSA01
+static char alsa_devname[512] = "hw:0,0";
+static int alsa_use_plugin = 0;
+#endif
+
+/* our device handles */
+
+typedef struct _oss_dev
+{
+ int d_fd;
+ unsigned int d_space; /* bytes available for writing/reading */
+ int d_bufsize; /* total buffer size in blocks for this device */
+ int d_dropcount; /* # of buffers to drop for resync (output only) */
+ unsigned int d_nchannels; /* number of channels for this device */
+ unsigned int d_bytespersamp; /* bytes per sample (2 for 16 bit, 4 for 32) */
+} t_oss_dev;
+
+static t_oss_dev linux_dacs[OSS_MAXDEV];
+static t_oss_dev linux_adcs[OSS_MAXDEV];
+static int linux_noutdevs = 0;
+static int linux_nindevs = 0;
+
+ /* exported variables */
+int sys_schedadvance = OSS_DEFAUDIOBUF; /* scheduler advance in microsecs */
+float sys_dacsr;
+int sys_hipriority = 0;
+t_sample *sys_soundout;
+t_sample *sys_soundin;
+
+ /* OSS-specific private variables */
+static int oss_blockmode = 1; /* flag to use "blockmode" */
+static int oss_32bit = 0; /* allow 23 bit transfers in OSS */
+static char ossdsp[] = "/dev/dsp%d";
+
+#ifndef INT32_MAX
+#define INT32_MAX 0x7fffffff
+#endif
+ /* don't assume we can turn all 31 bits when doing float-to-fix;
+ otherwise some audio drivers (e.g. Midiman/ALSA) wrap around. */
+#define FMAX 0x7ffff000
+#define CLIP32(x) (((x)>FMAX)?FMAX:((x) < -FMAX)?-FMAX:(x))
+
+
+/* ------------- private routines for all APIS ------------------- */
+
+static void linux_flush_all_underflows_to_zero(void)
+{
+/*
+ TODO: Implement similar thing for linux (GGeiger)
+
+ One day we will figure this out, I hope, because it
+ costs CPU time dearly on Intel - LT
+ */
+ /* union fpc_csr f;
+ f.fc_word = get_fpc_csr();
+ f.fc_struct.flush = 1;
+ set_fpc_csr(f.fc_word);
+ */
+}
+
+ /* set sample rate and channels. Must set sample rate before "configuring"
+ any devices so we know scheduler advance in samples. */
+
+static void linux_setsr(int sr)
+{
+ sys_dacsr = sr;
+ linux_advance_samples = (sys_schedadvance * sys_dacsr) / (1000000.);
+ if (linux_advance_samples < 3 * DACBLKSIZE)
+ linux_advance_samples = 3 * DACBLKSIZE;
+}
+
+static void linux_setch(int chin, int chout)
+{
+ int nblk;
+ int inbytes = chin * (DACBLKSIZE*sizeof(float));
+ int outbytes = chout * (DACBLKSIZE*sizeof(float));
+
+ linux_inchannels = chin;
+ linux_outchannels = chout;
+ if (sys_soundin)
+ free(sys_soundin);
+ sys_soundin = (t_float *)malloc(inbytes);
+ memset(sys_soundin, 0, inbytes);
+
+ if (sys_soundout)
+ free(sys_soundout);
+ sys_soundout = (t_float *)malloc(outbytes);
+ memset(sys_soundout, 0, outbytes);
+
+ if (sys_verbose)
+ post("input channels = %d, output channels = %d",
+ linux_inchannels, linux_outchannels);
+}
+
+/* ---------------- MIDI routines -------------------------- */
+
+static int oss_nmidiin;
+static int oss_midiinfd[MAXMIDIINDEV];
+static int oss_nmidiout;
+static int oss_midioutfd[MAXMIDIOUTDEV];
+
+static void oss_midiout(int fd, int n)
+{
+ char b = n;
+ if ((write(fd, (char *) &b, 1)) != 1)
+ perror("midi write");
+}
+
+#define O_MIDIFLAG O_NDELAY
+
+void linux_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec)
+{
+ int i;
+ for (i = 0; i < nmidiout; i++)
+ oss_midioutfd[i] = -1;
+ for (i = 0, oss_nmidiin = 0; i < nmidiin; i++)
+ {
+ int fd = -1, j, outdevindex = -1;
+ char namebuf[80];
+ int devno = midiinvec[i];
+
+ for (j = 0; j < nmidiout; j++)
+ if (midioutvec[j] == midiinvec[i])
+ outdevindex = j;
+
+ /* try to open the device for read/write. */
+ if (devno == 1 && fd < 0 && outdevindex >= 0)
+ {
+ sys_setalarm(1000000);
+ fd = open("/dev/midi", O_RDWR | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr,
+ "device 1: tried /dev/midi READ/WRITE; returned %d\n", fd);
+ if (outdevindex >= 0 && fd >= 0)
+ oss_midioutfd[outdevindex] = fd;
+ }
+ if (fd < 0 && outdevindex >= 0)
+ {
+ sys_setalarm(1000000);
+ sprintf(namebuf, "/dev/midi%2.2d", devno-1);
+ fd = open(namebuf, O_RDWR | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr,
+ "device %d: tried %s READ/WRITE; returned %d\n",
+ devno, namebuf, fd);
+ if (outdevindex >= 0 && fd >= 0)
+ oss_midioutfd[outdevindex] = fd;
+ }
+ if (fd < 0 && outdevindex >= 0)
+ {
+ sys_setalarm(1000000);
+ sprintf(namebuf, "/dev/midi%d", devno-1);
+ fd = open(namebuf, O_RDWR | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr, "device %d: tried %s READ/WRITE; returned %d\n",
+ devno, namebuf, fd);
+ if (outdevindex >= 0 && fd >= 0)
+ oss_midioutfd[outdevindex] = fd;
+ }
+ if (devno == 1 && fd < 0)
+ {
+ sys_setalarm(1000000);
+ fd = open("/dev/midi", O_RDONLY | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr,
+ "device 1: tried /dev/midi READONLY; returned %d\n", fd);
+ }
+ if (fd < 0)
+ {
+ sys_setalarm(1000000);
+ sprintf(namebuf, "/dev/midi%2.2d", devno-1);
+ fd = open(namebuf, O_RDONLY | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr, "device %d: tried %s READONLY; returned %d\n",
+ devno, namebuf, fd);
+ }
+ if (fd < 0)
+ {
+ sys_setalarm(1000000);
+ sprintf(namebuf, "/dev/midi%d", devno-1);
+ fd = open(namebuf, O_RDONLY | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr, "device %d: tried %s READONLY; returned %d\n",
+ devno, namebuf, fd);
+ }
+ if (fd >= 0)
+ oss_midiinfd[oss_nmidiin++] = fd;
+ else post("couldn't open MIDI input device %d", devno);
+ }
+ for (i = 0, oss_nmidiout = 0; i < nmidiout; i++)
+ {
+ int fd = oss_midioutfd[i];
+ char namebuf[80];
+ int devno = midioutvec[i];
+ if (devno == 1 && fd < 0)
+ {
+ sys_setalarm(1000000);
+ fd = open("/dev/midi", O_WRONLY | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr,
+ "device 1: tried /dev/midi WRITEONLY; returned %d\n", fd);
+ }
+ if (fd < 0)
+ {
+ sys_setalarm(1000000);
+ sprintf(namebuf, "/dev/midi%2.2d", devno-1);
+ fd = open(namebuf, O_WRONLY | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr, "device %d: tried %s WRITEONLY; returned %d\n",
+ devno, namebuf, fd);
+ }
+ if (fd < 0)
+ {
+ sys_setalarm(1000000);
+ sprintf(namebuf, "/dev/midi%d", devno-1);
+ fd = open(namebuf, O_WRONLY | O_MIDIFLAG);
+ if (sys_verbose)
+ fprintf(stderr, "device %d: tried %s WRITEONLY; returned %d\n",
+ devno, namebuf, fd);
+ }
+ if (fd >= 0)
+ oss_midioutfd[oss_nmidiout++] = fd;
+ else post("couldn't open MIDI output device %d", devno);
+ }
+
+ if (oss_nmidiin < nmidiin || oss_nmidiout < nmidiout || sys_verbose)
+ post("opened %d MIDI input device(s) and %d MIDI output device(s).",
+ oss_nmidiin, oss_nmidiout);
+}
+
+#define md_msglen(x) (((x)<0xC0)?2:((x)<0xE0)?1:((x)<0xF0)?2:\
+ ((x)==0xF2)?2:((x)<0xF4)?1:0)
+
+void sys_putmidimess(int portno, int a, int b, int c)
+{
+ if (portno >= 0 && portno < oss_nmidiout)
+ {
+ switch (md_msglen(a))
+ {
+ case 2:
+ oss_midiout(oss_midioutfd[portno],a);
+ oss_midiout(oss_midioutfd[portno],b);
+ oss_midiout(oss_midioutfd[portno],c);
+ return;
+ case 1:
+ oss_midiout(oss_midioutfd[portno],a);
+ oss_midiout(oss_midioutfd[portno],b);
+ return;
+ case 0:
+ oss_midiout(oss_midioutfd[portno],a);
+ return;
+ };
+ }
+}
+
+void sys_putmidibyte(int portno, int byte)
+{
+ if (portno >= 0 && portno < oss_nmidiout)
+ oss_midiout(oss_midioutfd[portno], byte);
+}
+
+#if 0 /* this is the "select" version which doesn't work with OSS
+ driver for emu10k1 (it doesn't implement select.) */
+void sys_poll_midi(void)
+{
+ int i, throttle = 100;
+ struct timeval timout;
+ int did = 1, maxfd = 0;
+ while (did)
+ {
+ fd_set readset, writeset, exceptset;
+ did = 0;
+ if (throttle-- < 0)
+ break;
+ timout.tv_sec = 0;
+ timout.tv_usec = 0;
+
+ FD_ZERO(&writeset);
+ FD_ZERO(&readset);
+ FD_ZERO(&exceptset);
+ for (i = 0; i < oss_nmidiin; i++)
+ {
+ if (oss_midiinfd[i] > maxfd)
+ maxfd = oss_midiinfd[i];
+ FD_SET(oss_midiinfd[i], &readset);
+ }
+ select(maxfd+1, &readset, &writeset, &exceptset, &timout);
+ for (i = 0; i < oss_nmidiin; i++)
+ if (FD_ISSET(oss_midiinfd[i], &readset))
+ {
+ char c;
+ int ret = read(oss_midiinfd[i], &c, 1);
+ if (ret <= 0)
+ fprintf(stderr, "Midi read error\n");
+ else sys_midibytein(i, (c & 0xff));
+ did = 1;
+ }
+ }
+}
+#else
+
+ /* this version uses the asynchronous "read()" ... */
+void sys_poll_midi(void)
+{
+ int i, throttle = 100;
+ struct timeval timout;
+ int did = 1, maxfd = 0;
+ while (did)
+ {
+ fd_set readset, writeset, exceptset;
+ did = 0;
+ if (throttle-- < 0)
+ break;
+ for (i = 0; i < oss_nmidiin; i++)
+ {
+ char c;
+ int ret = read(oss_midiinfd[i], &c, 1);
+ if (ret < 0)
+ {
+ if (errno != EAGAIN)
+ perror("MIDI");
+ }
+ else if (ret != 0)
+ {
+ sys_midibytein(i, (c & 0xff));
+ did = 1;
+ }
+ }
+ }
+}
+#endif
+
+void linux_close_midi()
+{
+ int i;
+ for (i = 0; i < oss_nmidiin; i++)
+ close(oss_midiinfd[i]);
+ for (i = 0; i < oss_nmidiout; i++)
+ close(oss_midioutfd[i]);
+ oss_nmidiin = oss_nmidiout = 0;
+}
+
+#define MAXAUDIODEV 4
+#define DEFAULTINDEV 1
+#define DEFAULTOUTDEV 1
+
+/* ----------------------- public routines ----------------------- */
+void sys_listdevs( void)
+{
+ post("device listing not implemented in Linux yet\n");
+}
+
+void sys_open_audio(int naudioindev, int *audioindev, int nchindev,
+ int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev,
+ int *choutdev, int rate)
+{ /* IOhannes */
+ int i, *ip;
+ int defaultchannels =
+ (linux_whichapi == API_RME ? RME_DEFAULTCH : OSS_DEFAULTCH);
+ if (rate < 1)
+ rate=44100;
+
+ if (naudioindev == -1)
+ { /* not set */
+ if (nchindev==-1)
+ {
+ nchindev=1;
+ chindev[0]=defaultchannels;
+ naudioindev=1;
+ audioindev[0] = DEFAULTINDEV;
+ }
+ else
+ {
+ for (i = 0; i < MAXAUDIODEV; i++)
+ audioindev[i]=i+1;
+ naudioindev = nchindev;
+ }
+ }
+ else
+ {
+ if (nchindev == -1)
+ {
+ nchindev = naudioindev;
+ for (i = 0; i < naudioindev; i++)
+ chindev[i] = defaultchannels;
+ }
+ else if (nchindev > naudioindev)
+ {
+ for (i = naudioindev; i < nchindev; i++)
+ {
+ if (i == 0)
+ audioindev[0] = DEFAULTINDEV;
+ else audioindev[i] = audioindev[i-1] + 1;
+ }
+ naudioindev = nchindev;
+ }
+ else if (nchindev < naudioindev)
+ {
+ for (i = nchindev; i < naudioindev; i++)
+ {
+ if (i == 0)
+ chindev[0] = defaultchannels;
+ else chindev[i] = chindev[i-1];
+ }
+ naudioindev = nchindev;
+ }
+ }
+
+ if (naudiooutdev == -1)
+ { /* not set */
+ if (nchoutdev==-1)
+ {
+ nchoutdev=1;
+ choutdev[0]=defaultchannels;
+ naudiooutdev=1;
+ audiooutdev[0] = DEFAULTOUTDEV;
+ }
+ else
+ {
+ for (i = 0; i < MAXAUDIODEV; i++)
+ audiooutdev[i] = i+1;
+ naudiooutdev = nchoutdev;
+ }
+ }
+ else
+ {
+ if (nchoutdev == -1)
+ {
+ nchoutdev = naudiooutdev;
+ for (i = 0; i < naudiooutdev; i++)
+ choutdev[i] = defaultchannels;
+ }
+ else if (nchoutdev > naudiooutdev)
+ {
+ for (i = naudiooutdev; i < nchoutdev; i++)
+ {
+ if (i == 0)
+ audiooutdev[0] = DEFAULTOUTDEV;
+ else audiooutdev[i] = audiooutdev[i-1] + 1;
+ }
+ naudiooutdev = nchoutdev;
+ }
+ else if (nchoutdev < naudiooutdev)
+ {
+ for (i = nchoutdev; i < naudiooutdev; i++)
+ {
+ if (i == 0)
+ choutdev[0] = defaultchannels;
+ else choutdev[i] = choutdev[i-1];
+ }
+ naudiooutdev = nchoutdev;
+ }
+ }
+
+ linux_flush_all_underflows_to_zero();
+#ifdef ALSA
+ if (linux_whichapi == API_ALSA)
+ alsa_open_audio((naudioindev > 0 ? chindev[0] : 0),
+ (naudiooutdev > 0 ? choutdev[0] : 0), rate);
+ else
+#endif
+#ifdef RME_HAMMERFALL
+ if (linux_whichapi == API_RME)
+ rme9652_open_audio((naudioindev > 0 ? chindev[0] : 0),
+ (naudiooutdev > 0 ? choutdev[0] : 0), rate);
+ else
+#endif
+ oss_open_audio(naudioindev, audioindev, nchindev, chindev,
+ naudiooutdev, audiooutdev, nchoutdev, choutdev, rate);
+}
+
+void sys_close_audio(void)
+{
+ /* set timeout to avoid hanging close() call */
+
+ sys_setalarm(1000000);
+
+#ifdef ALSA
+ if (linux_whichapi == API_ALSA)
+ alsa_close_audio();
+ else
+#endif
+#ifdef RME_HAMMERFALL
+ if (linux_whichapi == API_RME)
+ rme9652_close_audio();
+ else
+#endif
+ oss_close_audio();
+
+ sys_setalarm(0);
+}
+
+void sys_open_midi(int nmidiin, int *midiinvec,
+ int nmidiout, int *midioutvec)
+{
+ linux_open_midi(nmidiin, midiinvec, nmidiout, midioutvec);
+}
+
+void sys_close_midi( void)
+{
+ sys_setalarm(1000000);
+ linux_close_midi();
+ sys_setalarm(0);
+}
+
+int sys_send_dacs(void)
+{
+ if (linux_meters)
+ {
+ int i, n;
+ float maxsamp;
+ for (i = 0, n = linux_inchannels * DACBLKSIZE, maxsamp = linux_inmax;
+ i < n; i++)
+ {
+ float f = sys_soundin[i];
+ if (f > maxsamp) maxsamp = f;
+ else if (-f > maxsamp) maxsamp = -f;
+ }
+ linux_inmax = maxsamp;
+ for (i = 0, n = linux_outchannels * DACBLKSIZE, maxsamp = linux_outmax;
+ i < n; i++)
+ {
+ float f = sys_soundout[i];
+ if (f > maxsamp) maxsamp = f;
+ else if (-f > maxsamp) maxsamp = -f;
+ }
+ linux_outmax = maxsamp;
+ }
+#ifdef ALSA
+ if (linux_whichapi == API_ALSA)
+ return alsa_send_dacs();
+#endif
+#ifdef RME_HAMMERFALL
+ if (linux_whichapi == API_RME)
+ return rme9652_send_dacs();
+#endif
+ return oss_send_dacs();
+}
+
+float sys_getsr(void)
+{
+ return (sys_dacsr);
+}
+
+int sys_get_outchannels(void)
+{
+ return (linux_outchannels);
+}
+
+int sys_get_inchannels(void)
+{
+ return (linux_inchannels);
+}
+
+void sys_audiobuf(int n)
+{
+ /* set the size, in milliseconds, of the audio FIFO */
+ if (n < 5) n = 5;
+ else if (n > 5000) n = 5000;
+ sys_schedadvance = n * 1000;
+}
+
+void sys_getmeters(float *inmax, float *outmax)
+{
+ if (inmax)
+ {
+ linux_meters = 1;
+ *inmax = linux_inmax;
+ *outmax = linux_outmax;
+ }
+ else
+ linux_meters = 0;
+ linux_inmax = linux_outmax = 0;
+}
+
+void sys_reportidle(void)
+{
+}
+
+void sys_set_priority(int higher)
+{
+ struct sched_param par;
+ int p1 ,p2, p3;
+#ifdef _POSIX_PRIORITY_SCHEDULING
+
+ p1 = sched_get_priority_min(SCHED_FIFO);
+ p2 = sched_get_priority_max(SCHED_FIFO);
+ p3 = (higher ? p2 - 1 : p2 - 3);
+ par.sched_priority = p3;
+
+ if (sched_setscheduler(0,SCHED_FIFO,&par) != -1)
+ fprintf(stderr, "priority %d scheduling enabled.\n", p3);
+#endif
+
+#ifdef _POSIX_MEMLOCK
+ if (mlockall(MCL_FUTURE) != -1)
+ fprintf(stderr, "memory locking enabled.\n");
+#endif
+}
+
+void sys_setblocksize(int n)
+{
+ if (n < 1)
+ n = 1;
+ linux_fragsize = n;
+ oss_blockmode = 1;
+}
+
+/* ------------ linux-specific command-line flags -------------- */
+
+void linux_setfrags(int n)
+{
+ linux_nfragment = n;
+ oss_blockmode = 1;
+}
+
+void linux_streammode( void)
+{
+ oss_blockmode = 0;
+}
+
+void linux_32bit( void)
+{
+ oss_32bit = 1;
+}
+
+void linux_set_sound_api(int which)
+{
+ linux_whichapi = which;
+ if (sys_verbose)
+ post("linux_whichapi %d", linux_whichapi);
+}
+
+#ifdef ALSA99
+void linux_alsa_devno(int devno)
+{
+ alsa_devno = devno;
+}
+
+#endif
+
+#ifdef ALSA01
+void linux_alsa_devname(char *devname)
+{
+ strncpy(alsa_devname, devname, 511);
+}
+
+void linux_alsa_use_plugin(int t)
+{
+ if (t == 1)
+ alsa_use_plugin = 1;
+ else
+ alsa_use_plugin = 0;
+}
+
+#endif
+
+/* -------------- Audio I/O using the OSS API ------------------ */
+
+typedef struct _multidev {
+ int fd;
+ int channels;
+ int format;
+} t_multidev;
+
+int oss_reset(int fd) {
+ int err;
+ if ((err = ioctl(fd,SNDCTL_DSP_RESET)) < 0)
+ error("OSS: Could not reset");
+ return err;
+}
+
+ /* The AFMT_S32_BLOCKED format is not defined in standard linux kernels
+ but is proposed by Guenter Geiger to support extending OSS to handle
+ 32 bit sample. This is user in Geiger's OSS driver for RME Hammerfall.
+ I'm not clear why this isn't called AFMT_S32_[SLN]E... */
+
+#ifndef AFMT_S32_BLOCKED
+#define AFMT_S32_BLOCKED 0x0000400
+#endif
+
+void oss_configure(t_oss_dev *dev, int srate, int dac, int skipblocksize)
+{ /* IOhannes */
+ int orig, param, nblk, fd = dev->d_fd, wantformat;
+ int nchannels = dev->d_nchannels;
+ int advwas = sys_schedadvance;
+
+ audio_buf_info ainfo;
+
+ /* IOhannes :
+ * pd is very likely to crash if different formats are used on
+ multiple soundcards
+ */
+
+ /* set resolution - first try 4 byte samples */
+ if (oss_32bit && (ioctl(fd,SNDCTL_DSP_GETFMTS,&param) >= 0) &&
+ (param & AFMT_S32_BLOCKED))
+ {
+ wantformat = AFMT_S32_BLOCKED;
+ dev->d_bytespersamp = 4;
+ }
+ else
+ {
+ wantformat = AFMT_S16_NE;
+ dev->d_bytespersamp = 2;
+ }
+ param = wantformat;
+
+ if (sys_verbose)
+ post("bytes per sample = %d", dev->d_bytespersamp);
+ if (ioctl(fd, SNDCTL_DSP_SETFMT, &param) == -1)
+ fprintf(stderr,"OSS: Could not set DSP format\n");
+ else if (wantformat != param)
+ fprintf(stderr,"OSS: DSP format: wanted %d, got %d\n",
+ wantformat, param);
+
+ /* sample rate */
+ orig = param = srate;
+ if (ioctl(fd, SNDCTL_DSP_SPEED, &param) == -1)
+ fprintf(stderr,"OSS: Could not set sampling rate for device\n");
+ else if( orig != param )
+ fprintf(stderr,"OSS: sampling rate: wanted %d, got %d\n",
+ orig, param );
+
+ if (oss_blockmode && !skipblocksize)
+ {
+ int fragbytes, logfragsize, nfragment;
+ /* setting fragment count and size. */
+ if (linux_nfragment) /* if nfrags specified, take literally */
+ {
+ nfragment = linux_nfragment;
+ if (!linux_fragsize)
+ linux_fragsize = OSS_DEFFRAGSIZE;
+ sys_schedadvance = ((nfragment * linux_fragsize) * 1.e6)
+ / (float)srate;
+ linux_setsr(srate);
+ }
+ else
+ {
+ if (!linux_fragsize)
+ {
+ linux_fragsize = OSS_DEFFRAGSIZE;
+ while (linux_fragsize > DACBLKSIZE
+ && linux_fragsize * 4 > linux_advance_samples)
+ linux_fragsize = linux_fragsize/2;
+ }
+ /* post("adv_samples %d", linux_advance_samples); */
+ nfragment = (sys_schedadvance * (44100. * 1.e-6)) / linux_fragsize;
+ }
+ fragbytes = linux_fragsize * (dev->d_bytespersamp * nchannels);
+ logfragsize = ilog2(fragbytes);
+
+ if (fragbytes != (1 << logfragsize))
+ post("warning: OSS takes only power of 2 blocksize; using %d",
+ (1 << logfragsize)/(dev->d_bytespersamp * nchannels));
+ if (sys_verbose)
+ post("setting nfrags = %d, fragsize %d\n", nfragment, fragbytes);
+
+ param = orig = (nfragment<<16) + logfragsize;
+ if (ioctl(fd,SNDCTL_DSP_SETFRAGMENT, &param) == -1)
+ error("OSS: Could not set or read fragment size\n");
+ if (param != orig)
+ {
+ nfragment = ((param >> 16) & 0xffff);
+ logfragsize = (param & 0xffff);
+ post("warning: actual fragments %d, blocksize %d",
+ nfragment, (1 << logfragsize));
+ }
+ if (sys_verbose)
+ post("audiobuffer set to %d msec", (int)(0.001 * sys_schedadvance));
+ }
+
+ if (dac)
+ {
+ /* use "free space" to learn the buffer size. Normally you
+ should set this to your own desired value; but this seems not
+ to be implemented uniformly across different sound cards. LATER
+ we should figure out what to do if the requested scheduler advance
+ is greater than this buffer size; for now, we just print something
+ out. */
+
+ int defect;
+ if (ioctl(fd, SOUND_PCM_GETOSPACE,&ainfo) < 0)
+ fprintf(stderr,"OSS: ioctl on output device failed");
+ dev->d_bufsize = ainfo.bytes;
+
+ defect = linux_advance_samples * (dev->d_bytespersamp * nchannels)
+ - dev->d_bufsize - OSS_XFERSIZE(nchannels, dev->d_bytespersamp);
+ if (defect > 0)
+ {
+ if (sys_verbose || defect > (dev->d_bufsize >> 2))
+ fprintf(stderr,
+ "OSS: requested audio buffer size %d limited to %d\n",
+ linux_advance_samples * (dev->d_bytespersamp * nchannels),
+ dev->d_bufsize);
+ linux_advance_samples =
+ (dev->d_bufsize - OSS_XFERSAMPS(nchannels)) /
+ (dev->d_bytespersamp *nchannels);
+ }
+ }
+}
+
+static int oss_setchannels(int fd, int wantchannels, char *devname)
+{ /* IOhannes */
+ int param = wantchannels;
+
+ while (param>1) {
+ int save = param;
+ if (ioctl(fd, SNDCTL_DSP_CHANNELS, &param) == -1) {
+ error("OSS: SNDCTL_DSP_CHANNELS failed %s",devname);
+ } else {
+ if (param == save) return (param);
+ }
+ param=save-1;
+ }
+
+ return (0);
+}
+
+#define O_AUDIOFLAG 0 /* O_NDELAY */
+
+int oss_open_audio(int nindev, int *indev, int nchin, int *chin,
+ int noutdev, int *outdev, int nchout, int *chout, int rate)
+{ /* IOhannes */
+ int capabilities = 0;
+ int inchannels = 0, outchannels = 0;
+ char devname[20];
+ int n, i, fd;
+ char buf[OSS_MAXSAMPLEWIDTH * DACBLKSIZE * OSS_MAXCHPERDEV];
+ int num_devs = 0;
+ int wantmore=0;
+ int spread = 0;
+ audio_buf_info ainfo;
+
+ linux_nindevs = linux_noutdevs = 0;
+
+ /* set logical sample rate amd calculate linux_advance_samples. */
+ linux_setsr(rate);
+
+ /* mark input devices unopened */
+ for (i = 0; i < OSS_MAXDEV; i++)
+ linux_adcs[i].d_fd = -1;
+
+ /* open output devices */
+ wantmore=0;
+ if (noutdev < 0 || nindev < 0)
+ bug("linux_open_audio");
+
+ for (n = 0; n < noutdev; n++)
+ {
+ int gotchans, j, inindex = -1;
+ int thisdevice=outdev[n];
+ int wantchannels = (nchout>n) ? chout[n] : wantmore;
+ fd = -1;
+ if (!wantchannels)
+ goto end_out_loop;
+
+ if (thisdevice > 1)
+ sprintf(devname, "/dev/dsp%d", thisdevice-1);
+ else sprintf(devname, "/dev/dsp");
+
+ /* search for input request for same device. Succeed only
+ if the number of channels matches. */
+ for (j = 0; j < nindev; j++)
+ if (indev[j] == thisdevice && chin[j] == wantchannels)
+ inindex = j;
+
+ /* if the same device is requested for input and output,
+ try to open it read/write */
+ if (inindex >= 0)
+ {
+ sys_setalarm(1000000);
+ if ((fd = open(devname, O_RDWR | O_AUDIOFLAG)) == -1)
+ {
+ post("%s (read/write): %s", devname, strerror(errno));
+ post("(now will try write-only...)");
+ }
+ else
+ {
+ if (sys_verbose)
+ post("opened %s for reading and writing\n", devname);
+ linux_adcs[inindex].d_fd = fd;
+ }
+ }
+ /* if that didn't happen or if it failed, try write-only */
+ if (fd == -1)
+ {
+ sys_setalarm(1000000);
+ if ((fd = open(devname, O_WRONLY | O_AUDIOFLAG)) == -1)
+ {
+ post("%s (writeonly): %s",
+ devname, strerror(errno));
+ break;
+ }
+ if (sys_verbose)
+ post("opened %s for writing only\n", devname);
+ }
+ if (ioctl(fd, SNDCTL_DSP_GETCAPS, &capabilities) == -1)
+ error("OSS: SNDCTL_DSP_GETCAPS failed %s", devname);
+
+ gotchans = oss_setchannels(fd,
+ (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels,
+ devname);
+
+ if (sys_verbose)
+ post("opened audio output on %s; got %d channels",
+ devname, gotchans);
+
+ if (gotchans < 2)
+ {
+ /* can't even do stereo? just give up. */
+ close(fd);
+ }
+ else
+ {
+ linux_dacs[linux_noutdevs].d_nchannels = gotchans;
+ linux_dacs[linux_noutdevs].d_fd = fd;
+ oss_configure(linux_dacs+linux_noutdevs, rate, 1, 0);
+
+ linux_noutdevs++;
+ outchannels += gotchans;
+ if (inindex >= 0)
+ {
+ linux_adcs[inindex].d_nchannels = gotchans;
+ chin[inindex] = gotchans;
+ }
+ }
+ /* LATER think about spreading large numbers of channels over
+ various dsp's and vice-versa */
+ wantmore = wantchannels - gotchans;
+ end_out_loop: ;
+ }
+
+ /* open input devices */
+ wantmore = 0;
+ if (nindev==-1)
+ nindev=4; /* spread channels over default-devices */
+ for (n = 0; n < nindev; n++)
+ {
+ int gotchans=0;
+ int thisdevice=indev[n];
+ int wantchannels = (nchin>n)?chin[n]:wantmore;
+ int alreadyopened = 0;
+ if (!wantchannels)
+ goto end_in_loop;
+
+ if (thisdevice > 1)
+ sprintf(devname, "/dev/dsp%d", thisdevice - 1);
+ else sprintf(devname, "/dev/dsp");
+
+ sys_setalarm(1000000);
+
+ /* perhaps it's already open from the above? */
+ if (linux_dacs[n].d_fd >= 0)
+ {
+ fd = linux_dacs[n].d_fd;
+ alreadyopened = 1;
+ }
+ else
+ {
+ /* otherwise try to open it here. */
+ if ((fd = open(devname, O_RDONLY | O_AUDIOFLAG)) == -1)
+ {
+ post("%s (readonly): %s", devname, strerror(errno));
+ goto end_in_loop;
+ }
+ if (sys_verbose)
+ post("opened %s for reading only\n", devname);
+ }
+ linux_adcs[linux_nindevs].d_fd = fd;
+ gotchans = oss_setchannels(fd,
+ (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels,
+ devname);
+ if (sys_verbose)
+ post("opened audio input device %s; got %d channels",
+ devname, gotchans);
+
+ if (gotchans < 1)
+ {
+ close(fd);
+ goto end_in_loop;
+ }
+
+ linux_adcs[linux_nindevs].d_nchannels = gotchans;
+
+ oss_configure(linux_adcs+linux_nindevs, rate, 0, alreadyopened);
+
+ inchannels += gotchans;
+ linux_nindevs++;
+
+ wantmore = wantchannels-gotchans;
+ /* LATER think about spreading large numbers of channels over
+ various dsp's and vice-versa */
+ end_in_loop: ;
+ }
+
+ linux_setch(inchannels, outchannels);
+
+ /* We have to do a read to start the engine. This is
+ necessary because sys_send_dacs waits until the input
+ buffer is filled and only reads on a filled buffer.
+ This is good, because it's a way to make sure that we
+ will not block. But I wonder why we only have to read
+ from one of the devices and not all of them??? */
+
+ if (linux_nindevs)
+ {
+ if (sys_verbose)
+ fprintf(stderr,("OSS: issuing first ADC 'read' ... "));
+ read(linux_adcs[0].d_fd, buf,
+ linux_adcs[0].d_bytespersamp *
+ linux_adcs[0].d_nchannels * DACBLKSIZE);
+ if (sys_verbose)
+ fprintf(stderr, "...done.\n");
+ }
+ sys_setalarm(0);
+ return (0);
+}
+
+void oss_close_audio( void)
+{
+ int i;
+ for (i=0;i<linux_nindevs;i++)
+ close(linux_adcs[i].d_fd);
+
+ for (i=0;i<linux_noutdevs;i++)
+ close(linux_dacs[i].d_fd);
+
+ linux_nindevs = linux_noutdevs = 0;
+}
+
+static int linux_dacs_write(int fd,void* buf,long bytes)
+{
+ return write(fd, buf, bytes);
+}
+
+static int linux_adcs_read(int fd,void* buf,long bytes)
+{
+ return read(fd, buf, bytes);
+}
+
+ /* query audio devices for "available" data size. */
+static void oss_calcspace(void)
+{
+ int dev;
+ audio_buf_info ainfo;
+ for (dev=0; dev < linux_noutdevs; dev++)
+ {
+ if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0)
+ fprintf(stderr,"OSS: ioctl on output device %d failed",dev);
+ linux_dacs[dev].d_space = ainfo.bytes;
+ }
+
+ for (dev = 0; dev < linux_nindevs; dev++)
+ {
+ if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE,&ainfo) < 0)
+ fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed",
+ dev, linux_adcs[dev].d_fd);
+ linux_adcs[dev].d_space = ainfo.bytes;
+ }
+}
+
+void linux_audiostatus(void)
+{
+ int dev;
+ if (!oss_blockmode)
+ {
+ oss_calcspace();
+ for (dev=0; dev < linux_noutdevs; dev++)
+ fprintf(stderr, "dac %d space %d\n", dev, linux_dacs[dev].d_space);
+
+ for (dev = 0; dev < linux_nindevs; dev++)
+ fprintf(stderr, "adc %d space %d\n", dev, linux_adcs[dev].d_space);
+
+ }
+}
+
+/* this call resyncs audio output and input which will cause discontinuities
+in audio output and/or input. */
+
+static void oss_doresync( void)
+{
+ int dev, zeroed = 0, wantsize;
+ char buf[OSS_MAXSAMPLEWIDTH * DACBLKSIZE * OSS_MAXCHPERDEV];
+ audio_buf_info ainfo;
+
+ /* 1. if any input devices are ahead (have more than 1 buffer stored),
+ drop one or more buffers worth */
+ for (dev = 0; dev < linux_nindevs; dev++)
+ {
+ if (linux_adcs[dev].d_space == 0)
+ {
+ linux_adcs_read(linux_adcs[dev].d_fd, buf,
+ OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
+ linux_adcs[dev].d_bytespersamp));
+ }
+ else while (linux_adcs[dev].d_space >
+ OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
+ linux_adcs[dev].d_bytespersamp))
+ {
+ linux_adcs_read(linux_adcs[dev].d_fd, buf,
+ OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
+ linux_adcs[dev].d_bytespersamp));
+ if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE, &ainfo) < 0)
+ {
+ fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed",
+ dev, linux_adcs[dev].d_fd);
+ break;
+ }
+ linux_adcs[dev].d_space = ainfo.bytes;
+ }
+ }
+
+ /* 2. if any output devices are behind, feed them zeros to catch them
+ up */
+ for (dev = 0; dev < linux_noutdevs; dev++)
+ {
+ while (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize -
+ linux_advance_samples * (linux_dacs[dev].d_nchannels *
+ linux_dacs[dev].d_bytespersamp))
+ {
+ if (!zeroed)
+ {
+ unsigned int i;
+ for (i = 0; i < OSS_XFERSAMPS(linux_dacs[dev].d_nchannels);
+ i++)
+ buf[i] = 0;
+ zeroed = 1;
+ }
+ linux_dacs_write(linux_dacs[dev].d_fd, buf,
+ OSS_XFERSIZE(linux_dacs[dev].d_nchannels,
+ linux_dacs[dev].d_bytespersamp));
+ if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0)
+ {
+ fprintf(stderr, "OSS: ioctl on output device %d, fd %d failed",
+ dev, linux_dacs[dev].d_fd);
+ break;
+ }
+ linux_dacs[dev].d_space = ainfo.bytes;
+ }
+ }
+ /* 3. if any DAC devices are too far ahead, plan to drop the
+ number of frames which will let the others catch up. */
+ for (dev = 0; dev < linux_noutdevs; dev++)
+ {
+ if (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize -
+ (linux_advance_samples - 1) * linux_dacs[dev].d_nchannels *
+ linux_dacs[dev].d_bytespersamp)
+ {
+ linux_dacs[dev].d_dropcount = linux_advance_samples - 1 -
+ (linux_dacs[dev].d_space - linux_dacs[dev].d_bufsize) /
+ (linux_dacs[dev].d_nchannels *
+ linux_dacs[dev].d_bytespersamp) ;
+ }
+ else linux_dacs[dev].d_dropcount = 0;
+ }
+}
+
+int oss_send_dacs(void)
+{
+ float *fp1, *fp2;
+ long fill;
+ int i, j, dev, rtnval = SENDDACS_YES;
+ char buf[OSS_MAXSAMPLEWIDTH * DACBLKSIZE * OSS_MAXCHPERDEV];
+ t_oss_int16 *sp;
+ t_oss_int32 *lp;
+ /* the maximum number of samples we should have in the ADC buffer */
+ int idle = 0;
+ int thischan;
+ double timeref, timenow;
+
+ if (!linux_nindevs && !linux_noutdevs)
+ return (SENDDACS_NO);
+
+ if (!oss_blockmode)
+ {
+ /* determine whether we're idle. This is true if either (1)
+ some input device has less than one buffer to read or (2) some
+ output device has fewer than (linux_advance_samples) blocks buffered
+ already. */
+ oss_calcspace();
+
+ for (dev=0; dev < linux_noutdevs; dev++)
+ if (linux_dacs[dev].d_dropcount ||
+ (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space >
+ linux_advance_samples * linux_dacs[dev].d_bytespersamp *
+ linux_dacs[dev].d_nchannels))
+ idle = 1;
+ for (dev=0; dev < linux_nindevs; dev++)
+ if (linux_adcs[dev].d_space <
+ OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
+ linux_adcs[dev].d_bytespersamp))
+ idle = 1;
+ }
+
+ if (idle && !oss_blockmode)
+ {
+ /* sometimes---rarely---when the ADC available-byte-count is
+ zero, it's genuine, but usually it's because we're so
+ late that the ADC has overrun its entire kernel buffer. We
+ distinguish between the two by waiting 2 msec and asking again.
+ There should be an error flag we could check instead; look for this
+ someday... */
+ for (dev = 0;dev < linux_nindevs; dev++)
+ if (linux_adcs[dev].d_space == 0)
+ {
+ audio_buf_info ainfo;
+ sys_microsleep(2000);
+ oss_calcspace();
+ if (linux_adcs[dev].d_space != 0) continue;
+
+ /* here's the bad case. Give up and resync. */
+ sys_log_error(ERR_DATALATE);
+ oss_doresync();
+ return (SENDDACS_NO);
+ }
+ /* check for slippage between devices, either because
+ data got lost in the driver from a previous late condition, or
+ because the devices aren't synced. When we're idle, no
+ input device should have more than one buffer readable and
+ no output device should have less than linux_advance_samples-1
+ */
+
+ for (dev=0; dev < linux_noutdevs; dev++)
+ if (!linux_dacs[dev].d_dropcount &&
+ (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space <
+ (linux_advance_samples - 2) *
+ (linux_dacs[dev].d_bytespersamp *
+ linux_dacs[dev].d_nchannels)))
+ goto badsync;
+ for (dev=0; dev < linux_nindevs; dev++)
+ if (linux_adcs[dev].d_space > 3 *
+ OSS_XFERSIZE(linux_adcs[dev].d_nchannels,
+ linux_adcs[dev].d_bytespersamp))
+ goto badsync;
+
+ /* return zero to tell the scheduler we're idle. */
+ return (SENDDACS_NO);
+ badsync:
+ sys_log_error(ERR_RESYNC);
+ oss_doresync();
+ return (SENDDACS_NO);
+
+ }
+
+ /* do output */
+
+ timeref = sys_getrealtime();
+ for (dev=0, thischan = 0; dev < linux_noutdevs; dev++)
+ {
+ int nchannels = linux_dacs[dev].d_nchannels;
+ if (linux_dacs[dev].d_dropcount)
+ linux_dacs[dev].d_dropcount--;
+ else
+ {
+ if (linux_dacs[dev].d_bytespersamp == 4)
+ {
+ for (i = DACBLKSIZE * nchannels, fp1 = sys_soundout +
+ DACBLKSIZE*thischan,
+ lp = (t_oss_int32 *)buf; i--; fp1++, lp++)
+ {
+ float f = *fp1 * 2147483648.;
+ *lp = (f >= 2147483647. ? 2147483647. :
+ (f < -2147483648. ? -2147483648. : f));
+ }
+ }
+ else
+ {
+ for (i = DACBLKSIZE, fp1 = sys_soundout +
+ DACBLKSIZE*thischan,
+ sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels)
+ {
+ for (j=0, fp2 = fp1; j<nchannels; j++, fp2 += DACBLKSIZE)
+ {
+ int s = *fp2 * 32767.;
+ if (s > 32767) s = 32767;
+ else if (s < -32767) s = -32767;
+ sp[j] = s;
+ }
+ }
+ }
+ linux_dacs_write(linux_dacs[dev].d_fd, buf,
+ OSS_XFERSIZE(nchannels, linux_dacs[dev].d_bytespersamp));
+ if ((timenow = sys_getrealtime()) - timeref > 0.002)
+ {
+ if (!oss_blockmode)
+ sys_log_error(ERR_DACSLEPT);
+ else rtnval = SENDDACS_SLEPT;
+ }
+ timeref = timenow;
+ }
+ thischan += nchannels;
+ }
+ memset(sys_soundout, 0,
+ linux_outchannels * (sizeof(float) * DACBLKSIZE));
+
+ /* do input */
+
+ for (dev = 0, thischan = 0; dev < linux_nindevs; dev++)
+ {
+ int nchannels = linux_adcs[dev].d_nchannels;
+ linux_adcs_read(linux_adcs[dev].d_fd, buf,
+ OSS_XFERSIZE(nchannels, linux_adcs[dev].d_bytespersamp));
+
+ if ((timenow = sys_getrealtime()) - timeref > 0.002)
+ {
+ if (!oss_blockmode)
+ sys_log_error(ERR_ADCSLEPT);
+ else
+ rtnval = SENDDACS_SLEPT;
+ }
+ timeref = timenow;
+
+ if (linux_adcs[dev].d_bytespersamp == 4)
+ {
+ for (i = DACBLKSIZE*nchannels,
+ fp1 = sys_soundin + thischan*DACBLKSIZE,
+ lp = (t_oss_int32 *)buf; i--; fp1++, lp++)
+ {
+ *fp1 = ((float)(*lp))*(float)(1./2147483648.);
+ }
+ }
+ else
+ {
+ for (i = DACBLKSIZE,fp1 = sys_soundin + thischan*DACBLKSIZE,
+ sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels)
+ {
+ for (j=0;j<linux_inchannels;j++)
+ fp1[j*DACBLKSIZE] = (float)sp[j]*(float)3.051850e-05;
+ }
+ }
+ thischan += nchannels;
+ }
+ if (thischan != linux_inchannels)
+ bug("inchannels");
+ return (rtnval);
+}
+
+/* ----------------- audio I/O using the ALSA native API ---------------- */
+
+#ifdef ALSA
+static void alsa_checkversion( void)
+{
+ char snox[512];
+ int fd, nbytes;
+ if ((fd = open("/proc/asound/version", 0)) < 0 ||
+ (nbytes = read(fd, snox, 511)) < 1)
+ {
+ perror("cannot check Alsa version -- /proc/asound/version");
+ return;
+ }
+ snox[nbytes] = 0;
+#ifdef ALSA99
+ if (!strstr(snox, "Version 0.5"))
+ {
+ fprintf(stderr,
+"warning: Pd compiled for Alsa version 0.5 appears to be incompatible with\n\
+the installed version of ALSA. Here is what I found in /proc/asound/version:\n"
+ );
+ fprintf(stderr, "%s", snox);
+ }
+#else
+ if (!strstr(snox, "Version 0.9"))
+ {
+ fprintf(stderr,
+"warning: Pd compiled for Alsa version 0.9 appears to be incompatible with\n\
+the installed version of ALSA. Here is what I found in /proc/asound/version:\n"
+ );
+ fprintf(stderr, "%s", snox);
+ }
+#endif
+}
+#endif
+
+#ifdef ALSA99
+static int alsa_open_audio(int wantinchans, int wantoutchans,
+ int srate)
+{
+ int dir, voices, bsize;
+ int err, id, rate, i;
+ char *cardname;
+ snd_ctl_hw_info_t hwinfo;
+ snd_pcm_info_t pcminfo;
+ snd_pcm_channel_info_t channelinfo;
+ snd_ctl_t *handle;
+ snd_pcm_sync_t sync;
+
+ linux_inchannels = 0;
+ linux_outchannels = 0;
+
+ rate = 44100;
+ alsa_samplewidth = 4; /* first try 4 byte samples */
+
+ if (!wantinchans && !wantoutchans)
+ return (1);
+
+ alsa_checkversion();
+ if (sys_verbose)
+ {
+ if ((err = snd_card_get_longname(alsa_devno-1, &cardname)) < 0)
+ {
+ fprintf(stderr, "PD-ALSA: unable to get name of card number %d\n",
+ alsa_devno);
+ return 1;
+ }
+ fprintf(stderr, "PD-ALSA: using card %s\n", cardname);
+ free(cardname);
+ }
+
+ if ((err = snd_ctl_open(&handle, alsa_devno-1)) < 0)
+ {
+ fprintf(stderr, "PD-ALSA: unable to open control: %s\n",
+ snd_strerror(err));
+ return 1;
+ }
+
+ if ((err = snd_ctl_hw_info(handle, &hwinfo)) < 0)
+ {
+ fprintf(stderr, "PD-ALSA: unable to open get info: %s\n",
+ snd_strerror(err));
+ return 1;
+ }
+ if (hwinfo.pcmdevs < 1)
+ {
+ fprintf(stderr, "PD-ALSA: device %d doesn't support PCM\n",
+ alsa_devno);
+ snd_ctl_close(handle);
+ return 1;
+ }
+
+ if ((err = snd_ctl_pcm_info(handle, 0, &pcminfo)) < 0)
+ {
+ fprintf(stderr, "PD-ALSA: unable to open get pcm info: %s\n",
+ snd_strerror(err));
+ snd_ctl_close(handle);
+ return (1);
+ }
+ snd_ctl_close(handle);
+
+ /* find out if opening for input, output, or both and check that the
+ device can handle it. */
+ if (wantinchans && wantoutchans)
+ {
+ if (!(pcminfo.flags & SND_PCM_INFO_DUPLEX))
+ {
+ fprintf(stderr, "PD-ALSA: device is not full duplex\n");
+ return (1);
+ }
+ dir = SND_PCM_OPEN_DUPLEX;
+ }
+ else if (wantoutchans)
+ {
+ if (!(pcminfo.flags & SND_PCM_INFO_PLAYBACK))
+ {
+ fprintf(stderr, "PD-ALSA: device is not full duplex\n");
+ return (1);
+ }
+ dir = SND_PCM_OPEN_PLAYBACK;
+ }
+ else
+ {
+ if (!(pcminfo.flags & SND_PCM_INFO_CAPTURE))
+ {
+ fprintf(stderr, "PD-ALSA: device is not full duplex\n");
+ return (1);
+ }
+ dir = SND_PCM_OPEN_CAPTURE;
+ }
+
+ /* try to open the device */
+ if ((err = snd_pcm_open(&alsa_device[0].handle, alsa_devno-1, 0, dir)) < 0)
+ {
+ fprintf(stderr, "PD-ALSA: error opening device: %s\n",
+ snd_strerror(err));
+ return (1);
+ }
+ /* get information from the handle */
+ if (wantinchans)
+ {
+ channelinfo.channel = SND_PCM_CHANNEL_CAPTURE;
+ channelinfo.subdevice = 0;
+ if ((err = snd_pcm_channel_info(alsa_device[0].handle, &channelinfo))
+ < 0)
+ {
+ fprintf(stderr, "PD-ALSA: snd_pcm_channel_info (input): %s\n",
+ snd_strerror(err));
+ return (1);
+ }
+ if (sys_verbose)
+ post("input channels supported: %d-%d\n",
+ channelinfo.min_voices, channelinfo.max_voices);
+
+ if (wantinchans < channelinfo.min_voices)
+ post("increasing input channels to minimum of %d\n",
+ wantinchans = channelinfo.min_voices);
+ if (wantinchans > channelinfo.max_voices)
+ post("decreasing input channels to maximum of %d\n",
+ wantinchans = channelinfo.max_voices);
+ if (alsa_samplewidth == 4 &&
+ !(channelinfo.formats & (1<<SND_PCM_SFMT_S32_LE)))
+ {
+ fprintf(stderr,
+ "PD_ALSA: input doesn't support 32-bit samples; using 16\n");
+ alsa_samplewidth = 2;
+ }
+ if (alsa_samplewidth == 2 &&
+ !(channelinfo.formats & (1<<SND_PCM_SFMT_S16_LE)))
+ {
+ fprintf(stderr,
+ "PD_ALSA: can't find 4 or 2 byte format; giving up\n");
+ return (1);
+ }
+ }
+
+ if (wantoutchans)
+ {
+ channelinfo.channel = SND_PCM_CHANNEL_PLAYBACK;
+ channelinfo.subdevice = 0;
+ if ((err = snd_pcm_channel_info(alsa_device[0].handle, &channelinfo))
+ < 0)
+ {
+ fprintf(stderr, "PD-ALSA: snd_pcm_channel_info (output): %s\n",
+ snd_strerror(err));
+ return (1);
+ }
+ if (sys_verbose)
+ post("output channels supported: %d-%d\n",
+ channelinfo.min_voices, channelinfo.max_voices);
+ if (wantoutchans < channelinfo.min_voices)
+ post("increasing output channels to minimum of %d\n",
+ wantoutchans = channelinfo.min_voices);
+ if (wantoutchans > channelinfo.max_voices)
+ post("decreasing output channels to maximum of %d\n",
+ wantoutchans = channelinfo.max_voices);
+ if (alsa_samplewidth == 4 &&
+ !(channelinfo.formats & (1<<SND_PCM_SFMT_S32_LE)))
+ {
+ fprintf(stderr,
+ "PD_ALSA: output doesn't support 32-bit samples; using 16\n");
+ alsa_samplewidth = 2;
+ }
+ if (alsa_samplewidth == 2 &&
+ !(channelinfo.formats & (1<<SND_PCM_SFMT_S16_LE)))
+ {
+ fprintf(stderr,
+ "PD_ALSA: can't find 4 or 2 byte format; giving up\n");
+ return (1);
+ }
+ }
+
+ linux_setsr(rate);
+ linux_setch(wantinchans, wantoutchans);
+
+ if (wantinchans)
+ alsa_set_params(&alsa_device[0], SND_PCM_CHANNEL_CAPTURE,
+ srate, wantinchans);
+ if (wantoutchans)
+ alsa_set_params(&alsa_device[0], SND_PCM_CHANNEL_PLAYBACK,
+ srate, wantoutchans);
+
+ n_alsa_dev = 1;
+
+ /* check that all is as we think it should be */
+ for (i = 0; i < n_alsa_dev; i++)
+ {
+ /* We need to handle if the rate is not the same for all
+ * devices. For now just hope. */
+ rate = alsa_device[i].setup.format.rate;
+
+ /* It turns out that this checking does not work on all of my cards
+ * - in full duplex on my trident 4dwave the setup on the capture channel
+ * shows a sampling rate of 0. This is not true on my ess solo1. Checking
+ * the dac last helps the problem. All of this needs to be much smarter
+ * anyway (last minute hack). A warning above is all I have time for.
+ */
+ if (rate != srate)
+ {
+ post("PD-ALSA: unable to obtain rate %i using %i", srate, rate);
+ post("PD-ALSA: (despite this warning Pd might still work.)");
+ }
+ }
+ bsize = alsa_samplewidth *
+ (linux_inchannels > linux_outchannels ? linux_inchannels :
+ linux_outchannels) * DACBLKSIZE;
+ alsa_buf = malloc(bsize);
+ if (!alsa_buf)
+ return (1);
+ memset(alsa_buf, 0, bsize);
+ return 0;
+}
+
+void alsa_set_params(t_alsa_dev *dev, int dir, int rate, int voices)
+{
+ int err;
+ struct snd_pcm_channel_params params;
+
+ memset(&dev->info, 0, sizeof(dev->info));
+ dev->info.channel = dir;
+ if ((err = snd_pcm_channel_info(dev->handle, &dev->info) < 0))
+ {
+ fprintf(stderr, "PD-ALSA: error getting channel info: %s\n",
+ snd_strerror(err));
+ }
+ memset(&params, 0, sizeof(params));
+ params.format.interleave = 1; /* may do non-interleaved later */
+ /* format is 2 or 4 bytes per sample depending on what was possible */
+ params.format.format =
+ (alsa_samplewidth == 4 ? SND_PCM_SFMT_S32_LE : SND_PCM_SFMT_S16_LE);
+
+ /*will check this further down -just try for now*/
+ params.format.rate = rate;
+ params.format.voices = voices;
+ params.start_mode = SND_PCM_START_GO; /* seems most reliable */
+ /*do not stop at overrun/underrun*/
+ params.stop_mode = SND_PCM_STOP_ROLLOVER;
+
+ params.channel = dir; /* playback|capture */
+ params.buf.stream.queue_size =
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * voices;
+ params.buf.stream.fill = SND_PCM_FILL_SILENCE_WHOLE;
+ params.mode = SND_PCM_MODE_STREAM;
+
+ if ((err = snd_pcm_channel_params(dev->handle, &params)) < 0)
+ {
+ printf("PD-ALSA: error setting parameters %s", snd_strerror(err));
+ }
+
+ /* This should clear the buffers but does not. There is often noise at
+ startup that sounds like crap left in the buffers - maybe in the lib
+ instead of the driver? Some solution needs to be found.
+ */
+
+ if ((err = snd_pcm_channel_prepare(dev->handle, dir)) < 0)
+ {
+ printf("PD-ALSA: error preparing channel %s", snd_strerror(err));
+ }
+ dev->setup.channel = dir;
+
+ if ((err = snd_pcm_channel_setup(dev->handle, &dev->setup)) < 0)
+ {
+ printf("PD-ALSA: error getting setup %s", snd_strerror(err));
+ }
+ /* for some reason, if you don't writesomething before starting the
+ converters we get trash on startup */
+ if (dir == SND_PCM_CHANNEL_PLAYBACK)
+ {
+ char foo[1024];
+ int xxx = 1024 - (1024 % (linux_outchannels * alsa_samplewidth));
+ int i, r;
+ for (i = 0; i < xxx; i++)
+ foo[i] = 0;
+ if ((r = snd_pcm_write(dev->handle, foo, xxx)) < xxx)
+ fprintf(stderr, "alsa_write: %s\n", snd_strerror(errno));
+ }
+ snd_pcm_channel_go(dev->handle, dir);
+}
+
+void alsa_close_audio(void)
+{
+ int i;
+ for(i = 0; i < n_alsa_dev; i++)
+ snd_pcm_close(alsa_device[i].handle);
+}
+
+/* #define DEBUG_ALSA_XFER */
+
+int alsa_send_dacs(void)
+{
+ static int16_t *sp;
+ t_sample *fp, *fp1, *fp2;
+ int i, j, k, err, devno = 0;
+ int inputcount = 0, outputcount = 0, inputlate = 0, outputlate = 0;
+ int result;
+ snd_pcm_channel_status_t stat;
+ static int callno = 0;
+ static int xferno = 0;
+ int countwas = 0;
+ double timelast;
+ static double timenow;
+ int inchannels = linux_inchannels;
+ int outchannels = linux_outchannels;
+ int inbytesperframe = inchannels * alsa_samplewidth;
+ int outbytesperframe = outchannels * alsa_samplewidth;
+ int intransfersize = DACBLKSIZE * inbytesperframe;
+ int outtransfersize = DACBLKSIZE * outbytesperframe;
+ int alsaerror;
+ int loggederror = 0;
+
+ if (!inchannels && !outchannels)
+ return (SENDDACS_NO);
+ timelast = timenow;
+ timenow = sys_getrealtime();
+
+#ifdef DEBUG_ALSA_XFER
+ if (timenow - timelast > 0.050)
+ fprintf(stderr, "(%d)",
+ (int)(1000 * (timenow - timelast))), fflush(stderr);
+#endif
+
+ callno++;
+ /* get input and output channel status */
+ if (inchannels > 0)
+ {
+ devno = 0;
+ stat.channel = SND_PCM_CHANNEL_CAPTURE;
+ if (alsaerror = snd_pcm_channel_status(alsa_device[devno].handle,
+ &stat))
+ {
+ fprintf(stderr, "snd_pcm_channel_status (input): %s\n",
+ snd_strerror(alsaerror));
+ return (SENDDACS_NO);
+ }
+ inputcount = stat.count;
+ inputlate = (stat.underrun > 0 || stat.overrun > 0);
+ }
+ if (outchannels > 0)
+ {
+ devno = 0;
+ stat.channel = SND_PCM_CHANNEL_PLAYBACK;
+ if (alsaerror = snd_pcm_channel_status(alsa_device[devno].handle,
+ &stat))
+ {
+ fprintf(stderr, "snd_pcm_channel_status (output): %s\n",
+ snd_strerror(alsaerror));
+ return (SENDDACS_NO);
+ }
+ outputcount = stat.count;
+ outputlate = (stat.underrun > 0 || stat.overrun > 0);
+ }
+
+ /* check if input not ready */
+ if (inputcount < intransfersize)
+ {
+ /* fprintf(stderr, "no adc; count %d, free %d, call %d, xfer %d\n",
+ stat.count,
+ stat.free,
+ callno, xferno); */
+ if (outchannels > 0)
+ {
+ /* if there's no input but output is hungry, feed output. */
+ while (outputcount < (linux_advance_samples + ALSA_JITTER)
+ * outbytesperframe)
+ {
+ if (!loggederror)
+ sys_log_error(ERR_RESYNC), loggederror = 1;
+ memset(alsa_buf, 0, outtransfersize);
+ result = snd_pcm_write(alsa_device[devno].handle,
+ alsa_buf, outtransfersize);
+ if (result < outtransfersize)
+ {
+#ifdef DEBUG_ALSA_XFER
+ if (result >= 0 || errno == EAGAIN)
+ fprintf(stderr, "ALSA: write returned %d of %d\n",
+ result, outtransfersize);
+ else fprintf(stderr, "ALSA: write: %s\n",
+ snd_strerror(errno));
+ fprintf(stderr,
+ "inputcount %d, outputcount %d, outbufsize %d\n",
+ inputcount, outputcount,
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * outchannels);
+#endif
+ return (SENDDACS_NO);
+ }
+ stat.channel = SND_PCM_CHANNEL_PLAYBACK;
+ if (alsaerror =
+ snd_pcm_channel_status(alsa_device[devno].handle,
+ &stat))
+ {
+ fprintf(stderr, "snd_pcm_channel_status (output): %s\n",
+ snd_strerror(alsaerror));
+ return (SENDDACS_NO);
+ }
+ outputcount = stat.count;
+ }
+ }
+
+ return SENDDACS_NO;
+ }
+
+ /* if output buffer has at least linux_advance_samples in it, we're
+ not ready for this batch. */
+ if (outputcount > linux_advance_samples * outbytesperframe)
+ {
+ if (inchannels > 0)
+ {
+ while (inputcount > (DACBLKSIZE + ALSA_JITTER) * outbytesperframe)
+ {
+ if (!loggederror)
+ sys_log_error(ERR_RESYNC), loggederror = 1;
+ devno = 0;
+ result = snd_pcm_read(alsa_device[devno].handle, alsa_buf,
+ intransfersize);
+ if (result < intransfersize)
+ {
+#ifdef DEBUG_ALSA_XFER
+ if (result < 0)
+ fprintf(stderr,
+ "snd_pcm_read %d %d: %s\n",
+ callno, xferno, snd_strerror(errno));
+ else fprintf(stderr,
+ "snd_pcm_read %d %d returned only %d\n",
+ callno, xferno, result);
+ fprintf(stderr,
+ "inputcount %d, outputcount %d, inbufsize %d\n",
+ inputcount, outputcount,
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * inchannels);
+#endif
+ return (SENDDACS_NO);
+ }
+ devno = 0;
+ stat.channel = SND_PCM_CHANNEL_CAPTURE;
+ if (alsaerror =
+ snd_pcm_channel_status(alsa_device[devno].handle,
+ &stat))
+ {
+ fprintf(stderr, "snd_pcm_channel_status (input): %s\n",
+ snd_strerror(alsaerror));
+ return (SENDDACS_NO);
+ }
+ inputcount = stat.count;
+ inputlate = (stat.underrun > 0 || stat.overrun > 0);
+ }
+ return (SENDDACS_NO);
+ }
+ }
+ if (sys_getrealtime() - timenow > 0.002)
+ {
+#ifdef DEBUG_ALSA_XFER
+ fprintf(stderr, "check %d took %d msec\n",
+ callno, (int)(1000 * (timenow - timelast))), fflush(stderr);
+#endif
+ sys_log_error(ERR_DACSLEPT);
+ timenow = sys_getrealtime();
+ }
+ if (inputlate || outputlate)
+ sys_log_error(ERR_DATALATE);
+
+ /* do output */
+ /* this "for" loop won't work for more than one device. */
+ for (devno = 0, fp = sys_soundout; devno < (outchannels > 0); devno++,
+ fp += 128)
+ {
+ if (alsa_samplewidth == 4)
+ {
+ for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--;
+ j += outchannels, fp2++)
+ {
+ float s1 = *fp2 * INT32_MAX;
+ ((t_alsa_sample32 *)alsa_buf)[j] = CLIP32(s1);
+ }
+ }
+ }
+ else
+ {
+ for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--;
+ j += outchannels, fp2++)
+ {
+ int s = *fp2 * 32767.;
+ if (s > 32767)
+ s = 32767;
+ else if (s < -32767)
+ s = -32767;
+ ((t_alsa_sample16 *)alsa_buf)[j] = s;
+ }
+ }
+ }
+
+ result = snd_pcm_write(alsa_device[devno].handle, alsa_buf,
+ outtransfersize);
+ if (result < outtransfersize)
+ {
+#ifdef DEBUG_ALSA_XFER
+ if (result >= 0 || errno == EAGAIN)
+ fprintf(stderr, "ALSA: write returned %d of %d\n",
+ result, outtransfersize);
+ else fprintf(stderr, "ALSA: write: %s\n",
+ snd_strerror(errno));
+ fprintf(stderr,
+ "inputcount %d, outputcount %d, outbufsize %d\n",
+ inputcount, outputcount,
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * outchannels);
+#endif
+ sys_log_error(ERR_DACSLEPT);
+ return (SENDDACS_NO);
+ }
+ }
+ /* zero out the output buffer */
+ memset(sys_soundout, 0, DACBLKSIZE * sizeof(*sys_soundout) *
+ linux_outchannels);
+ if (sys_getrealtime() - timenow > 0.002)
+ {
+#if DEBUG_ALSA_XFER
+ fprintf(stderr, "output %d took %d msec\n",
+ callno, (int)(1000 * (timenow - timelast))), fflush(stderr);
+#endif
+ timenow = sys_getrealtime();
+ sys_log_error(ERR_DACSLEPT);
+ }
+
+ /* do input */
+ for (devno = 0, fp = sys_soundin; devno < (linux_inchannels > 0); devno++,
+ fp += 128)
+ {
+ result = snd_pcm_read(alsa_device[devno].handle, alsa_buf,
+ intransfersize);
+ if (result < intransfersize)
+ {
+#ifdef DEBUG_ALSA_XFER
+ if (result < 0)
+ fprintf(stderr,
+ "snd_pcm_read %d %d: %s\n",
+ callno, xferno, snd_strerror(errno));
+ else fprintf(stderr,
+ "snd_pcm_read %d %d returned only %d\n",
+ callno, xferno, result);
+ fprintf(stderr,
+ "inputcount %d, outputcount %d, inbufsize %d\n",
+ inputcount, outputcount,
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * inchannels);
+#endif
+ sys_log_error(ERR_ADCSLEPT);
+ return (SENDDACS_NO);
+ }
+ if (alsa_samplewidth == 4)
+ {
+ for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--;
+ j += inchannels, fp2++)
+ *fp2 = (float) ((t_alsa_sample32 *)alsa_buf)[j]
+ * (1./ INT32_MAX);
+ }
+ }
+ else
+ {
+ for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; j += inchannels, fp2++)
+ *fp2 = (float) ((t_alsa_sample16 *)alsa_buf)[j]
+ * 3.051850e-05;
+ }
+ }
+ }
+ xferno++;
+ if (sys_getrealtime() - timenow > 0.002)
+ {
+#ifdef DEBUG_ALSA_XFER
+ fprintf(stderr, "routine took %d msec\n",
+ (int)(1000 * (sys_getrealtime() - timenow)));
+#endif
+ sys_log_error(ERR_ADCSLEPT);
+ }
+ return SENDDACS_YES;
+}
+
+#endif /* ALSA99 */
+
+/* support for ALSA pcmv2 api by Karl MacMillan<karlmac@peabody.jhu.edu> */
+
+#ifdef ALSA01
+
+static void check_error(int err, const char *why)
+{
+ if (err < 0)
+ fprintf(stderr, "%s: %s\n", why, snd_strerror(err));
+}
+
+static int alsa_open_audio(int wantinchans, int wantoutchans, int srate)
+{
+ int err, inchans = 0, outchans = 0, subunitdir;
+ char devname[512];
+ snd_pcm_hw_params_t* hw_params;
+ snd_pcm_sw_params_t* sw_params;
+ snd_output_t* out;
+ int frag_size = (linux_fragsize ? linux_fragsize : ALSA_DEFFRAGSIZE);
+ int nfrags, i;
+ short* tmp_buf;
+ unsigned int tmp_uint;
+ int advwas = sys_schedadvance;
+
+ if (linux_nfragment)
+ {
+ nfrags = linux_nfragment;
+ sys_schedadvance = (frag_size * linux_nfragment * 1.0e6) / srate;
+ }
+ else nfrags = sys_schedadvance * (float)srate / (1e6 * frag_size);
+
+ if (sys_verbose || (sys_schedadvance != advwas))
+ post("audio buffer set to %d", (int)(0.001 * sys_schedadvance));
+ if (wantinchans || wantoutchans)
+ alsa_checkversion();
+ if (wantinchans)
+ {
+ err = snd_pcm_open(&alsa_device.inhandle, alsa_devname,
+ SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
+
+ check_error(err, "snd_pcm_open (input)");
+ if (err < 0)
+ inchans = 0;
+ else
+ {
+ inchans = wantinchans;
+ snd_pcm_nonblock(alsa_device.inhandle, 1);
+ }
+ }
+ if (wantoutchans)
+ {
+ err = snd_pcm_open(&alsa_device.outhandle, alsa_devname,
+ SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+
+ check_error(err, "snd_pcm_open (output)");
+ if (err < 0)
+ outchans = 0;
+ else
+ {
+ outchans = wantoutchans;
+ snd_pcm_nonblock(alsa_device.outhandle, 1);
+ }
+ }
+ if (inchans)
+ {
+ if (sys_verbose)
+ post("opening sound input...");
+ err = snd_pcm_hw_params_malloc(&hw_params);
+ check_error(err, "snd_pcm_hw_params_malloc (input)");
+
+ // get the default params
+ err = snd_pcm_hw_params_any(alsa_device.inhandle, hw_params);
+ check_error(err, "snd_pcm_hw_params_any (input)");
+ // set interleaved access - FIXME deal with other access types
+ err = snd_pcm_hw_params_set_access(alsa_device.inhandle, hw_params,
+ SND_PCM_ACCESS_RW_INTERLEAVED);
+ check_error(err, "snd_pcm_hw_params_set_access (input)");
+ // Try to set 32 bit format first
+ err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params,
+ SND_PCM_FORMAT_S32);
+ if (err < 0)
+ {
+ /* fprintf(stderr,
+ "PD-ALSA: 32 bit format not available - using 16\n"); */
+ err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params,
+ SND_PCM_FORMAT_S16);
+ check_error(err, "snd_pcm_hw_params_set_format (input)");
+ alsa_samplewidth = 2;
+ }
+ else
+ {
+ alsa_samplewidth = 4;
+ }
+ post("Sample width set to %d bytes", alsa_samplewidth);
+ // set the subformat
+ err = snd_pcm_hw_params_set_subformat(alsa_device.inhandle, hw_params,
+ SND_PCM_SUBFORMAT_STD);
+ check_error(err, "snd_pcm_hw_params_set_subformat (input)");
+ // set the number of channels
+ tmp_uint = inchans;
+ err = snd_pcm_hw_params_set_channels_min(alsa_device.inhandle,
+ hw_params, &tmp_uint);
+ check_error(err, "snd_pcm_hw_params_set_channels (input)");
+ if (tmp_uint != (unsigned)inchans)
+ post("ALSA: set input channels to %d", tmp_uint);
+ inchans = tmp_uint;
+ // set the sampling rate
+ err = snd_pcm_hw_params_set_rate_min(alsa_device.inhandle, hw_params,
+ &srate, 0);
+ check_error(err, "snd_pcm_hw_params_set_rate_min (input)");
+#if 0
+ err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir);
+ post("input sample rate %d", err);
+#endif
+ // set the period - ie frag size
+ // post("fragsize a %d", frag_size);
+
+ /* LATER try this to get a recommended period size...
+ right now, it trips an assertion failure in ALSA lib */
+#if 0
+ post("input period was %d, min %d, max %d\n",
+ snd_pcm_hw_params_get_period_size(hw_params, 0),
+ snd_pcm_hw_params_get_period_size_min(hw_params, 0),
+ snd_pcm_hw_params_get_period_size_max(hw_params, 0));
+#endif
+ err = snd_pcm_hw_params_set_period_size_near(alsa_device.inhandle,
+ hw_params,
+ (snd_pcm_uframes_t)
+ frag_size, 0);
+ check_error(err, "snd_pcm_hw_params_set_period_size_near (input)");
+ // post("fragsize b %d", frag_size);
+ // set the number of periods - ie numfrags
+ // post("nfrags a %d", nfrags);
+ err = snd_pcm_hw_params_set_periods_near(alsa_device.inhandle,
+ hw_params, nfrags, 0);
+ check_error(err, "snd_pcm_hw_params_set_periods_near (input)");
+ // set the buffer size
+ err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.inhandle,
+ hw_params, nfrags * frag_size);
+ check_error(err, "snd_pcm_hw_params_set_buffer_size_near (input)");
+
+ err = snd_pcm_hw_params(alsa_device.inhandle, hw_params);
+ check_error(err, "snd_pcm_hw_params (input)");
+
+ snd_pcm_hw_params_free(hw_params);
+
+ err = snd_pcm_sw_params_malloc(&sw_params);
+ check_error(err, "snd_pcm_sw_params_malloc (input)");
+ err = snd_pcm_sw_params_current(alsa_device.inhandle, sw_params);
+ check_error(err, "snd_pcm_sw_params_current (input)");
+#if 1
+ err = snd_pcm_sw_params_set_start_mode(alsa_device.inhandle, sw_params,
+ SND_PCM_START_EXPLICIT);
+ check_error(err, "snd_pcm_sw_params_set_start_mode (input)");
+ err = snd_pcm_sw_params_set_xrun_mode(alsa_device.inhandle, sw_params,
+ SND_PCM_XRUN_NONE);
+ check_error(err, "snd_pcm_sw_params_set_xrun_mode (input)");
+#else
+ err = snd_pcm_sw_params_set_start_threshold(alsa_device.inhandle,
+ sw_params, nfrags * frag_size);
+ check_error(err, "snd_pcm_sw_params_set_start_threshold (input)");
+ err = snd_pcm_sw_params_set_stop_threshold(alsa_device.inhandle,
+ sw_params, 1);
+ check_error(err, "snd_pcm_sw_params_set_stop_threshold (input)");
+#endif
+
+ err = snd_pcm_sw_params_set_avail_min(alsa_device.inhandle, sw_params,
+ frag_size);
+ check_error(err, "snd_pcm_sw_params_set_avail_min (input)");
+ err = snd_pcm_sw_params(alsa_device.inhandle, sw_params);
+ check_error(err, "snd_pcm_sw_params (input)");
+
+ snd_pcm_sw_params_free(sw_params);
+
+ snd_output_stdio_attach(&out, stderr, 0);
+#if 0
+ if (sys_verbose)
+ {
+ snd_pcm_dump_hw_setup(alsa_device.inhandle, out);
+ snd_pcm_dump_sw_setup(alsa_device.inhandle, out);
+ }
+#endif
+ }
+
+ if (outchans)
+ {
+ int foo;
+ if (sys_verbose)
+ post("opening sound output...");
+ err = snd_pcm_hw_params_malloc(&hw_params);
+ check_error(err, "snd_pcm_sw_params (output)");
+
+ // get the default params
+ err = snd_pcm_hw_params_any(alsa_device.outhandle, hw_params);
+ check_error(err, "snd_pcm_hw_params_any (output)");
+ // set interleaved access - FIXME deal with other access types
+ err = snd_pcm_hw_params_set_access(alsa_device.outhandle, hw_params,
+ SND_PCM_ACCESS_RW_INTERLEAVED);
+ check_error(err, "snd_pcm_hw_params_set_access (output)");
+ // Try to set 32 bit format first
+ err = snd_pcm_hw_params_set_format(alsa_device.outhandle, hw_params,
+ SND_PCM_FORMAT_S32);
+ if (err < 0)
+ {
+ err = snd_pcm_hw_params_set_format(alsa_device.outhandle,
+ hw_params,SND_PCM_FORMAT_S16);
+ check_error(err, "snd_pcm_hw_params_set_format (output)");
+ /* fprintf(stderr,
+ "PD-ALSA: 32 bit format not available - using 16\n"); */
+ alsa_samplewidth = 2;
+ }
+ else
+ {
+ alsa_samplewidth = 4;
+ }
+ // set the subformat
+ err = snd_pcm_hw_params_set_subformat(alsa_device.outhandle, hw_params,
+ SND_PCM_SUBFORMAT_STD);
+ check_error(err, "snd_pcm_hw_params_set_subformat (output)");
+ // set the number of channels
+ tmp_uint = outchans;
+ err = snd_pcm_hw_params_set_channels_min(alsa_device.outhandle,
+ hw_params, &tmp_uint);
+ check_error(err, "snd_pcm_hw_params_set_channels (output)");
+ if (tmp_uint != (unsigned)outchans)
+ post("alsa: set output channels to %d", tmp_uint);
+ outchans = tmp_uint;
+ // set the sampling rate
+ err = snd_pcm_hw_params_set_rate_min(alsa_device.outhandle, hw_params,
+ &srate, 0);
+ check_error(err, "snd_pcm_hw_params_set_rate_min (output)");
+#if 0
+ err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir);
+ post("output sample rate %d", err);
+#endif
+ // set the period - ie frag size
+#if 0
+ post("output period was %d, min %d, max %d\n",
+ snd_pcm_hw_params_get_period_size(hw_params, 0),
+ snd_pcm_hw_params_get_period_size_min(hw_params, 0),
+ snd_pcm_hw_params_get_period_size_max(hw_params, 0));
+#endif
+ // post("fragsize c %d", frag_size);
+ err = snd_pcm_hw_params_set_period_size_near(alsa_device.outhandle,
+ hw_params,
+ (snd_pcm_uframes_t)
+ frag_size, 0);
+ // post("fragsize d %d", frag_size);
+ check_error(err, "snd_pcm_hw_params_set_period_size_near (output)");
+ // set the number of periods - ie numfrags
+ err = snd_pcm_hw_params_set_periods_near(alsa_device.outhandle,
+ hw_params, nfrags, 0);
+ check_error(err, "snd_pcm_hw_params_set_periods_near (output)");
+ // set the buffer size
+ err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.outhandle,
+ hw_params, nfrags * frag_size);
+
+ check_error(err, "snd_pcm_hw_params_set_buffer_size_near (output)");
+
+ err = snd_pcm_hw_params(alsa_device.outhandle, hw_params);
+ check_error(err, "snd_pcm_hw_params (output)");
+
+ snd_pcm_hw_params_free(hw_params);
+
+ err = snd_pcm_sw_params_malloc(&sw_params);
+ check_error(err, "snd_pcm_sw_params_malloc (output)");
+ err = snd_pcm_sw_params_current(alsa_device.outhandle, sw_params);
+ check_error(err, "snd_pcm_sw_params_current (output)");
+#if 1
+ err = snd_pcm_sw_params_set_start_mode(alsa_device.outhandle,
+ sw_params,
+ SND_PCM_START_EXPLICIT);
+ check_error(err, "snd_pcm_sw_params_set_start_mode (output)");
+ err = snd_pcm_sw_params_set_xrun_mode(alsa_device.outhandle, sw_params,
+ SND_PCM_XRUN_NONE);
+ check_error(err, "snd_pcm_sw_params_set_xrun_mode (output)");
+#else
+ err = snd_pcm_sw_params_set_start_threshold(alsa_device.inhandle,
+ sw_params, nfrags * frag_size);
+ check_error(err, "snd_pcm_sw_params_set_start_threshold (output)");
+ err = snd_pcm_sw_params_set_stop_threshold(alsa_device.inhandle,
+ sw_params, 1);
+ check_error(err, "snd_pcm_sw_params_set_stop_threshold (output)");
+#endif
+
+ err = snd_pcm_sw_params_set_avail_min(alsa_device.outhandle, sw_params,
+ frag_size);
+ check_error(err, "snd_pcm_sw_params_set_avail_min (output)");
+ err = snd_pcm_sw_params(alsa_device.outhandle, sw_params);
+ check_error(err, "snd_pcm_sw_params (output)");
+
+ snd_pcm_sw_params_free(sw_params);
+
+ snd_output_stdio_attach(&out, stderr, 0);
+#if 0
+ if (sys_verbose)
+ {
+ snd_pcm_dump_hw_setup(alsa_device.outhandle, out);
+ snd_pcm_dump_sw_setup(alsa_device.outhandle, out);
+ }
+#endif
+ }
+
+ linux_setsr(srate);
+ linux_setch(inchans, outchans);
+
+ if (inchans)
+ snd_pcm_prepare(alsa_device.inhandle);
+ if (outchans)
+ snd_pcm_prepare(alsa_device.outhandle);
+
+ // if duplex we can link the channels so they start together
+ if (inchans && outchans)
+ snd_pcm_link(alsa_device.inhandle, alsa_device.outhandle);
+
+ // set up the buffer
+ if (outchans > inchans)
+ alsa_buf = (short *)calloc(sizeof(char) * alsa_samplewidth, DACBLKSIZE
+ * outchans);
+ else
+ alsa_buf = (short *)calloc(sizeof(char) * alsa_samplewidth, DACBLKSIZE
+ * inchans);
+ // fill the buffer with silence
+ if (outchans)
+ {
+ i = nfrags + 1;
+ while (i--)
+ snd_pcm_writei(alsa_device.outhandle, alsa_buf, frag_size);
+ }
+
+ // set up the status variables
+ err = snd_pcm_status_malloc(&in_status);
+ check_error(err, "snd_pcm_status_malloc");
+ err = snd_pcm_status_malloc(&out_status);
+ check_error(err, "snd_pcm_status_malloc");
+
+ // start the device
+#if 1
+ if (outchans)
+ {
+ err = snd_pcm_start(alsa_device.outhandle);
+ check_error(err, "snd_pcm_start");
+ }
+ else if (inchans)
+ {
+ err = snd_pcm_start(alsa_device.inhandle);
+ check_error(err, "snd_pcm_start");
+ }
+#endif
+
+ return 0;
+}
+
+void alsa_close_audio(void)
+{
+ int err;
+ if (linux_inchannels)
+ {
+ err = snd_pcm_close(alsa_device.inhandle);
+ check_error(err, "snd_pcm_close (input)");
+ }
+ if (linux_outchannels)
+ {
+ err = snd_pcm_close(alsa_device.outhandle);
+ check_error(err, "snd_pcm_close (output)");
+ }
+}
+
+// #define DEBUG_ALSA_XFER
+
+int alsa_send_dacs(void)
+{
+ static int16_t *sp;
+ static int xferno = 0;
+ static int callno = 0;
+ static double timenow;
+ double timelast;
+ t_sample *fp, *fp1, *fp2;
+ int i, j, k, err, devno = 0;
+ int inputcount = 0, outputcount = 0, inputlate = 0, outputlate = 0;
+ int result;
+ int inchannels = linux_inchannels;
+ int outchannels = linux_outchannels;
+ unsigned int intransfersize = DACBLKSIZE;
+ unsigned int outtransfersize = DACBLKSIZE;
+
+ // get the status
+ if (!inchannels && !outchannels)
+ {
+ return SENDDACS_NO;
+ }
+
+ timelast = timenow;
+ timenow = sys_getrealtime();
+
+#ifdef DEBUG_ALSA_XFER
+ if (timenow - timelast > 0.050)
+ fprintf(stderr, "(%d)",
+ (int)(1000 * (timenow - timelast))), fflush(stderr);
+#endif
+
+ callno++;
+
+ if (inchannels)
+ {
+ snd_pcm_status(alsa_device.inhandle, in_status);
+ if (snd_pcm_status_get_avail(in_status) < intransfersize)
+ return SENDDACS_NO;
+ }
+ if (outchannels)
+ {
+ snd_pcm_status(alsa_device.outhandle, out_status);
+ if (snd_pcm_status_get_avail(out_status) < outtransfersize)
+ return SENDDACS_NO;
+ }
+
+ /* do output */
+ if (outchannels)
+ {
+ fp = sys_soundout;
+ if (alsa_samplewidth == 4)
+ {
+ for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--;
+ j += outchannels, fp2++)
+ {
+ float s1 = *fp2 * INT32_MAX;
+ ((t_alsa_sample32 *)alsa_buf)[j] = CLIP32(s1);
+ }
+ }
+ }
+ else
+ {
+ for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--;
+ j += outchannels, fp2++)
+ {
+ int s = *fp2 * 32767.;
+ if (s > 32767)
+ s = 32767;
+ else if (s < -32767)
+ s = -32767;
+ ((t_alsa_sample16 *)alsa_buf)[j] = s;
+ }
+ }
+ }
+
+ result = snd_pcm_writei(alsa_device.outhandle, alsa_buf,
+ outtransfersize);
+ if (result != (int)outtransfersize)
+ {
+ #ifdef DEBUG_ALSA_XFER
+ if (result >= 0 || errno == EAGAIN)
+ fprintf(stderr, "ALSA: write returned %d of %d\n",
+ result, outtransfersize);
+ else fprintf(stderr, "ALSA: write: %s\n",
+ snd_strerror(errno));
+ fprintf(stderr,
+ "inputcount %d, outputcount %d, outbufsize %d\n",
+ inputcount, outputcount,
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * outchannels);
+ #endif
+ sys_log_error(ERR_DACSLEPT);
+ return (SENDDACS_NO);
+ }
+
+ /* zero out the output buffer */
+ memset(sys_soundout, 0, DACBLKSIZE * sizeof(*sys_soundout) *
+ linux_outchannels);
+ if (sys_getrealtime() - timenow > 0.002)
+ {
+ #ifdef DEBUG_ALSA_XFER
+ fprintf(stderr, "output %d took %d msec\n",
+ callno, (int)(1000 * (timenow - timelast))), fflush(stderr);
+ #endif
+ timenow = sys_getrealtime();
+ sys_log_error(ERR_DACSLEPT);
+ }
+ }
+ /* do input */
+ if (linux_inchannels)
+ {
+ result = snd_pcm_readi(alsa_device.inhandle, alsa_buf, intransfersize);
+ if (result < (int)intransfersize)
+ {
+#ifdef DEBUG_ALSA_XFER
+ if (result < 0)
+ fprintf(stderr,
+ "snd_pcm_read %d %d: %s\n",
+ callno, xferno, snd_strerror(errno));
+ else fprintf(stderr,
+ "snd_pcm_read %d %d returned only %d\n",
+ callno, xferno, result);
+ fprintf(stderr,
+ "inputcount %d, outputcount %d, inbufsize %d\n",
+ inputcount, outputcount,
+ (ALSA_EXTRABUFFER + linux_advance_samples)
+ * alsa_samplewidth * inchannels);
+#endif
+ sys_log_error(ERR_ADCSLEPT);
+ return (SENDDACS_NO);
+ }
+ fp = sys_soundin;
+ if (alsa_samplewidth == 4)
+ {
+ for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--;
+ j += inchannels, fp2++)
+ *fp2 = (float) ((t_alsa_sample32 *)alsa_buf)[j]
+ * (1./ INT32_MAX);
+ }
+ }
+ else
+ {
+ for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE)
+ {
+ for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; j += inchannels,
+ fp2++)
+ *fp2 = (float) ((t_alsa_sample16 *)alsa_buf)[j]
+ * 3.051850e-05;
+ }
+ }
+ }
+ xferno++;
+ if (sys_getrealtime() - timenow > 0.002)
+ {
+#ifdef DEBUG_ALSA_XFER
+ fprintf(stderr, "routine took %d msec\n",
+ (int)(1000 * (sys_getrealtime() - timenow)));
+#endif
+ sys_log_error(ERR_ADCSLEPT);
+ }
+ return SENDDACS_YES;
+}
+
+void alsa_resync( void)
+{
+ int i, result;
+ if (linux_whichapi != API_ALSA)
+ {
+ error("restart-audio: implemented for ALSA only.");
+ return;
+ }
+ memset(alsa_buf, 0,
+ sizeof(char) * alsa_samplewidth * DACBLKSIZE * linux_outchannels);
+ for (i = 0; i < 100; i++)
+ {
+ result = snd_pcm_writei(alsa_device.outhandle, alsa_buf,
+ DACBLKSIZE);
+ if (result != (int)DACBLKSIZE)
+ break;
+ }
+ post("%d written", i);
+}
+
+
+#endif /* ALSA01 */
+
+/***************************************************
+ * Code using the RME_9652 API
+ */
+
+ /*
+ trying native device for future use of native memory map:
+ because of busmaster if you dont use the dac, you dont need
+ CPU Power und also no nearly no CPU-Power is used in device
+
+ since always all DAs and ADs are synced (else they wouldnt work)
+ we use linux_dacs[0], linux_adcs[0]
+ */
+
+#ifdef RME_HAMMERFALL
+
+#define RME9652_MAX_CHANNELS 26
+
+#define RME9652_CH_PER_NATIVE_DEVICE 1
+
+static int rme9652_dac_devices[RME9652_MAX_CHANNELS];
+static int rme9652_adc_devices[RME9652_MAX_CHANNELS];
+
+static char rme9652_dsp_dac[] = "/dev/rme9652/C0da%d";
+static char rme9652_dsp_adc[] = "/dev/rme9652/C0ad%d";
+
+static int num_of_rme9652_dac = 0;
+static int num_of_rme9652_adc = 0;
+
+static int rme_soundindevonset = 1;
+static int rme_soundoutdevonset = 1;
+
+void rme_soundindev(int which)
+{
+ rme_soundindevonset = which;
+}
+
+void rme_soundoutdev(int which)
+{
+ rme_soundoutdevonset = which;
+}
+
+void rme9652_configure(int dev, int fd,int srate, int dac) {
+ int orig, param, nblk;
+ audio_buf_info ainfo;
+ orig = param = srate;
+
+ /* samplerate */
+
+ fprintf(stderr,"RME9652: configuring %d, fd=%d, sr=%d\n, dac=%d\n",
+ dev,fd,srate,dac);
+
+ if (ioctl(fd,SNDCTL_DSP_SPEED,&param) == -1)
+ fprintf(stderr,"RME9652: Could not set sampling rate for device\n");
+ else if( orig != param )
+ fprintf(stderr,"RME9652: sampling rate: wanted %d, got %d\n",
+ orig, param );
+
+ // setting the correct samplerate (could be different than expected)
+ srate = param;
+
+
+ /* setting resolution */
+
+ /* use ctrlpanel to change, experiment, channels 1 */
+
+ orig = param = AFMT_S16_NE;
+ if (ioctl(fd,SNDCTL_DSP_SETFMT,&param) == -1)
+ fprintf(stderr,"RME9652: Could not set DSP format\n");
+ else if( orig != param )
+ fprintf(stderr,"RME9652: DSP format: wanted %d, got %d\n",orig, param );
+
+ /* setting channels */
+ orig = param = RME9652_CH_PER_NATIVE_DEVICE;
+
+ if (ioctl(fd,SNDCTL_DSP_CHANNELS,&param) == -1)
+ fprintf(stderr,"RME9652: Could not set channels\n");
+ else if( orig != param )
+ fprintf(stderr,"RME9652: num channels: wanted %d, got %d\n",orig, param );
+
+ if (dac)
+ {
+
+ /* use "free space" to learn the buffer size. Normally you
+ should set this to your own desired value; but this seems not
+ to be implemented uniformly across different sound cards. LATER
+ we should figure out what to do if the requested scheduler advance
+ is greater than this buffer size; for now, we just print something
+ out. */
+
+ if( ioctl(linux_dacs[0].d_fd, SOUND_PCM_GETOSPACE,&ainfo) < 0 )
+ fprintf(stderr,"RME: ioctl on output device %d failed",dev);
+
+ linux_dacs[0].d_bufsize = ainfo.bytes;
+
+ fprintf(stderr,"RME: ioctl SOUND_PCM_GETOSPACE says %d buffsize\n",
+ linux_dacs[0].d_bufsize);
+
+
+ if (linux_advance_samples * (RME_SAMPLEWIDTH *
+ RME9652_CH_PER_NATIVE_DEVICE)
+ > linux_dacs[0].d_bufsize - RME_BYTESPERCHAN)
+ {
+ fprintf(stderr,
+ "RME: requested audio buffer size %d limited to %d\n",
+ linux_advance_samples
+ * (RME_SAMPLEWIDTH * RME9652_CH_PER_NATIVE_DEVICE),
+ linux_dacs[0].d_bufsize);
+ linux_advance_samples =
+ (linux_dacs[0].d_bufsize - RME_BYTESPERCHAN)
+ / (RME_SAMPLEWIDTH *RME9652_CH_PER_NATIVE_DEVICE);
+ }
+ }
+}
+
+
+int rme9652_open_audio(int inchans, int outchans,int srate)
+{
+ int orig;
+ int tmp;
+ int inchannels = 0,outchannels = 0;
+ char devname[20];
+ int i;
+ char buf[RME_SAMPLEWIDTH*RME9652_CH_PER_NATIVE_DEVICE*DACBLKSIZE];
+ int num_devs = 0;
+ audio_buf_info ainfo;
+
+ linux_nindevs = linux_noutdevs = 0;
+
+ if (sys_verbose)
+ post("RME open");
+ /* First check if we can */
+ /* open the write ports */
+
+ for (num_devs=0; outchannels < outchans; num_devs++)
+ {
+ int channels = RME9652_CH_PER_NATIVE_DEVICE;
+
+ sprintf(devname, rme9652_dsp_dac, num_devs + rme_soundoutdevonset);
+ if ((tmp = open(devname,O_WRONLY)) == -1)
+ {
+ DEBUG(fprintf(stderr,"RME9652: failed to open %s writeonly\n",
+ devname);)
+ break;
+ }
+ DEBUG(fprintf(stderr,"RME9652: out device Nr. %d (%d) on %s\n",
+ linux_noutdevs+1,tmp,devname);)
+
+ if (outchans > outchannels)
+ {
+ rme9652_dac_devices[linux_noutdevs] = tmp;
+ linux_noutdevs++;
+ outchannels += channels;
+ }
+ else close(tmp);
+ }
+ if( linux_noutdevs > 0)
+ linux_dacs[0].d_fd = rme9652_dac_devices[0];
+
+ /* Second check if we can */
+ /* open the read ports */
+
+ for (num_devs=0; inchannels < inchans; num_devs++)
+ {
+ int channels = RME9652_CH_PER_NATIVE_DEVICE;
+
+ sprintf(devname, rme9652_dsp_adc, num_devs+rme_soundindevonset);
+
+ if ((tmp = open(devname,O_RDONLY)) == -1)
+ {
+ DEBUG(fprintf(stderr,"RME9652: failed to open %s readonly\n",
+ devname);)
+ break;
+ }
+ DEBUG(fprintf(stderr,"RME9652: in device Nr. %d (%d) on %s\n",
+ linux_nindevs+1,tmp,devname);)
+
+ if (inchans > inchannels)
+ {
+ rme9652_adc_devices[linux_nindevs] = tmp;
+ linux_nindevs++;
+ inchannels += channels;
+ }
+ else
+ close(tmp);
+ }
+ if(linux_nindevs > 0)
+ linux_adcs[0].d_fd = rme9652_adc_devices[0];
+
+ /* configure soundcards */
+
+ rme9652_configure(0, linux_adcs[0].d_fd,srate, 0);
+ rme9652_configure(0, linux_dacs[0].d_fd,srate, 1);
+
+ /* We have to do a read to start the engine. This is
+ necessary because sys_send_dacs waits until the input
+ buffer is filled and only reads on a filled buffer.
+ This is good, because it's a way to make sure that we
+ will not block */
+
+ if (linux_nindevs)
+ {
+ fprintf(stderr,("RME9652: starting read engine ... "));
+
+
+ for (num_devs=0; num_devs < linux_nindevs; num_devs++)
+ read(rme9652_adc_devices[num_devs],
+ buf, RME_SAMPLEWIDTH* RME9652_CH_PER_NATIVE_DEVICE*
+ DACBLKSIZE);
+
+
+ for (num_devs=0; num_devs < linux_noutdevs; num_devs++)
+ write(rme9652_dac_devices[num_devs],
+ buf, RME_SAMPLEWIDTH* RME9652_CH_PER_NATIVE_DEVICE*
+ DACBLKSIZE);
+
+ if(linux_noutdevs)
+ ioctl(rme9652_dac_devices[0],SNDCTL_DSP_SYNC);
+
+ fprintf(stderr,"done\n");
+ }
+
+ linux_setsr(srate);
+ linux_setch(linux_nindevs, linux_noutdevs);
+
+ num_of_rme9652_dac = linux_noutdevs;
+ num_of_rme9652_adc = linux_nindevs;
+
+ if(linux_noutdevs)linux_noutdevs=1;
+ if(linux_nindevs)linux_nindevs=1;
+
+ /* trick RME9652 behaves as one device fromread write pointers */
+ return (0);
+}
+
+void rme9652_close_audio( void)
+{
+ int i;
+ for (i=0;i<num_of_rme9652_dac;i++)
+ close(rme9652_dac_devices[i]);
+
+ for (i=0;i<num_of_rme9652_adc;i++)
+ close(rme9652_adc_devices[i]);
+}
+
+
+/* query audio devices for "available" data size. */
+/* not needed because oss_calcspace does the same */
+static int rme9652_calcspace(void)
+{
+ audio_buf_info ainfo;
+
+
+ /* one for all */
+
+ if (ioctl(linux_dacs[0].d_fd, SOUND_PCM_GETOSPACE,&ainfo) < 0)
+ fprintf(stderr,
+ "RME9652: calc ioctl SOUND_PCM_GETOSPACE on output device fd %d failed\n",
+ linux_dacs[0].d_fd);
+ linux_dacs[0].d_space = ainfo.bytes;
+
+ if (ioctl(linux_adcs[0].d_fd, SOUND_PCM_GETISPACE,&ainfo) < 0)
+ fprintf(stderr,
+ "RME9652: calc ioctl SOUND_PCM_GETISPACE on input device fd %d failed\n",
+ rme9652_adc_devices[0]);
+
+ linux_adcs[0].d_space = ainfo.bytes;
+
+ return 1;
+}
+
+/* this call resyncs audio output and input which will cause discontinuities
+in audio output and/or input. */
+
+static void rme9652_doresync( void)
+{
+ if(linux_noutdevs)
+ ioctl(rme9652_dac_devices[0],SNDCTL_DSP_SYNC);
+}
+
+static int mycount =0;
+
+int rme9652_send_dacs(void)
+{
+ float *fp;
+ long fill;
+ int i, j, dev;
+ /* the maximum number of samples we should have in the ADC buffer */
+ t_rme_sample buf[RME9652_CH_PER_NATIVE_DEVICE*DACBLKSIZE], *sp;
+
+ double timeref, timenow;
+
+ mycount++;
+
+ if (!linux_nindevs && !linux_noutdevs) return (0);
+
+ rme9652_calcspace();
+
+ /* do output */
+
+ timeref = sys_getrealtime();
+
+ if(linux_noutdevs){
+
+ if (linux_dacs[0].d_dropcount)
+ linux_dacs[0].d_dropcount--;
+ else{
+ /* fprintf(stderr,"output %d\n", linux_outchannels);*/
+
+ for(j=0;j<linux_outchannels;j++){
+
+ t_rme_sample *a,*b,*c,*d;
+ float *fp1,*fp2,*fp3,*fp4;
+
+ fp1 = sys_soundout + j*DACBLKSIZE-4;
+ fp2 = fp1 + 1;
+ fp3 = fp1 + 2;
+ fp4 = fp1 + 3;
+ a = buf-4;
+ b=a+1;
+ c=a+2;
+ d=a+3;
+
+ for (i = DACBLKSIZE>>2;i--;)
+ {
+ float s1 = *(fp1+=4) * INT32_MAX;
+ float s2 = *(fp2+=4) * INT32_MAX;
+ float s3 = *(fp3+=4) * INT32_MAX;
+ float s4 = *(fp4+=4) * INT32_MAX;
+
+ *(a+=4) = CLIP32(s1);
+ *(b+=4) = CLIP32(s2);
+ *(c+=4) = CLIP32(s3);
+ *(d+=4) = CLIP32(s4);
+ }
+
+ linux_dacs_write(rme9652_dac_devices[j],buf,RME_BYTESPERCHAN);
+ }
+ }
+
+ if ((timenow = sys_getrealtime()) - timeref > 0.02)
+ sys_log_error(ERR_DACSLEPT);
+ timeref = timenow;
+ }
+
+ memset(sys_soundout, 0,
+ linux_outchannels * (sizeof(float) * DACBLKSIZE));
+
+ /* do input */
+
+ if(linux_nindevs) {
+
+ for(j=0;j<linux_inchannels;j++){
+
+ linux_adcs_read(rme9652_adc_devices[j], buf, RME_BYTESPERCHAN);
+
+ if ((timenow = sys_getrealtime()) - timeref > 0.02)
+ sys_log_error(ERR_ADCSLEPT);
+ timeref = timenow;
+ {
+ t_rme_sample *a,*b,*c,*d;
+ float *fp1,*fp2,*fp3,*fp4;
+
+ fp1 = sys_soundin + j*DACBLKSIZE-4;
+ fp2 = fp1 + 1;
+ fp3 = fp1 + 2;
+ fp4 = fp1 + 3;
+ a = buf-4;
+ b=a+1;
+ c=a+2;
+ d=a+3;
+
+ for (i = (DACBLKSIZE>>2);i--;)
+ {
+ *(fp1+=4) = *(a+=4) * (float)(1./INT32_MAX);
+ *(fp2+=4) = *(b+=4) * (float)(1./INT32_MAX);
+ *(fp3+=4) = *(c+=4) * (float)(1./INT32_MAX);
+ *(fp4+=4) = *(d+=4) * (float)(1./INT32_MAX);
+ }
+ }
+ }
+ }
+ /* fprintf(stderr,"ready \n");*/
+
+ return (1);
+}
+
+#endif /* RME_HAMMERFALL */
diff --git a/pd/src/s_loader.c b/pd/src/s_loader.c
new file mode 100644
index 00000000..4c0ef972
--- /dev/null
+++ b/pd/src/s_loader.c
@@ -0,0 +1,142 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#ifdef DL_OPEN
+#include <dlfcn.h>
+#endif
+#ifdef UNIX
+#include <stdlib.h>
+#include <unistd.h>
+#endif
+#ifdef NT
+#include <io.h>
+#include <windows.h>
+#endif
+#ifdef MACOSX
+#include <mach-o/dyld.h>
+#endif
+#include <string.h>
+#include "m_imp.h"
+#include <stdio.h>
+
+typedef void (*t_xxx)(void);
+
+static char sys_dllextent[] =
+#ifdef __FreeBSD__
+ ".pd_freebsd";
+#endif
+#ifdef IRIX
+#ifdef N32
+ ".pd_irix6";
+#else
+ ".pd_irix5";
+#endif
+#endif
+#ifdef __linux__
+ ".pd_linux";
+#endif
+#ifdef MACOSX
+ ".pd_darwin";
+#endif
+#ifdef NT
+ ".dll";
+#endif
+
+
+int sys_load_lib(char *dirname, char *classname)
+{
+ char symname[MAXPDSTRING], filename[MAXPDSTRING], dirbuf[MAXPDSTRING],
+ *nameptr, *lastdot;
+ void *dlobj;
+ t_xxx makeout;
+ int fd;
+#ifdef NT
+ HINSTANCE ntdll;
+#endif
+#if 0
+ fprintf(stderr, "lib %s %s\n", dirname, classname);
+#endif
+ if ((fd = open_via_path(dirname, classname, sys_dllextent,
+ dirbuf, &nameptr, MAXPDSTRING, 1)) < 0)
+ {
+ return (0);
+ }
+ else
+ {
+ close(fd);
+ /* refabricate the pathname */
+ strcpy(filename, dirbuf);
+ strcat(filename, "/");
+ strcat(filename, nameptr);
+ /* extract the setup function name */
+ if (lastdot = strrchr(nameptr, '.'))
+ *lastdot = 0;
+
+#ifdef MACOSX
+ strcpy(symname, "_");
+ strcat(symname, nameptr);
+#else
+ strcpy(symname, nameptr);
+#endif
+ /* if the last character is a tilde, replace with "_tilde" */
+ if (symname[strlen(symname) - 1] == '~')
+ strcpy(symname + (strlen(symname) - 1), "_tilde");
+ /* and append _setup to form the C setup function name */
+ strcat(symname, "_setup");
+#ifdef DL_OPEN
+ dlobj = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
+ if (!dlobj)
+ {
+ post("%s: %s", filename, dlerror());
+ return (0);
+ }
+ makeout = (t_xxx)dlsym(dlobj, symname);
+#endif
+#ifdef NT
+ sys_bashfilename(filename, filename);
+ ntdll = LoadLibrary(filename);
+ if (!ntdll)
+ {
+ post("%s: couldn't load", filename);
+ return (0);
+ }
+ makeout = (t_xxx)GetProcAddress(ntdll, symname);
+#endif
+#ifdef MACOSX
+ {
+ NSObjectFileImage image;
+ void *ret;
+ NSSymbol s;
+ if ( NSCreateObjectFileImageFromFile( filename, &image) != NSObjectFileImageSuccess )
+ {
+ post("%s: couldn't load", filename);
+ return 0;
+ }
+ ret = NSLinkModule( image, filename, NSLINKMODULE_OPTION_BINDNOW);
+
+ s = NSLookupSymbolInModule(ret, symname);
+
+ if (s)
+ makeout = (t_xxx)NSAddressOfSymbol( s);
+ else makeout = 0;
+ }
+#endif
+ }
+ if (!makeout)
+ {
+ post("load_object: Symbol \"%s\" not found", symname);
+ return 0;
+ }
+ (*makeout)();
+ return (1);
+}
+
+
+
+
+
+
+
+
+
diff --git a/pd/src/s_mac.c b/pd/src/s_mac.c
new file mode 100644
index 00000000..a36f192a
--- /dev/null
+++ b/pd/src/s_mac.c
@@ -0,0 +1,356 @@
+/* Copyright (c) 1997-2001 Guenter Geiger, Miller Puckette, Larry Troxler,
+* Winfried Ritsch, Karl MacMillan, and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* this file implements the sys_ functions profiled in m_imp.h for
+ audio and MIDI I/O on Macintosh OS X.
+
+ Audio simply calls routines in s_portaudio.c, which in turn call the
+ portaudio package. s_portaudio.c is also intended for use from NT.
+
+ MIDI is handled by "portmidi".
+*/
+
+
+#include "m_imp.h"
+#include <stdio.h>
+#ifdef UNIX
+#include <unistd.h>
+#endif
+#ifndef MACOSX
+#include <stdlib.h>
+#else
+#include <stdlib.h>
+#include "portaudio.h"
+#include "portmidi.h"
+#include "porttime.h"
+#include "pminternal.h"
+#endif
+#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <errno.h>
+
+/* Defines */
+#define DEBUG(x) x
+#define DEBUG2(x) {x;}
+
+#define PA_DEFAULTCH 2 /* portaudio specific? */
+#define PA_MAXCH 100
+#define PA_DEFAULTSRATE 44100
+typedef short t_pa_sample;
+#define PA_SAMPLEWIDTH sizeof(t_pa_sample)
+#define PA_BYTESPERCHAN (DACBLKSIZE * PA_SAMPLEWIDTH)
+#define PA_XFERSAMPS (PA_DEFAULTCH*DACBLKSIZE)
+#define PA_XFERSIZE (PA_SAMPLEWIDTH * PA_XFERSAMPS)
+
+static int mac_whichapi = API_PORTAUDIO;
+static int mac_inchannels;
+static int mac_outchannels;
+static int mac_advance_samples; /* scheduler advance in samples */
+static int mac_meters; /* true if we're metering */
+static float mac_inmax; /* max input amplitude */
+static float mac_outmax; /* max output amplitude */
+static int mac_blocksize = 256; /* audio I/O block size in sample frames */
+
+ /* exported variables */
+int sys_schedadvance = 50000; /* scheduler advance in microseconds */
+float sys_dacsr;
+int sys_hipriority = 0;
+t_sample *sys_soundout;
+t_sample *sys_soundin;
+
+static PmStream *mac_midiindevlist[MAXMIDIINDEV];
+static PmStream *mac_midioutdevlist[MAXMIDIOUTDEV];
+static int mac_nmidiindev;
+static int mac_nmidioutdev;
+
+ /* set channels and sample rate. */
+
+static void mac_setchsr(int chin, int chout, int sr)
+{
+ int nblk;
+ int inbytes = chin * (DACBLKSIZE*sizeof(float));
+ int outbytes = chout * (DACBLKSIZE*sizeof(float));
+
+ mac_inchannels = chin;
+ mac_outchannels = chout;
+ sys_dacsr = sr;
+ mac_advance_samples = (sys_schedadvance * sys_dacsr) / (1000000.);
+ if (mac_advance_samples < 3 * DACBLKSIZE)
+ mac_advance_samples = 3 * DACBLKSIZE;
+
+ if (sys_soundin)
+ free(sys_soundin);
+ sys_soundin = (t_float *)malloc(inbytes);
+ memset(sys_soundin, 0, inbytes);
+
+ if (sys_soundout)
+ free(sys_soundout);
+ sys_soundout = (t_float *)malloc(outbytes);
+ memset(sys_soundout, 0, outbytes);
+
+ if (sys_verbose)
+ post("input channels = %d, output channels = %d",
+ mac_inchannels, mac_outchannels);
+}
+
+/* ----------------------- public routines ----------------------- */
+
+void sys_open_audio(int naudioindev, int *audioindev, int nchindev,
+ int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev,
+ int *choutdev, int rate) /* IOhannes */
+{
+ int inchans=
+ (nchindev > 0 ? chindev[0] : (nchindev == 0 ? 0 : PA_DEFAULTCH));
+ int outchans=
+ (nchoutdev > 0 ? choutdev[0] : (nchoutdev == 0 ? 0 : PA_DEFAULTCH));
+ int soundindev = (naudioindev <= 0 ? -1 : (audioindev[0]-1));
+ int soundoutdev = (naudiooutdev <= 0 ? -1 : (audiooutdev[0]-1));
+ int sounddev = (inchans > 0 ? soundindev : soundoutdev);
+ if (naudioindev > 1 || nchindev > 1 || naudiooutdev > 1 || nchoutdev > 1)
+ post("sorry, only one portaudio device can be open at once.\n");
+ /* post("nindev %d, noutdev %d", naudioindev, naudiooutdev);
+ post("soundindev %d, soundoutdev %d", soundindev, soundoutdev); */
+ if (sys_verbose)
+ post("channels in %d, out %d", inchans, outchans);
+ if (rate < 1)
+ rate = PA_DEFAULTSRATE;
+ mac_setchsr(inchans, outchans, rate);
+ pa_open_audio(inchans, outchans, rate, sys_soundin, sys_soundout,
+ mac_blocksize, mac_advance_samples/mac_blocksize,
+ soundindev, soundoutdev);
+}
+
+void sys_close_audio(void)
+{
+ pa_close_audio();
+}
+
+
+int sys_send_dacs(void)
+{
+ if (mac_meters)
+ {
+ int i, n;
+ float maxsamp;
+ for (i = 0, n = mac_inchannels * DACBLKSIZE, maxsamp = mac_inmax;
+ i < n; i++)
+ {
+ float f = sys_soundin[i];
+ if (f > maxsamp) maxsamp = f;
+ else if (-f > maxsamp) maxsamp = -f;
+ }
+ mac_inmax = maxsamp;
+ for (i = 0, n = mac_outchannels * DACBLKSIZE, maxsamp = mac_outmax;
+ i < n; i++)
+ {
+ float f = sys_soundout[i];
+ if (f > maxsamp) maxsamp = f;
+ else if (-f > maxsamp) maxsamp = -f;
+ }
+ mac_outmax = maxsamp;
+ }
+ return pa_send_dacs();
+}
+
+float sys_getsr(void)
+{
+ return (sys_dacsr);
+}
+
+int sys_get_outchannels(void)
+{
+ return (mac_outchannels);
+}
+
+int sys_get_inchannels(void)
+{
+ return (mac_inchannels);
+}
+
+void sys_audiobuf(int n)
+{
+ /* set the size, in milliseconds, of the audio FIFO */
+ if (n < 5) n = 5;
+ else if (n > 5000) n = 5000;
+ sys_schedadvance = n * 1000;
+}
+
+void sys_getmeters(float *inmax, float *outmax)
+{
+ if (inmax)
+ {
+ mac_meters = 1;
+ *inmax = mac_inmax;
+ *outmax = mac_outmax;
+ }
+ else
+ mac_meters = 0;
+ mac_inmax = mac_outmax = 0;
+}
+
+void sys_reportidle(void)
+{
+}
+
+void sys_open_midi(int nmidiin, int *midiinvec,
+ int nmidiout, int *midioutvec)
+{
+ int i = 0;
+ int n = 0;
+ PmError err;
+
+ Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */
+ mac_nmidiindev = 0;
+
+ for (i = 0; i < nmidiin; i++)
+ {
+ err = Pm_OpenInput(&mac_midiindevlist[mac_nmidiindev], midiinvec[i],
+ NULL, 100, NULL, NULL, NULL);
+ if (err)
+ post("could not open midi input device number %d: %s",
+ midiinvec[i], Pm_GetErrorText(err));
+ else
+ {
+ if (sys_verbose)
+ post("Midi Input opened.\n");
+ mac_nmidiindev++;
+ }
+ }
+
+ mac_nmidioutdev = 0;
+ for (i = 0; i < nmidiout; i++)
+ {
+ err = Pm_OpenOutput(&mac_midioutdevlist[mac_nmidioutdev], midioutvec[i],
+ NULL, 0, NULL, NULL, 0);
+ if (err)
+ post("could not open midi output device number %d: %s",
+ midioutvec[i], Pm_GetErrorText(err));
+ else
+ {
+ if (sys_verbose)
+ post("Midi Output opened.\n");
+ mac_nmidioutdev++;
+ }
+ }
+}
+
+void sys_close_midi( void)
+{
+ int i;
+ for (i = 0; i < mac_nmidiindev; i++)
+ Pm_Close(mac_midiindevlist[mac_nmidiindev]);
+ mac_nmidiindev = 0;
+ for (i = 0; i < mac_nmidioutdev; i++)
+ Pm_Close(mac_midioutdevlist[mac_nmidioutdev]);
+ mac_nmidioutdev = 0;
+}
+
+void sys_putmidimess(int portno, int a, int b, int c)
+{
+ PmEvent buffer;
+ fprintf(stderr, "put 1 msg %d %d\n", portno, mac_nmidioutdev);
+ if (portno >= 0 && portno < mac_nmidioutdev)
+ {
+ buffer.message = Pm_Message(a, b, c);
+ buffer.timestamp = 0;
+ fprintf(stderr, "put msg\n");
+ Pm_Write(mac_midioutdevlist[portno], &buffer, 1);
+ }
+}
+
+void sys_putmidibyte(int portno, int byte)
+{
+ post("sorry, no byte-by-byte MIDI output implemented in MAC OSX");
+}
+
+void sys_poll_midi(void)
+{
+ int i, nmess;
+ PmEvent buffer;
+ for (i = 0; i < mac_nmidiindev; i++)
+ {
+ int nmess = Pm_Read(mac_midiindevlist[i], &buffer, 1);
+ if (nmess > 0)
+ {
+ int status = Pm_MessageStatus(buffer.message);
+ int data1 = Pm_MessageData1(buffer.message);
+ int data2 = Pm_MessageData2(buffer.message);
+ int msgtype = (status >> 4) - 8;
+ switch (msgtype)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 6:
+ sys_midibytein(i, status);
+ sys_midibytein(i, data1);
+ sys_midibytein(i, data2);
+ break;
+ case 4:
+ case 5:
+ sys_midibytein(i, status);
+ sys_midibytein(i, data1);
+ break;
+ case 7:
+ sys_midibytein(i, status);
+ break;
+ }
+ }
+ }
+}
+
+void sys_set_priority(int higher)
+{
+ int retval;
+ errno = 0;
+ retval = setpriority(PRIO_PROCESS, 0, (higher? -20 : -19));
+ if (retval == -1 & errno != 0)
+ {
+ perror("setpriority");
+ fprintf(stderr, "priority bost faled.\n");
+ }
+}
+
+void sys_listdevs(void )
+{
+ pa_listdevs();
+}
+
+void sys_setblocksize(int n)
+{
+ if (n < 1)
+ n = 1;
+ if (n != (1 << ilog2(n)))
+ warn("blocksize adjusted to power of 2: %d",
+ (n = (1 << ilog2(n))));
+ mac_blocksize = n;
+}
+
+ /* dummy stuff that shouldn't he here */
+void nt_soundindev(int which)
+{
+}
+
+void nt_soundoutdev(int which)
+{
+}
+
+void nt_midiindev(int which)
+{
+}
+
+void nt_midioutdev(int which)
+{
+}
+
+void nt_noresync(void )
+{
+}
+
+void glob_audio(void *dummy, t_floatarg fadc, t_floatarg fdac)
+{
+}
diff --git a/pd/src/s_main.c b/pd/src/s_main.c
new file mode 100644
index 00000000..3c0f4164
--- /dev/null
+++ b/pd/src/s_main.c
@@ -0,0 +1,803 @@
+/* Copyright (c) 1997-1999 Miller Puckette and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* IOhannes :
+ * hacked the code to add advanced multidevice-support
+ * 1311:forum::für::umläute:2001
+ */
+
+char pd_version[] = "Pd version 0.35\n";
+char pd_compiletime[] = __TIME__;
+char pd_compiledate[] = __DATE__;
+
+#include "m_imp.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#ifdef UNIX
+#include <unistd.h>
+#endif
+#ifdef NT
+#include <io.h>
+#include <windows.h>
+#include <winbase.h>
+#endif
+
+void pd_init(void);
+int sys_argparse(int argc, char **argv);
+void sys_findprogdir(char *progname);
+int sys_startgui(const char *guipath);
+int sys_rcfile(void);
+int m_scheduler(int nodacs);
+void m_schedsetsr( void);
+
+int sys_debuglevel;
+int sys_verbose;
+int sys_noloadbang;
+int sys_nogui;
+char *sys_guicmd;
+t_symbol *sys_libdir;
+static t_symbol *sys_guidir;
+static t_namelist *sys_externlist;
+static t_namelist *sys_openlist;
+static t_namelist *sys_messagelist;
+
+int sys_nmidiout = 1;
+#ifdef NT
+int sys_nmidiin = 0;
+#define DEFMIDIOUTDEV 0 /* For output, in NT, default to "midi_mapper" */
+#else
+int sys_nmidiin = 1;
+#define DEFMIDIOUTDEV 1 /* in other OSes, default to first MIDI device */
+#endif
+#define DEFMIDIINDEV 1 /* for NT this isn't used since sys_nmidiin is 0. */
+int sys_midiindevlist[MAXMIDIINDEV] = {DEFMIDIINDEV};
+int sys_midioutdevlist[MAXMIDIOUTDEV] = {DEFMIDIOUTDEV};
+
+typedef struct _fontinfo
+{
+ int fi_fontsize;
+ int fi_maxwidth;
+ int fi_maxheight;
+ int fi_hostfontsize;
+ int fi_width;
+ int fi_height;
+} t_fontinfo;
+
+ /* these give the nominal point size and maximum height of the characters
+ in the six fonts. */
+
+static t_fontinfo sys_fontlist[] = {
+ {8, 5, 9, 0, 0, 0}, {10, 7, 13, 0, 0, 0}, {12, 9, 16, 0, 0, 0},
+ {16, 10, 20, 0, 0, 0}, {24, 15, 25, 0, 0, 0}, {36, 25, 45, 0, 0, 0}};
+#define NFONT (sizeof(sys_fontlist)/sizeof(*sys_fontlist))
+
+/* here are the actual font size structs on msp's systems:
+NT:
+font 8 5 9 8 5 11
+font 10 7 13 10 6 13
+font 12 9 16 14 8 16
+font 16 10 20 16 10 18
+font 24 15 25 16 10 18
+font 36 25 42 36 22 41
+
+linux:
+font 8 5 9 8 5 9
+font 10 7 13 12 7 13
+font 12 9 16 14 9 15
+font 16 10 20 16 10 19
+font 24 15 25 24 15 24
+font 36 25 42 36 22 41
+*/
+
+static t_fontinfo *sys_findfont(int fontsize)
+{
+ unsigned int i;
+ t_fontinfo *fi;
+ for (i = 0, fi = sys_fontlist; i < (NFONT-1); i++, fi++)
+ if (fontsize < fi[1].fi_fontsize) return (fi);
+ return (sys_fontlist + (NFONT-1));
+}
+
+int sys_nearestfontsize(int fontsize)
+{
+ return (sys_findfont(fontsize)->fi_fontsize);
+}
+
+int sys_hostfontsize(int fontsize)
+{
+ return (sys_findfont(fontsize)->fi_hostfontsize);
+}
+
+int sys_fontwidth(int fontsize)
+{
+ return (sys_findfont(fontsize)->fi_width);
+}
+
+int sys_fontheight(int fontsize)
+{
+ return (sys_findfont(fontsize)->fi_height);
+}
+
+int sys_defaultfont;
+#ifdef NT
+#define DEFAULTFONT 12
+#else
+#define DEFAULTFONT 10
+#endif
+
+
+static int inchannels = -1, outchannels = -1;
+static int srate = 44100;
+
+/* IOhannes { */
+#define MAXSOUNDINDEV 4
+#define MAXSOUNDOUTDEV 4
+
+int sys_nsoundin = -1;
+int sys_nsoundout = -1;
+int sys_soundindevlist[MAXSOUNDINDEV] = {-1};
+int sys_soundoutdevlist[MAXSOUNDOUTDEV] = {-1};
+
+int sys_nchin = -1;
+int sys_nchout = -1;
+int sys_chinlist[MAXSOUNDINDEV] = {-1};
+int sys_choutlist[MAXSOUNDOUTDEV] = {-1};
+/* } IOhannes */
+
+static void openit(const char *dirname, const char *filename)
+{
+ char dirbuf[MAXPDSTRING], *nameptr;
+ int fd = open_via_path(dirname, filename, "", dirbuf, &nameptr,
+ MAXPDSTRING, 0);
+ if (fd)
+ {
+ close (fd);
+ glob_evalfile(0, gensym(nameptr), gensym(dirbuf));
+ }
+ else
+ error("%s: can't open", filename);
+}
+
+#define NHOSTFONT 7
+
+/* this is called from the gui process. The first argument is the cwd, and
+succeeding args give the widths and heights of known fonts. We wait until
+these are known to open files and send messages specified on the command line.
+We ask the GUI to specify the "cwd" in case we don't have a local OS to get it
+from; for instance we could be some kind of RT embedded system. However, to
+really make this make sense we would have to implement
+open(), read(), etc, calls to be served somehow from the GUI too. */
+
+void glob_initfromgui(void *dummy, t_symbol *s, int argc, t_atom *argv)
+{
+ char *cwd = atom_getsymbolarg(0, argc, argv)->s_name;
+ t_namelist *nl;
+ unsigned int i, j;
+ if (argc != 1 + 3 * NHOSTFONT) bug("glob_initfromgui");
+ for (i = 0; i < NFONT; i++)
+ {
+ int wantheight = sys_fontlist[i].fi_maxheight;
+ for (j = 0; j < NHOSTFONT-1; j++)
+ {
+ if (atom_getintarg(3 * (j + 1) + 3, argc, argv) > wantheight)
+ break;
+ }
+ /* j is now the "real" font index for the desired font index i. */
+ sys_fontlist[i].fi_hostfontsize = atom_getintarg(3 * j + 1, argc, argv);
+ sys_fontlist[i].fi_width = atom_getintarg(3 * j + 2, argc, argv);
+ sys_fontlist[i].fi_height = atom_getintarg(3 * j + 3, argc, argv);
+ }
+#if 0
+ for (i = 0; i < 6; i++)
+ fprintf(stderr, "font %d %d %d %d %d\n",
+ sys_fontlist[i].fi_fontsize,
+ sys_fontlist[i].fi_maxheight,
+ sys_fontlist[i].fi_hostfontsize,
+ sys_fontlist[i].fi_width,
+ sys_fontlist[i].fi_height);
+#endif
+ /* load dynamic libraries specified with "-lib" args */
+ for (nl = sys_externlist; nl; nl = nl->nl_next)
+ if (!sys_load_lib(cwd, nl->nl_string))
+ post("%s: can't load library", nl->nl_string);
+ namelist_free(sys_externlist);
+ sys_externlist = 0;
+ /* open patches specifies with "-open" args */
+ for (nl = sys_openlist; nl; nl = nl->nl_next)
+ openit(cwd, nl->nl_string);
+ namelist_free(sys_openlist);
+ sys_openlist = 0;
+ /* send messages specified with "-send" args */
+ for (nl = sys_messagelist; nl; nl = nl->nl_next)
+ {
+ t_binbuf *b = binbuf_new();
+ binbuf_text(b, nl->nl_string, strlen(nl->nl_string));
+ binbuf_eval(b, 0, 0, 0);
+ binbuf_free(b);
+ }
+ namelist_free(sys_messagelist);
+ sys_messagelist = 0;
+}
+
+static void sys_addextrapath(void);
+
+/* this is called from main() in s_entry.c */
+int sys_main(int argc, char **argv)
+{
+#ifdef PD_DEBUG
+ fprintf(stderr, "Pd: COMPILED FOR DEBUGGING\n");
+#endif
+ pd_init(); /* start the message system */
+ sys_findprogdir(argv[0]); /* set sys_progname, guipath */
+#ifdef __linux__
+ sys_rcfile(); /* parse the startup file */
+#endif
+ if (sys_argparse(argc, argv)) return (1); /* parse cmd line */
+ sys_addextrapath();
+ if (sys_verbose) fprintf(stderr, "%s compiled %s %s\n",
+ pd_version, pd_compiletime, pd_compiledate);
+ /* open audio and MIDI */
+ sys_open_midi(sys_nmidiin, sys_midiindevlist,
+ sys_nmidiout, sys_midioutdevlist);
+ sys_open_audio(sys_nsoundin, sys_soundindevlist, sys_nchin, sys_chinlist,
+ sys_nsoundout, sys_soundoutdevlist, sys_nchout, sys_choutlist, srate);
+
+ /* tell scheduler the sample rate */
+ m_schedsetsr();
+ if (sys_startgui(sys_guidir->s_name)) /* start the gui */
+ return(1);
+
+ /* run scheduler until it quits */
+ return (m_scheduler(!(inchannels || outchannels)));
+}
+
+static char *(usagemessage[]) = {
+"usage: pd [-flags] [file]...\n",
+"\naudio configuration flags:\n",
+"-r <n> -- specify sample rate\n",
+#if defined(__linux__) || defined(NT)
+"-inchannels ... -- number of audio in channels (by device, like \"2\" or \"16,8\")\n",
+"-outchannels ... -- number of audio out channels (by device)\n",
+#else
+"-inchannels <n> -- number of audio input channels\n",
+"-outchannels <n> -- number of audio output channels\n",
+#endif
+"-channels ... -- specify both input and output channels\n",
+"-audiobuf <n> -- specify size of audio buffer in msec\n",
+"-blocksize <n> -- specify audio I/O block size in sample frames\n",
+"-sleepgrain <n> -- specify number of milliseconds to sleep when idle\n",
+"-nodac -- suppress audio output\n",
+"-noadc -- suppress audio input\n",
+"-noaudio -- suppress audio input and output (-nosound is synonym) \n",
+"-listdev -- list audio and MIDI devices\n",
+
+#ifdef __linux__
+"-frags <n> -- specify number of audio fragments (defeats audiobuf)\n",
+"-fragsize <n> -- specify log of fragment size ('blocksize' is better...)\n",
+"-stream -- use stream mode audio (e.g., for es1370 audio cards)\n",
+"-32bit -- allow 32 bit OSS audio transfers (for RME Hammerfall)\n",
+#endif
+
+#ifdef ALSA99
+"-alsa -- use ALSA audio drivers\n",
+"-alsadev <n> -- specify ALSA I/O device number (counting from 1)\n",
+#endif
+
+#ifdef ALSA01
+"-alsa -- use ALSA audio drivers\n",
+"-alsadev <n> -- ALSA device # (counting from 1) or name: default hw:0,0\n",
+#endif
+
+#ifdef RME_HAMMERFALL
+"-rme -- use Ritsch's RME 9652 audio driver\n",
+#endif
+"-audioindev ... -- sound in device list; e.g., \"2,1\" for second and first\n",
+"-audiooutdev ... -- sound out device list, same as above \n",
+"-audiodev ... -- specify both -audioindev and -audiooutdev together\n",
+
+#ifdef NT
+"-resync -- resynchronize audio (default if more than 2 channels)\n",
+"-noresync -- never resynchronize audio I/O (default for stereo)\n",
+"-asio -- use ASIO audio driver (and not the 'MMIO' default)\n",
+#endif
+
+"\nMIDI configuration flags:\n",
+"-midiindev ... -- midi in device list; e.g., \"1,3\" for first and third\n",
+"-midioutdev ... -- midi out device list, same format\n",
+"-mididev ... -- specify -midioutdev and -midiindev together\n",
+"-nomidiin -- suppress MIDI input\n",
+"-nomidiout -- suppress MIDI output\n",
+"-nomidi -- suppress MIDI input and output\n",
+
+"\ngeneral flags:\n",
+"-path <path> -- add to file search path\n",
+"-open <file> -- open file(s) on startup\n",
+"-lib <file> -- load object library(s)\n",
+"-font <n> -- specify default font size in points\n",
+"-verbose -- extra printout on startup and when searching for files\n",
+"-d <n> -- specify debug level\n",
+"-noloadbang -- suppress all loadbangs\n",
+"-nogui -- suppress starting the GUI\n",
+"-guicmd \"cmd...\" -- substitute another GUI program (e.g., rsh)\n",
+"-send \"msg...\" -- send a message at startup (after patches are loaded)\n",
+#ifdef UNIX
+"-rt or -realtime -- use real-time priority (needs root privilege)\n",
+#endif
+};
+
+static void sys_parsedevlist(int *np, int *vecp, int max, char *str)
+{
+ int n = 0;
+ while (n < max)
+ {
+ if (!*str) break;
+ else
+ {
+ char *endp;
+ vecp[n] = strtol(str, &endp, 10);
+ if (endp == str)
+ break;
+ n++;
+ if (!endp)
+ break;
+ str = endp + 1;
+ }
+ }
+ *np = n;
+}
+
+static int sys_getmultidevchannels(int n, int *devlist)
+{
+ int sum = 0;
+ if (n<0)return(-1);
+ if (n==0)return 0;
+ while(n--)sum+=*devlist++;
+ return sum;
+}
+
+
+ /* this routine tries to figure out where to find the auxilliary files
+ Pd will need to run. This is either done by looking at the command line
+ invokation for Pd, or if htat fails, by consulting the variable
+ INSTALL_PREFIX. In NT, we don't try to use INSTALL_PREFIX. */
+void sys_findprogdir(char *progname)
+{
+ char sbuf[MAXPDSTRING], sbuf2[MAXPDSTRING], *sp;
+ char *lastslash;
+#ifdef UNIX
+ struct stat statbuf;
+#endif
+
+ /* find out by what string Pd was invoked; put answer in "sbuf". */
+#ifdef NT
+ GetModuleFileName(NULL, sbuf2, sizeof(sbuf2));
+ sbuf2[MAXPDSTRING-1] = 0;
+ sys_unbashfilename(sbuf2, sbuf);
+#endif /* NT */
+#ifdef UNIX
+ strncpy(sbuf, progname, MAXPDSTRING);
+ sbuf[MAXPDSTRING-1] = 0;
+#endif
+ lastslash = strrchr(sbuf, '/');
+ if (lastslash)
+ {
+ /* bash last slash to zero so that sbuf is directory pd was in,
+ e.g., ~/pd/bin */
+ *lastslash = 0;
+ /* go back to the parent from there, e.g., ~/pd */
+ lastslash = strrchr(sbuf, '/');
+ if (lastslash)
+ {
+ strncpy(sbuf2, sbuf, lastslash-sbuf);
+ sbuf2[lastslash-sbuf] = 0;
+ }
+ else strcpy(sbuf2, "..");
+ }
+ else
+ {
+ /* no slashes found. Try INSTALL_PREFIX. */
+#ifdef INSTALL_PREFIX
+ strcpy(sbuf2, INSTALL_PREFIX);
+#else
+ strcpy(sbuf2, ".");
+#endif
+ }
+ /* now we believe sbuf2 holds the parent directory of the directory
+ pd was found in. We now want to infer the "lib" directory and the
+ "gui" directory. In "simple" UNIX installations, the layout is
+ .../bin/pd
+ .../bin/pd-gui
+ .../doc
+ and in "complicated" UNIX installations, it's:
+ .../bin/pd
+ .../lib/pd/bin/pd-gui
+ .../lib/pd/doc
+ To decide which, we stat .../lib/pd; if that exists, we assume it's
+ the complicated layout. In NT, it's the "simple" layout, but
+ the gui program is straight wish80:
+ .../bin/pd
+ .../bin/wish80.exe
+ .../doc
+ */
+#ifdef UNIX
+ strncpy(sbuf, sbuf2, MAXPDSTRING-30);
+ sbuf[MAXPDSTRING-30] = 0;
+ strcat(sbuf, "/lib/pd");
+ if (stat(sbuf, &statbuf) >= 0)
+ {
+ /* complicated layout: lib dir is the one we just stat-ed above */
+ sys_libdir = gensym(sbuf);
+ /* gui lives in .../lib/pd/bin */
+ strncpy(sbuf, sbuf2, MAXPDSTRING-30);
+ sbuf[MAXPDSTRING-30] = 0;
+ strcat(sbuf, "/lib/pd/bin");
+ sys_guidir = gensym(sbuf);
+ }
+ else
+ {
+ /* simple layout: lib dir is the parent */
+ sys_libdir = gensym(sbuf2);
+ /* gui lives in .../bin */
+ strncpy(sbuf, sbuf2, MAXPDSTRING-30);
+ sbuf[MAXPDSTRING-30] = 0;
+ strcat(sbuf, "/bin");
+ sys_guidir = gensym(sbuf);
+ }
+#endif
+#ifdef NT
+ sys_libdir = gensym(sbuf2);
+ sys_guidir = &s_; /* in NT the guipath just depends on the libdir */
+#endif
+}
+
+int sys_argparse(int argc, char **argv)
+{
+ char sbuf[MAXPDSTRING];
+#ifdef NT
+ int resync = -1;
+#endif
+ argc--; argv++;
+ while ((argc > 0) && **argv == '-')
+ {
+ if (!strcmp(*argv, "-r") && argc > 1 &&
+ sscanf(argv[1], "%d", &srate) >= 1)
+ {
+ argc -= 2;
+ argv += 2;
+ }
+ else if (!strcmp(*argv, "-inchannels"))
+ { /* IOhannes */
+ sys_parsedevlist(&sys_nchin, sys_chinlist, MAXSOUNDINDEV, argv[1]);
+ inchannels=sys_getmultidevchannels(sys_nchin, sys_chinlist);
+
+ if (!sys_nchin)
+ goto usage;
+
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-outchannels"))
+ { /* IOhannes */
+ sys_parsedevlist(&sys_nchout, sys_choutlist,MAXSOUNDOUTDEV, argv[1]);
+ outchannels=sys_getmultidevchannels(sys_nchout, sys_choutlist);
+
+ if (!sys_nchout)
+ goto usage;
+
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-channels"))
+ {
+ sys_parsedevlist(&sys_nchin, sys_chinlist,MAXSOUNDINDEV,
+ argv[1]);
+ inchannels = sys_getmultidevchannels(sys_nchin, sys_chinlist);
+ sys_parsedevlist(&sys_nchout, sys_choutlist,MAXSOUNDOUTDEV,
+ argv[1]);
+ outchannels = sys_getmultidevchannels(sys_nchout, sys_choutlist);
+
+ if (!sys_nchout)
+ goto usage;
+
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-soundbuf") || !strcmp(*argv, "-audiobuf"))
+ {
+ sys_audiobuf(atoi(argv[1]));
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-blocksize"))
+ {
+ sys_setblocksize(atoi(argv[1]));
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-sleepgrain"))
+ {
+ sys_sleepgrain = 1000 * atoi(argv[1]);
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-nodac"))
+ { /* IOhannes */
+ sys_nsoundout=0;
+ sys_nchout = 0;
+ outchannels =0;
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-noadc"))
+ { /* IOhannes */
+ sys_nsoundin=0;
+ sys_nchin = 0;
+ inchannels =0;
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-nosound") || !strcmp(*argv, "-noaudio"))
+ { /* IOhannes */
+ sys_nsoundin=sys_nsoundout = 0;
+ sys_nchin = sys_nchout = 0;
+ inchannels =outchannels =0;
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-nomidiin"))
+ {
+ sys_nmidiin = 0;
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-nomidiout"))
+ {
+ sys_nmidiout = 0;
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-nomidi"))
+ {
+ sys_nmidiin = sys_nmidiout = 0;
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-midiindev"))
+ {
+ sys_parsedevlist(&sys_nmidiin, sys_midiindevlist, MAXMIDIINDEV,
+ argv[1]);
+ if (!sys_nmidiin)
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-midioutdev"))
+ {
+ sys_parsedevlist(&sys_nmidiout, sys_midioutdevlist, MAXMIDIOUTDEV,
+ argv[1]);
+ if (!sys_nmidiout)
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-mididev"))
+ {
+ sys_parsedevlist(&sys_nmidiin, sys_midiindevlist, MAXMIDIINDEV,
+ argv[1]);
+ sys_parsedevlist(&sys_nmidiout, sys_midioutdevlist, MAXMIDIOUTDEV,
+ argv[1]);
+ if (!sys_nmidiout)
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-path"))
+ {
+ sys_addpath(argv[1]);
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-open") && argc > 1)
+ {
+ sys_openlist = namelist_append(sys_openlist, argv[1]);
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-lib") && argc > 1)
+ {
+ sys_externlist = namelist_append(sys_externlist, argv[1]);
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-font") && argc > 1)
+ {
+ sys_defaultfont = sys_nearestfontsize(atoi(argv[1]));
+ argc -= 2;
+ argv += 2;
+ }
+ else if (!strcmp(*argv, "-verbose"))
+ {
+ sys_verbose = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-d") && argc > 1 &&
+ sscanf(argv[1], "%d", &sys_debuglevel) >= 1)
+ {
+ argc -= 2;
+ argv += 2;
+ }
+ else if (!strcmp(*argv, "-noloadbang"))
+ {
+ sys_noloadbang = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-nogui"))
+ {
+ sys_nogui = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-guicmd") && argc > 1)
+ {
+ sys_guicmd = argv[1];
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-send") && argc > 1)
+ {
+ sys_messagelist = namelist_append(sys_messagelist, argv[1]);
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-listdev"))
+ {
+ sys_listdevs();
+ argc--; argv++;
+ }
+#ifdef UNIX
+ else if (!strcmp(*argv, "-rt"))
+ {
+ sys_hipriority = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-realtime"))
+ {
+ sys_hipriority = 1;
+ argc--; argv++;
+ }
+#endif
+#ifdef __linux__
+ else if (!strcmp(*argv, "-frags"))
+ {
+ linux_setfrags(atoi(argv[1]));
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-fragsize"))
+ {
+ post("pd: -fragsize argument is obsolete; use '-blocksize %d'\n",
+ (1 << atoi(argv[1])));
+ sys_setblocksize(1 << atoi(argv[1]));
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-stream"))
+ {
+ linux_streammode();
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-32bit"))
+ {
+ linux_32bit();
+ argc--; argv++;
+ }
+#ifdef ALSA01
+ else if (!strcmp(*argv, "-alsa"))
+ {
+ linux_set_sound_api(API_ALSA);
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-alsadev"))
+ {
+ if (argv[1][0] >= '1' && argv[1][0] <= '9')
+ {
+ char buf[80];
+ sprintf(buf, "hw:%d,0", atoi(argv[1]) - 1);
+ linux_alsa_devname(buf);
+ }
+ else linux_alsa_devname(argv[1]);
+ linux_set_sound_api(API_ALSA);
+ argc -= 2; argv +=2;
+ }
+#endif
+#ifdef ALSA99
+ else if (!strcmp(*argv, "-alsa"))
+ {
+ linux_set_sound_api(API_ALSA);
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-alsadev"))
+ {
+ linux_alsa_devno(atoi(argv[1]));
+ linux_set_sound_api(API_ALSA);
+ argc -= 2; argv +=2;
+ }
+#endif
+#ifdef RME_HAMMERFALL
+ else if (!strcmp(*argv, "-rme"))
+ {
+ linux_set_sound_api(API_RME);
+ argc--; argv++;
+ }
+#endif
+#endif
+ else if (!strcmp(*argv, "-soundindev") ||
+ !strcmp(*argv, "-audioindev"))
+ { /* IOhannes */
+ sys_parsedevlist(&sys_nsoundin, sys_soundindevlist,
+ MAXSOUNDINDEV, argv[1]);
+ if (!sys_nsoundin)
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-soundoutdev") ||
+ !strcmp(*argv, "-audiooutdev"))
+ { /* IOhannes */
+ sys_parsedevlist(&sys_nsoundout, sys_soundoutdevlist,
+ MAXSOUNDOUTDEV, argv[1]);
+ if (!sys_nsoundout)
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(*argv, "-sounddev") || !strcmp(*argv, "-audiodev"))
+ {
+ sys_parsedevlist(&sys_nsoundin, sys_soundindevlist,
+ MAXSOUNDINDEV, argv[1]);
+ sys_parsedevlist(&sys_nsoundout, sys_soundoutdevlist,
+ MAXSOUNDOUTDEV, argv[1]);
+ if (!sys_nsoundout)
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+#ifdef NT
+ else if (!strcmp(*argv, "-asio"))
+ {
+ nt_set_sound_api(API_PORTAUDIO);
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-noresync"))
+ {
+ resync = 0;
+ argc--; argv++;
+ }
+ else if (!strcmp(*argv, "-resync"))
+ {
+ resync = 1;
+ argc--; argv++;
+ }
+
+#endif /* NT */
+ else
+ {
+ unsigned int i;
+ usage:
+ for (i = 0; i < sizeof(usagemessage)/sizeof(*usagemessage); i++)
+ fprintf(stderr, "%s", usagemessage[i]);
+ return (1);
+ }
+ }
+#ifdef NT
+ /* resynchronization is on by default for mulltichannel, otherwise
+ off. */
+ if (resync == -1)
+ resync = (inchannels > 2 || outchannels > 2);
+ if (!resync)
+ nt_noresync();
+#endif
+ if (!sys_defaultfont) sys_defaultfont = DEFAULTFONT;
+ for (; argc > 0; argc--, argv++)
+ sys_openlist = namelist_append(sys_openlist, *argv);
+
+
+ return (0);
+}
+
+int sys_getblksize(void)
+{
+ return (DACBLKSIZE);
+}
+
+static void sys_addextrapath(void)
+{
+ char sbuf[MAXPDSTRING];
+ /* add "extra" library to path */
+ strncpy(sbuf, sys_libdir->s_name, MAXPDSTRING-30);
+ sbuf[MAXPDSTRING-30] = 0;
+ strcat(sbuf, "/extra");
+ sys_addpath(sbuf);
+}
+
diff --git a/pd/src/s_nt.c b/pd/src/s_nt.c
new file mode 100644
index 00000000..99346e7c
--- /dev/null
+++ b/pd/src/s_nt.c
@@ -0,0 +1,1586 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* modified 2/98 by Winfried Ritsch to deal with up to 4 synchronized
+"wave" devices, which is how ADAT boards appear to the WAVE API. */
+
+#include "m_imp.h"
+#include <stdio.h>
+
+#include <windows.h>
+
+#include <MMSYSTEM.H>
+
+/* ------------------------- audio -------------------------- */
+
+static void nt_close_midiin(void);
+
+static void postflags(void);
+
+#define NAPORTS 16 /* wini hack for multiple ADDA devices */
+#define NT_MAXCH (2 * NAPORTS)
+#define CHANNELS_PER_DEVICE 2
+#define DEFAULTCHANS 2
+#define DEFAULTSRATE 44100
+#define SAMPSIZE 2
+
+#define REALDACBLKSIZE (4 * DACBLKSIZE) /* larger underlying bufsize */
+
+#define MAXBUFFER 100 /* number of buffers in use at maximum advance */
+#define DEFBUFFER 30 /* default is about 30x6 = 180 msec! */
+static int nt_naudiobuffer = DEFBUFFER;
+static int nt_advance_samples;
+
+float sys_dacsr = DEFAULTSRATE;
+
+static int nt_whichapi = API_MMIO;
+static int nt_meters; /* true if we're metering */
+static float nt_inmax; /* max input amplitude */
+static float nt_outmax; /* max output amplitude */
+static int nt_nwavein, nt_nwaveout; /* number of WAVE devices in and out */
+static int nt_blocksize = 0; /* audio I/O block size in sample frames */
+int sys_schedadvance = 20000; /* scheduler advance in microseconds */
+
+typedef struct _sbuf
+{
+ HANDLE hData;
+ HPSTR lpData; // pointer to waveform data memory
+ HANDLE hWaveHdr;
+ WAVEHDR *lpWaveHdr; // pointer to header structure
+} t_sbuf;
+
+t_sbuf ntsnd_outvec[NAPORTS][MAXBUFFER]; /* circular buffer array */
+HWAVEOUT ntsnd_outdev[NAPORTS]; /* output device */
+static int ntsnd_outphase[NAPORTS]; /* index of next buffer to send */
+
+t_sbuf ntsnd_invec[NAPORTS][MAXBUFFER]; /* circular buffer array */
+HWAVEIN ntsnd_indev[NAPORTS]; /* input device */
+static int ntsnd_inphase[NAPORTS]; /* index of next buffer to read */
+int sys_hipriority = 0;
+
+static void nt_waveinerror(char *s, int err)
+{
+ char t[256];
+ waveInGetErrorText(err, t, 256);
+ fprintf(stderr, s, t);
+}
+
+static void nt_waveouterror(char *s, int err)
+{
+ char t[256];
+ waveOutGetErrorText(err, t, 256);
+ fprintf(stderr, s, t);
+}
+
+static void wave_prep(t_sbuf *bp)
+{
+ WAVEHDR *wh;
+ short *sp;
+ int i;
+ /*
+ * Allocate and lock memory for the waveform data. The memory
+ * for waveform data must be globally allocated with
+ * GMEM_MOVEABLE and GMEM_SHARE flags.
+ */
+
+ if (!(bp->hData =
+ GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
+ (DWORD) (CHANNELS_PER_DEVICE * REALDACBLKSIZE * SAMPSIZE))))
+ printf("alloc 1 failed\n");
+
+ if (!(bp->lpData =
+ (HPSTR) GlobalLock(bp->hData)))
+ printf("lock 1 failed\n");
+
+ /* Allocate and lock memory for the header. */
+
+ if (!(bp->hWaveHdr =
+ GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR))))
+ printf("alloc 2 failed\n");
+
+ if (!(wh = bp->lpWaveHdr =
+ (WAVEHDR *) GlobalLock(bp->hWaveHdr)))
+ printf("lock 2 failed\n");
+
+ for (i = CHANNELS_PER_DEVICE * REALDACBLKSIZE,
+ sp = (short *)bp->lpData; i--; )
+ *sp++ = 0;
+
+ wh->lpData = bp->lpData;
+ wh->dwBufferLength = (CHANNELS_PER_DEVICE * REALDACBLKSIZE * SAMPSIZE);
+ wh->dwFlags = 0;
+ wh->dwLoops = 0L;
+ wh->lpNext = 0;
+ wh->reserved = 0;
+}
+
+static int nt_inalloc[NAPORTS], nt_outalloc[NAPORTS];
+static UINT nt_whichdac = WAVE_MAPPER, nt_whichadc = WAVE_MAPPER;
+
+int mmio_open_audio(void)
+{
+ PCMWAVEFORMAT form;
+ int i;
+ UINT mmresult;
+ int nad, nda;
+
+ if (sys_verbose)
+ post("%d devices in, %d devices out",
+ nt_nwavein, nt_nwaveout);
+
+ form.wf.wFormatTag = WAVE_FORMAT_PCM;
+ form.wf.nChannels = CHANNELS_PER_DEVICE;
+ form.wf.nSamplesPerSec = sys_dacsr;
+ form.wf.nAvgBytesPerSec = sys_dacsr * (CHANNELS_PER_DEVICE * SAMPSIZE);
+ form.wf.nBlockAlign = CHANNELS_PER_DEVICE * SAMPSIZE;
+ form.wBitsPerSample = 8 * SAMPSIZE;
+
+ for (nad=0; nad < nt_nwavein; nad++)
+ {
+ /* Open waveform device(s), sucessively numbered, for input */
+
+ mmresult = waveInOpen(&ntsnd_indev[nad], nt_whichadc+nad,
+ (WAVEFORMATEX *)(&form), 0L, 0L, CALLBACK_NULL);
+
+ if (sys_verbose)
+ printf("opened adc device %d with return %d\n",
+ nt_whichadc+nad,mmresult);
+
+ if (mmresult != MMSYSERR_NOERROR)
+ {
+ nt_waveinerror("waveInOpen: %s\n", mmresult);
+ nt_nwavein = nad; /* nt_nwavein = 0 wini */
+ }
+ else
+ {
+ if (!nt_inalloc[nad])
+ {
+ for (i = 0; i < nt_naudiobuffer; i++)
+ wave_prep(&ntsnd_invec[nad][i]);
+ nt_inalloc[nad] = 1;
+ }
+ for (i = 0; i < nt_naudiobuffer; i++)
+ {
+ mmresult = waveInPrepareHeader(ntsnd_indev[nad],
+ ntsnd_invec[nad][i].lpWaveHdr, sizeof(WAVEHDR));
+ if (mmresult != MMSYSERR_NOERROR)
+ nt_waveinerror("waveinprepareheader: %s\n", mmresult);
+ mmresult = waveInAddBuffer(ntsnd_indev[nad],
+ ntsnd_invec[nad][i].lpWaveHdr, sizeof(WAVEHDR));
+ if (mmresult != MMSYSERR_NOERROR)
+ nt_waveinerror("waveInAddBuffer: %s\n", mmresult);
+ }
+ }
+ }
+ /* quickly start them all together */
+ for(nad=0; nad < nt_nwavein; nad++)
+ waveInStart(ntsnd_indev[nad]);
+
+ for(nda=0; nda < nt_nwaveout; nda++)
+ {
+
+ /* Open a waveform device for output in sucessiv device numbering*/
+ mmresult = waveOutOpen(&ntsnd_outdev[nda], nt_whichdac + nda,
+ (WAVEFORMATEX *)(&form), 0L, 0L, CALLBACK_NULL);
+
+ if (sys_verbose)
+ fprintf(stderr,"opened dac device %d, with return %d\n",
+ nt_whichdac +nda, mmresult);
+
+ if (mmresult != MMSYSERR_NOERROR)
+ {
+ fprintf(stderr,"Wave out open device %d + %d\n",nt_whichdac,nda);
+ nt_waveouterror("waveOutOpen device: %s\n", mmresult);
+ nt_nwaveout = nda;
+ }
+ else
+ {
+ if (!(nt_outalloc[nda]))
+ {
+ for (i = 0; i < nt_naudiobuffer; i++)
+ {
+ wave_prep(&ntsnd_outvec[nda][i]);
+ /* set DONE flag as if we had queued them */
+ ntsnd_outvec[nda][i].lpWaveHdr->dwFlags = WHDR_DONE;
+ }
+ nt_outalloc[nda] = 1;
+ }
+ }
+ }
+
+ return (0);
+}
+
+void mmio_close_audio( void)
+{
+ int errcode;
+ int nda, nad;
+ if (sys_verbose)
+ post("closing audio...");
+
+ for (nda=0; nda < nt_nwaveout; nda++) /*if (nt_nwaveout) wini */
+ {
+ errcode = waveOutReset(ntsnd_outdev[nda]);
+ if (errcode != MMSYSERR_NOERROR)
+ printf("error resetting output %d: %d\n", nda, errcode);
+ errcode = waveOutClose(ntsnd_outdev[nda]);
+ if (errcode != MMSYSERR_NOERROR)
+ printf("error closing output %d: %d\n",nda , errcode);
+ }
+ nt_nwaveout = 0;
+
+ for(nad=0; nad < nt_nwavein;nad++) /* if (nt_nwavein) wini */
+ {
+ errcode = waveInReset(ntsnd_indev[nad]);
+ if (errcode != MMSYSERR_NOERROR)
+ printf("error resetting input: %d\n", errcode);
+ errcode = waveInClose(ntsnd_indev[nad]);
+ if (errcode != MMSYSERR_NOERROR)
+ printf("error closing input: %d\n", errcode);
+ }
+ nt_nwavein = 0;
+}
+
+
+#define ADCJITTER 10 /* We tolerate X buffers of jitter by default */
+#define DACJITTER 10
+
+static int nt_adcjitterbufsallowed = ADCJITTER;
+static int nt_dacjitterbufsallowed = DACJITTER;
+
+ /* ------------- MIDI time stamping from audio clock ------------ */
+
+#ifdef MIDI_TIMESTAMP
+
+static double nt_hibuftime;
+static double initsystime = -1;
+
+ /* call this whenever we reset audio */
+static void nt_resetmidisync(void)
+{
+ initsystime = clock_getsystime();
+ nt_hibuftime = sys_getrealtime();
+}
+
+ /* call this whenever we're idled waiting for audio to be ready.
+ The routine maintains a high and low water point for the difference
+ between real and DAC time. */
+
+static void nt_midisync(void)
+{
+ double jittersec, diff;
+
+ if (initsystime == -1) nt_resetmidisync();
+ jittersec = (nt_dacjitterbufsallowed > nt_adcjitterbufsallowed ?
+ nt_dacjitterbufsallowed : nt_adcjitterbufsallowed)
+ * REALDACBLKSIZE / sys_getsr();
+ diff = sys_getrealtime() - 0.001 * clock_gettimesince(initsystime);
+ if (diff > nt_hibuftime) nt_hibuftime = diff;
+ if (diff < nt_hibuftime - jittersec)
+ {
+ post("jitter excess %d %f", dac, diff);
+ nt_resetmidisync();
+ }
+}
+
+static double nt_midigettimefor(LARGE_INTEGER timestamp)
+{
+ /* this is broken now... used to work when "timestamp" was derived from
+ QueryPerformanceCounter() instead of the gates approved
+ timeGetSystemTime() call in the MIDI callback routine below. */
+ return (nt_tixtotime(timestamp) - nt_hibuftime);
+}
+#endif /* MIDI_TIMESTAMP */
+
+
+static int nt_fill = 0;
+#define WRAPFWD(x) ((x) >= nt_naudiobuffer ? (x) - nt_naudiobuffer: (x))
+#define WRAPBACK(x) ((x) < 0 ? (x) + nt_naudiobuffer: (x))
+#define MAXRESYNC 500
+
+#if 0 /* this is used for debugging */
+static void nt_printaudiostatus(void)
+{
+ int nad, nda;
+ for (nad = 0; nad < nt_nwavein; nad++)
+ {
+ int phase = ntsnd_inphase[nad];
+ int phase2 = phase, phase3 = WRAPFWD(phase2), count, ntrans = 0;
+ int firstphasedone = -1, firstphasebusy = -1;
+ for (count = 0; count < nt_naudiobuffer; count++)
+ {
+ int donethis =
+ (ntsnd_invec[nad][phase2].lpWaveHdr->dwFlags & WHDR_DONE);
+ int donenext =
+ (ntsnd_invec[nad][phase3].lpWaveHdr->dwFlags & WHDR_DONE);
+ if (donethis && !donenext)
+ {
+ if (firstphasebusy >= 0) goto multipleadc;
+ firstphasebusy = count;
+ }
+ if (!donethis && donenext)
+ {
+ if (firstphasedone >= 0) goto multipleadc;
+ firstphasedone = count;
+ }
+ phase2 = phase3;
+ phase3 = WRAPFWD(phase2 + 1);
+ }
+ post("nad %d phase %d busy %d done %d", nad, phase, firstphasebusy,
+ firstphasedone);
+ continue;
+ multipleadc:
+ startpost("nad %d phase %d: oops:", nad, phase);
+ for (count = 0; count < nt_naudiobuffer; count++)
+ {
+ char buf[80];
+ sprintf(buf, " %d",
+ (ntsnd_invec[nad][count].lpWaveHdr->dwFlags & WHDR_DONE));
+ poststring(buf);
+ }
+ endpost();
+ }
+ for (nda = 0; nda < nt_nwaveout; nda++)
+ {
+ int phase = ntsnd_outphase[nad];
+ int phase2 = phase, phase3 = WRAPFWD(phase2), count, ntrans = 0;
+ int firstphasedone = -1, firstphasebusy = -1;
+ for (count = 0; count < nt_naudiobuffer; count++)
+ {
+ int donethis =
+ (ntsnd_outvec[nda][phase2].lpWaveHdr->dwFlags & WHDR_DONE);
+ int donenext =
+ (ntsnd_outvec[nda][phase3].lpWaveHdr->dwFlags & WHDR_DONE);
+ if (donethis && !donenext)
+ {
+ if (firstphasebusy >= 0) goto multipledac;
+ firstphasebusy = count;
+ }
+ if (!donethis && donenext)
+ {
+ if (firstphasedone >= 0) goto multipledac;
+ firstphasedone = count;
+ }
+ phase2 = phase3;
+ phase3 = WRAPFWD(phase2 + 1);
+ }
+ if (firstphasebusy < 0) post("nda %d phase %d all %d",
+ nda, phase, (ntsnd_outvec[nad][0].lpWaveHdr->dwFlags & WHDR_DONE));
+ else post("nda %d phase %d busy %d done %d", nda, phase, firstphasebusy,
+ firstphasedone);
+ continue;
+ multipledac:
+ startpost("nda %d phase %d: oops:", nda, phase);
+ for (count = 0; count < nt_naudiobuffer; count++)
+ {
+ char buf[80];
+ sprintf(buf, " %d",
+ (ntsnd_outvec[nad][count].lpWaveHdr->dwFlags & WHDR_DONE));
+ poststring(buf);
+ }
+ endpost();
+ }
+}
+#endif /* 0 */
+
+/* this is a hack to avoid ever resyncing audio pointers in case for whatever
+reason the sync testing below gives false positives. */
+
+static int nt_resync_cancelled;
+
+void nt_noresync( void)
+{
+ nt_resync_cancelled = 1;
+}
+
+static void nt_resyncaudio(void)
+{
+ UINT mmresult;
+ int nad, nda, count;
+ if (nt_resync_cancelled)
+ return;
+ /* for each open input device, eat all buffers which are marked
+ ready. The next one will thus be "busy". */
+ post("resyncing audio");
+ for (nad = 0; nad < nt_nwavein; nad++)
+ {
+ int phase = ntsnd_inphase[nad];
+ for (count = 0; count < MAXRESYNC; count++)
+ {
+ WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr;
+ if (!(inwavehdr->dwFlags & WHDR_DONE)) break;
+ if (inwavehdr->dwFlags & WHDR_PREPARED)
+ waveInUnprepareHeader(ntsnd_indev[nad],
+ inwavehdr, sizeof(WAVEHDR));
+ inwavehdr->dwFlags = 0L;
+ waveInPrepareHeader(ntsnd_indev[nad], inwavehdr, sizeof(WAVEHDR));
+ mmresult = waveInAddBuffer(ntsnd_indev[nad], inwavehdr,
+ sizeof(WAVEHDR));
+ if (mmresult != MMSYSERR_NOERROR)
+ nt_waveinerror("waveInAddBuffer: %s\n", mmresult);
+ ntsnd_inphase[nad] = phase = WRAPFWD(phase + 1);
+ }
+ if (count == MAXRESYNC) post("resync error 1");
+ }
+ /* Each output buffer which is "ready" is filled with zeros and
+ queued. */
+ for (nda = 0; nda < nt_nwaveout; nda++)
+ {
+ int phase = ntsnd_outphase[nda];
+ for (count = 0; count < MAXRESYNC; count++)
+ {
+ WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr;
+ if (!(outwavehdr->dwFlags & WHDR_DONE)) break;
+ if (outwavehdr->dwFlags & WHDR_PREPARED)
+ waveOutUnprepareHeader(ntsnd_outdev[nda],
+ outwavehdr, sizeof(WAVEHDR));
+ outwavehdr->dwFlags = 0L;
+ memset((char *)(ntsnd_outvec[nda][phase].lpData),
+ 0, (CHANNELS_PER_DEVICE * REALDACBLKSIZE * SAMPSIZE));
+ waveOutPrepareHeader(ntsnd_outdev[nda], outwavehdr,
+ sizeof(WAVEHDR));
+ mmresult = waveOutWrite(ntsnd_outdev[nda], outwavehdr,
+ sizeof(WAVEHDR));
+ if (mmresult != MMSYSERR_NOERROR)
+ nt_waveouterror("waveOutAddBuffer: %s\n", mmresult);
+ ntsnd_outphase[nda] = phase = WRAPFWD(phase + 1);
+ }
+ if (count == MAXRESYNC) post("resync error 2");
+ }
+
+#ifdef MIDI_TIMESTAMP
+ nt_resetmidisync();
+#endif
+
+}
+
+#define LATE 0
+#define RESYNC 1
+#define NOTHING 2
+static int nt_errorcount;
+static int nt_resynccount;
+static double nt_nextreporttime = -1;
+
+void nt_logerror(int which)
+{
+#if 0
+ post("error %d %d", count, which);
+ if (which < NOTHING) nt_errorcount++;
+ if (which == RESYNC) nt_resynccount++;
+ if (sys_getrealtime() > nt_nextreporttime)
+ {
+ post("%d audio I/O error%s", nt_errorcount,
+ (nt_errorcount > 1 ? "s" : ""));
+ if (nt_resynccount) post("DAC/ADC sync error");
+ nt_errorcount = nt_resynccount = 0;
+ nt_nextreporttime = sys_getrealtime() - 5;
+ }
+#endif
+}
+
+/* system buffer with t_sample types for one tick */
+t_sample *sys_soundout;
+t_sample *sys_soundin;
+float sys_dacsr;
+
+int mmio_send_dacs(void)
+{
+ HMMIO hmmio;
+ UINT mmresult;
+ HANDLE hFormat;
+ int i, j;
+ short *sp1, *sp2;
+ float *fp1, *fp2;
+ int nextfill, doxfer = 0;
+ int nda, nad;
+ if (!nt_nwavein && !nt_nwaveout) return (0);
+
+
+ if (nt_meters)
+ {
+ int i, n;
+ float maxsamp;
+ for (i = 0, n = 2 * nt_nwavein * DACBLKSIZE, maxsamp = nt_inmax;
+ i < n; i++)
+ {
+ float f = sys_soundin[i];
+ if (f > maxsamp) maxsamp = f;
+ else if (-f > maxsamp) maxsamp = -f;
+ }
+ nt_inmax = maxsamp;
+ for (i = 0, n = 2 * nt_nwaveout * DACBLKSIZE, maxsamp = nt_outmax;
+ i < n; i++)
+ {
+ float f = sys_soundout[i];
+ if (f > maxsamp) maxsamp = f;
+ else if (-f > maxsamp) maxsamp = -f;
+ }
+ nt_outmax = maxsamp;
+ }
+
+ /* the "fill pointer" nt_fill controls where in the next
+ I/O buffers we will write and/or read. If it's zero, we
+ first check whether the buffers are marked "done". */
+
+ if (!nt_fill)
+ {
+ for (nad = 0; nad < nt_nwavein; nad++)
+ {
+ int phase = ntsnd_inphase[nad];
+ WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr;
+ if (!(inwavehdr->dwFlags & WHDR_DONE)) goto idle;
+ }
+ for (nda = 0; nda < nt_nwaveout; nda++)
+ {
+ int phase = ntsnd_outphase[nda];
+ WAVEHDR *outwavehdr =
+ ntsnd_outvec[nda][phase].lpWaveHdr;
+ if (!(outwavehdr->dwFlags & WHDR_DONE)) goto idle;
+ }
+ for (nad = 0; nad < nt_nwavein; nad++)
+ {
+ int phase = ntsnd_inphase[nad];
+ WAVEHDR *inwavehdr =
+ ntsnd_invec[nad][phase].lpWaveHdr;
+ if (inwavehdr->dwFlags & WHDR_PREPARED)
+ waveInUnprepareHeader(ntsnd_indev[nad],
+ inwavehdr, sizeof(WAVEHDR));
+ }
+ for (nda = 0; nda < nt_nwaveout; nda++)
+ {
+ int phase = ntsnd_outphase[nda];
+ WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr;
+ if (outwavehdr->dwFlags & WHDR_PREPARED)
+ waveOutUnprepareHeader(ntsnd_outdev[nda],
+ outwavehdr, sizeof(WAVEHDR));
+ }
+ }
+
+ /* Convert audio output to fixed-point and put it in the output
+ buffer. */
+ for (nda = 0, fp1 = sys_soundout; nda < nt_nwaveout; nda++)
+ {
+ int phase = ntsnd_outphase[nda];
+
+ for (i = 0, sp1 = (short *)(ntsnd_outvec[nda][phase].lpData) +
+ CHANNELS_PER_DEVICE * nt_fill;
+ i < 2; i++, fp1 += DACBLKSIZE, sp1++)
+ {
+ for (j = 0, fp2 = fp1, sp2 = sp1; j < DACBLKSIZE;
+ j++, fp2++, sp2 += CHANNELS_PER_DEVICE)
+ {
+ int x1 = 32767.f * *fp2;
+ if (x1 > 32767) x1 = 32767;
+ else if (x1 < -32767) x1 = -32767;
+ *sp2 = x1;
+ }
+ }
+ }
+ memset(sys_soundout, 0,
+ (DACBLKSIZE*sizeof(t_sample)*CHANNELS_PER_DEVICE)*nt_nwaveout);
+
+ /* vice versa for the input buffer */
+
+ for (nad = 0, fp1 = sys_soundin; nad < nt_nwavein; nad++)
+ {
+ int phase = ntsnd_inphase[nad];
+
+ for (i = 0, sp1 = (short *)(ntsnd_invec[nad][phase].lpData) +
+ CHANNELS_PER_DEVICE * nt_fill;
+ i < 2; i++, fp1 += DACBLKSIZE, sp1++)
+ {
+ for (j = 0, fp2 = fp1, sp2 = sp1; j < DACBLKSIZE;
+ j++, fp2++, sp2 += CHANNELS_PER_DEVICE)
+ {
+ *fp2 = ((float)(1./32767.)) * (float)(*sp2);
+ }
+ }
+ }
+
+ nt_fill = nt_fill + DACBLKSIZE;
+ if (nt_fill == REALDACBLKSIZE)
+ {
+ nt_fill = 0;
+
+ for (nad = 0; nad < nt_nwavein; nad++)
+ {
+ int phase = ntsnd_inphase[nad];
+ HWAVEIN device = ntsnd_indev[nad];
+ WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr;
+ waveInPrepareHeader(device, inwavehdr, sizeof(WAVEHDR));
+ mmresult = waveInAddBuffer(device, inwavehdr, sizeof(WAVEHDR));
+ if (mmresult != MMSYSERR_NOERROR)
+ nt_waveinerror("waveInAddBuffer: %s\n", mmresult);
+ ntsnd_inphase[nad] = WRAPFWD(phase + 1);
+ }
+ for (nda = 0; nda < nt_nwaveout; nda++)
+ {
+ int phase = ntsnd_outphase[nda];
+ HWAVEOUT device = ntsnd_outdev[nda];
+ WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr;
+ waveOutPrepareHeader(device, outwavehdr, sizeof(WAVEHDR));
+ mmresult = waveOutWrite(device, outwavehdr, sizeof(WAVEHDR));
+ if (mmresult != MMSYSERR_NOERROR)
+ nt_waveouterror("waveOutWrite: %s\n", mmresult);
+ ntsnd_outphase[nda] = WRAPFWD(phase + 1);
+ }
+
+ /* check for DAC underflow or ADC overflow. */
+ for (nad = 0; nad < nt_nwavein; nad++)
+ {
+ int phase = WRAPBACK(ntsnd_inphase[nad] - 2);
+ WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr;
+ if (inwavehdr->dwFlags & WHDR_DONE) goto late;
+ }
+ for (nda = 0; nda < nt_nwaveout; nda++)
+ {
+ int phase = WRAPBACK(ntsnd_outphase[nda] - 2);
+ WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr;
+ if (outwavehdr->dwFlags & WHDR_DONE) goto late;
+ }
+ }
+ return (1);
+
+late:
+
+ nt_logerror(LATE);
+ nt_resyncaudio();
+ return (1);
+
+idle:
+
+ /* If more than nt_adcjitterbufsallowed ADC buffers are ready
+ on any input device, resynchronize */
+
+ for (nad = 0; nad < nt_nwavein; nad++)
+ {
+ int phase = ntsnd_inphase[nad];
+ WAVEHDR *inwavehdr =
+ ntsnd_invec[nad]
+ [WRAPFWD(phase + nt_adcjitterbufsallowed)].lpWaveHdr;
+ if (inwavehdr->dwFlags & WHDR_DONE)
+ {
+ nt_resyncaudio();
+ return (0);
+ }
+ }
+
+ /* test dac sync the same way */
+ for (nda = 0; nda < nt_nwaveout; nda++)
+ {
+ int phase = ntsnd_outphase[nda];
+ WAVEHDR *outwavehdr =
+ ntsnd_outvec[nda]
+ [WRAPFWD(phase + nt_dacjitterbufsallowed)].lpWaveHdr;
+ if (outwavehdr->dwFlags & WHDR_DONE)
+ {
+ nt_resyncaudio();
+ return (0);
+ }
+ }
+#ifdef MIDI_TIMESTAMP
+ nt_midisync();
+#endif
+ return (0);
+}
+
+
+static void nt_setchsr(int inchannels, int outchannels, int sr)
+{
+ int inbytes = inchannels * (DACBLKSIZE*sizeof(float));
+ int outbytes = outchannels * (DACBLKSIZE*sizeof(float));
+
+ if (nt_nwavein)
+ free(sys_soundin);
+ if (nt_nwaveout)
+ free(sys_soundout);
+
+ nt_nwavein = inchannels/CHANNELS_PER_DEVICE;
+ nt_nwaveout = outchannels/CHANNELS_PER_DEVICE;
+ sys_dacsr = sr;
+
+ sys_soundin = (t_float *)malloc(inbytes);
+ memset(sys_soundin, 0, inbytes);
+
+ sys_soundout = (t_float *)malloc(outbytes);
+ memset(sys_soundout, 0, outbytes);
+
+ nt_advance_samples = (sys_schedadvance * sys_dacsr) / (1000000.);
+ if (nt_advance_samples < 3 * DACBLKSIZE)
+ nt_advance_samples = 3 * DACBLKSIZE;
+}
+
+/* ------------------------- MIDI output -------------------------- */
+static void nt_midiouterror(char *s, int err)
+{
+ char t[256];
+ midiOutGetErrorText(err, t, 256);
+ fprintf(stderr, s, t);
+}
+
+static HMIDIOUT hMidiOut[MAXMIDIOUTDEV]; /* output device */
+static int nt_nmidiout; /* number of devices */
+
+static void nt_open_midiout(int nmidiout, int *midioutvec)
+{
+ UINT result, wRtn;
+ int i;
+ int dev;
+ MIDIOUTCAPS midioutcaps;
+ if (nmidiout > MAXMIDIOUTDEV)
+ nmidiout = MAXMIDIOUTDEV;
+
+ dev = 0;
+
+ for (i = 0; i < nmidiout; i++)
+ {
+ MIDIOUTCAPS mocap;
+ result = midiOutOpen(&hMidiOut[dev], midioutvec[i]-1, 0, 0,
+ CALLBACK_NULL);
+ wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap,
+ sizeof(mocap));
+ if (result != MMSYSERR_NOERROR)
+ {
+ fprintf(stderr,"midiOutOpen: %s\n",midioutcaps.szPname);
+ nt_midiouterror("midiOutOpen: %s\n", result);
+ }
+ else
+ {
+ if (sys_verbose)
+ fprintf(stderr,"midiOutOpen: Open %s as Port %d\n",
+ midioutcaps.szPname, dev);
+ dev++;
+ }
+ }
+ nt_nmidiout = dev;
+}
+
+void sys_putmidimess(int portno, int a, int b, int c)
+{
+ DWORD foo;
+ MMRESULT res;
+ if (portno >= 0 && portno < nt_nmidiout)
+ {
+ foo = (a & 0xff) | ((b & 0xff) << 8) | ((c & 0xff) << 16);
+ res = midiOutShortMsg(hMidiOut[portno], foo);
+ if (res != MMSYSERR_NOERROR)
+ post("MIDI out error %d", res);
+ }
+}
+
+void sys_putmidibyte(int portno, int byte)
+{
+ MMRESULT res;
+ if (portno >= 0 && portno < nt_nmidiout)
+ {
+ res = midiOutShortMsg(hMidiOut[portno], byte);
+ if (res != MMSYSERR_NOERROR)
+ post("MIDI out error %d", res);
+ }
+}
+
+static void nt_close_midiout(void)
+{
+ int i;
+ for (i = 0; i < nt_nmidiout; i++)
+ {
+ midiOutReset(hMidiOut[i]);
+ midiOutClose(hMidiOut[i]);
+ }
+ nt_nmidiout = 0;
+}
+
+/* -------------------------- MIDI input ---------------------------- */
+
+#define INPUT_BUFFER_SIZE 1000 // size of input buffer in events
+
+static void nt_midiinerror(char *s, int err)
+{
+ char t[256];
+ midiInGetErrorText(err, t, 256);
+ fprintf(stderr, s, t);
+}
+
+
+/* Structure to represent a single MIDI event.
+ */
+
+#define EVNT_F_ERROR 0x00000001L
+
+typedef struct event_tag
+{
+ DWORD fdwEvent;
+ DWORD dwDevice;
+ LARGE_INTEGER timestamp;
+ DWORD data;
+} EVENT;
+typedef EVENT FAR *LPEVENT;
+
+/* Structure to manage the circular input buffer.
+ */
+typedef struct circularBuffer_tag
+{
+ HANDLE hSelf; /* handle to this structure */
+ HANDLE hBuffer; /* buffer handle */
+ WORD wError; /* error flags */
+ DWORD dwSize; /* buffer size (in EVENTS) */
+ DWORD dwCount; /* byte count (in EVENTS) */
+ LPEVENT lpStart; /* ptr to start of buffer */
+ LPEVENT lpEnd; /* ptr to end of buffer (last byte + 1) */
+ LPEVENT lpHead; /* ptr to head (next location to fill) */
+ LPEVENT lpTail; /* ptr to tail (next location to empty) */
+} CIRCULARBUFFER;
+typedef CIRCULARBUFFER FAR *LPCIRCULARBUFFER;
+
+
+/* Structure to pass instance data from the application
+ to the low-level callback function.
+ */
+typedef struct callbackInstance_tag
+{
+ HANDLE hSelf;
+ DWORD dwDevice;
+ LPCIRCULARBUFFER lpBuf;
+} CALLBACKINSTANCEDATA;
+typedef CALLBACKINSTANCEDATA FAR *LPCALLBACKINSTANCEDATA;
+
+/* Function prototypes
+ */
+LPCALLBACKINSTANCEDATA FAR PASCAL AllocCallbackInstanceData(void);
+void FAR PASCAL FreeCallbackInstanceData(LPCALLBACKINSTANCEDATA lpBuf);
+
+LPCIRCULARBUFFER AllocCircularBuffer(DWORD dwSize);
+void FreeCircularBuffer(LPCIRCULARBUFFER lpBuf);
+WORD FAR PASCAL GetEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent);
+
+// Callback instance data pointers
+LPCALLBACKINSTANCEDATA lpCallbackInstanceData[MAXMIDIINDEV];
+
+UINT wNumDevices = 0; // Number of MIDI input devices opened
+BOOL bRecordingEnabled = 1; // Enable/disable recording flag
+int nNumBufferLines = 0; // Number of lines in display buffer
+RECT rectScrollClip; // Clipping rectangle for scrolling
+
+LPCIRCULARBUFFER lpInputBuffer; // Input buffer structure
+EVENT incomingEvent; // Incoming MIDI event structure
+
+MIDIINCAPS midiInCaps[MAXMIDIINDEV]; // Device capabilities structures
+HMIDIIN hMidiIn[MAXMIDIINDEV]; // MIDI input device handles
+
+
+/* AllocCallbackInstanceData - Allocates a CALLBACKINSTANCEDATA
+ * structure. This structure is used to pass information to the
+ * low-level callback function, each time it receives a message.
+ *
+ * Because this structure is accessed by the low-level callback
+ * function, it must be allocated using GlobalAlloc() with the
+ * GMEM_SHARE and GMEM_MOVEABLE flags and page-locked with
+ * GlobalPageLock().
+ *
+ * Params: void
+ *
+ * Return: A pointer to the allocated CALLBACKINSTANCE data structure.
+ */
+LPCALLBACKINSTANCEDATA FAR PASCAL AllocCallbackInstanceData(void)
+{
+ HANDLE hMem;
+ LPCALLBACKINSTANCEDATA lpBuf;
+
+ /* Allocate and lock global memory.
+ */
+ hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE,
+ (DWORD)sizeof(CALLBACKINSTANCEDATA));
+ if(hMem == NULL)
+ return NULL;
+
+ lpBuf = (LPCALLBACKINSTANCEDATA)GlobalLock(hMem);
+ if(lpBuf == NULL){
+ GlobalFree(hMem);
+ return NULL;
+ }
+
+ /* Page lock the memory.
+ */
+ //GlobalPageLock((HGLOBAL)HIWORD(lpBuf));
+
+ /* Save the handle.
+ */
+ lpBuf->hSelf = hMem;
+
+ return lpBuf;
+}
+
+/* FreeCallbackInstanceData - Frees the given CALLBACKINSTANCEDATA structure.
+ *
+ * Params: lpBuf - Points to the CALLBACKINSTANCEDATA structure to be freed.
+ *
+ * Return: void
+ */
+void FAR PASCAL FreeCallbackInstanceData(LPCALLBACKINSTANCEDATA lpBuf)
+{
+ HANDLE hMem;
+
+ /* Save the handle until we're through here.
+ */
+ hMem = lpBuf->hSelf;
+
+ /* Free the structure.
+ */
+ //GlobalPageUnlock((HGLOBAL)HIWORD(lpBuf));
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+}
+
+
+/*
+ * AllocCircularBuffer - Allocates memory for a CIRCULARBUFFER structure
+ * and a buffer of the specified size. Each memory block is allocated
+ * with GlobalAlloc() using GMEM_SHARE and GMEM_MOVEABLE flags, locked
+ * with GlobalLock(), and page-locked with GlobalPageLock().
+ *
+ * Params: dwSize - The size of the buffer, in events.
+ *
+ * Return: A pointer to a CIRCULARBUFFER structure identifying the
+ * allocated display buffer. NULL if the buffer could not be allocated.
+ */
+
+
+LPCIRCULARBUFFER AllocCircularBuffer(DWORD dwSize)
+{
+ HANDLE hMem;
+ LPCIRCULARBUFFER lpBuf;
+ LPEVENT lpMem;
+
+ /* Allocate and lock a CIRCULARBUFFER structure.
+ */
+ hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE,
+ (DWORD)sizeof(CIRCULARBUFFER));
+ if(hMem == NULL)
+ return NULL;
+
+ lpBuf = (LPCIRCULARBUFFER)GlobalLock(hMem);
+ if(lpBuf == NULL)
+ {
+ GlobalFree(hMem);
+ return NULL;
+ }
+
+ /* Page lock the memory. Global memory blocks accessed by
+ * low-level callback functions must be page locked.
+ */
+#ifndef _WIN32
+ GlobalSmartPageLock((HGLOBAL)HIWORD(lpBuf));
+#endif
+
+ /* Save the memory handle.
+ */
+ lpBuf->hSelf = hMem;
+
+ /* Allocate and lock memory for the actual buffer.
+ */
+ hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, dwSize * sizeof(EVENT));
+ if(hMem == NULL)
+ {
+#ifndef _WIN32
+ GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf));
+#endif
+ GlobalUnlock(lpBuf->hSelf);
+ GlobalFree(lpBuf->hSelf);
+ return NULL;
+ }
+
+ lpMem = (LPEVENT)GlobalLock(hMem);
+ if(lpMem == NULL)
+ {
+ GlobalFree(hMem);
+#ifndef _WIN32
+ GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf));
+#endif
+ GlobalUnlock(lpBuf->hSelf);
+ GlobalFree(lpBuf->hSelf);
+ return NULL;
+ }
+
+ /* Page lock the memory. Global memory blocks accessed by
+ * low-level callback functions must be page locked.
+ */
+#ifndef _WIN32
+ GlobalSmartPageLock((HGLOBAL)HIWORD(lpMem));
+#endif
+
+ /* Set up the CIRCULARBUFFER structure.
+ */
+ lpBuf->hBuffer = hMem;
+ lpBuf->wError = 0;
+ lpBuf->dwSize = dwSize;
+ lpBuf->dwCount = 0L;
+ lpBuf->lpStart = lpMem;
+ lpBuf->lpEnd = lpMem + dwSize;
+ lpBuf->lpTail = lpMem;
+ lpBuf->lpHead = lpMem;
+
+ return lpBuf;
+}
+
+/* FreeCircularBuffer - Frees the memory for the given CIRCULARBUFFER
+ * structure and the memory for the buffer it references.
+ *
+ * Params: lpBuf - Points to the CIRCULARBUFFER to be freed.
+ *
+ * Return: void
+ */
+void FreeCircularBuffer(LPCIRCULARBUFFER lpBuf)
+{
+ HANDLE hMem;
+
+ /* Free the buffer itself.
+ */
+#ifndef _WIN32
+ GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf->lpStart));
+#endif
+ GlobalUnlock(lpBuf->hBuffer);
+ GlobalFree(lpBuf->hBuffer);
+
+ /* Free the CIRCULARBUFFER structure.
+ */
+ hMem = lpBuf->hSelf;
+#ifndef _WIN32
+ GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf));
+#endif
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+}
+
+/* GetEvent - Gets a MIDI event from the circular input buffer. Events
+ * are removed from the buffer. The corresponding PutEvent() function
+ * is called by the low-level callback function, so it must reside in
+ * the callback DLL. PutEvent() is defined in the CALLBACK.C module.
+ *
+ * Params: lpBuf - Points to the circular buffer.
+ * lpEvent - Points to an EVENT structure that is filled with the
+ * retrieved event.
+ *
+ * Return: Returns non-zero if successful, zero if there are no
+ * events to get.
+ */
+WORD FAR PASCAL GetEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent)
+{
+ /* If no event available, return */
+ if (!wNumDevices || lpBuf->dwCount <= 0) return (0);
+
+ /* Get the event.
+ */
+ *lpEvent = *lpBuf->lpTail;
+
+ /* Decrement the byte count, bump the tail pointer.
+ */
+ --lpBuf->dwCount;
+ ++lpBuf->lpTail;
+
+ /* Wrap the tail pointer, if necessary.
+ */
+ if(lpBuf->lpTail >= lpBuf->lpEnd)
+ lpBuf->lpTail = lpBuf->lpStart;
+
+ return 1;
+}
+
+/* PutEvent - Puts an EVENT in a CIRCULARBUFFER. If the buffer is full,
+ * it sets the wError element of the CIRCULARBUFFER structure
+ * to be non-zero.
+ *
+ * Params: lpBuf - Points to the CIRCULARBUFFER.
+ * lpEvent - Points to the EVENT.
+ *
+ * Return: void
+*/
+
+void FAR PASCAL PutEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent)
+{
+ /* If the buffer is full, set an error and return.
+ */
+ if(lpBuf->dwCount >= lpBuf->dwSize){
+ lpBuf->wError = 1;
+ return;
+ }
+
+ /* Put the event in the buffer, bump the head pointer and the byte count.
+ */
+ *lpBuf->lpHead = *lpEvent;
+
+ ++lpBuf->lpHead;
+ ++lpBuf->dwCount;
+
+ /* Wrap the head pointer, if necessary.
+ */
+ if(lpBuf->lpHead >= lpBuf->lpEnd)
+ lpBuf->lpHead = lpBuf->lpStart;
+}
+
+/* midiInputHandler - Low-level callback function to handle MIDI input.
+ * Installed by midiInOpen(). The input handler takes incoming
+ * MIDI events and places them in the circular input buffer. It then
+ * notifies the application by posting a MM_MIDIINPUT message.
+ *
+ * This function is accessed at interrupt time, so it should be as
+ * fast and efficient as possible. You can't make any
+ * Windows calls here, except PostMessage(). The only Multimedia
+ * Windows call you can make are timeGetSystemTime(), midiOutShortMsg().
+ *
+ *
+ * Param: hMidiIn - Handle for the associated input device.
+ * wMsg - One of the MIM_***** messages.
+ * dwInstance - Points to CALLBACKINSTANCEDATA structure.
+ * dwParam1 - MIDI data.
+ * dwParam2 - Timestamp (in milliseconds)
+ *
+ * Return: void
+ */
+void FAR PASCAL midiInputHandler(
+HMIDIIN hMidiIn,
+WORD wMsg,
+DWORD dwInstance,
+DWORD dwParam1,
+DWORD dwParam2)
+{
+ EVENT event;
+
+ switch(wMsg)
+ {
+ case MIM_OPEN:
+ break;
+
+ /* The only error possible is invalid MIDI data, so just pass
+ * the invalid data on so we'll see it.
+ */
+ case MIM_ERROR:
+ case MIM_DATA:
+ event.fdwEvent = (wMsg == MIM_ERROR) ? EVNT_F_ERROR : 0;
+ event.dwDevice = ((LPCALLBACKINSTANCEDATA)dwInstance)->dwDevice;
+ event.data = dwParam1;
+#ifdef MIDI_TIMESTAMP
+ event.timestamp = timeGetSystemTime();
+#endif
+ /* Put the MIDI event in the circular input buffer.
+ */
+
+ PutEvent(((LPCALLBACKINSTANCEDATA)dwInstance)->lpBuf,
+ (LPEVENT) &event);
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+void nt_open_midiin(int nmidiin, int *midiinvec)
+{
+ UINT wRtn;
+ char szErrorText[256];
+ unsigned int i;
+ unsigned int ndev = 0;
+ /* Allocate a circular buffer for low-level MIDI input. This buffer
+ * is filled by the low-level callback function and emptied by the
+ * application.
+ */
+ lpInputBuffer = AllocCircularBuffer((DWORD)(INPUT_BUFFER_SIZE));
+ if (lpInputBuffer == NULL)
+ {
+ printf("Not enough memory available for input buffer.\n");
+ return;
+ }
+
+ /* Open all MIDI input devices after allocating and setting up
+ * instance data for each device. The instance data is used to
+ * pass buffer management information between the application and
+ * the low-level callback function. It also includes a device ID,
+ * a handle to the MIDI Mapper, and a handle to the application's
+ * display window, so the callback can notify the window when input
+ * data is available. A single callback function is used to service
+ * all opened input devices.
+ */
+ for (i=0; (i<(unsigned)nmidiin) && (i<MAXMIDIINDEV); i++)
+ {
+ if ((lpCallbackInstanceData[ndev] = AllocCallbackInstanceData()) == NULL)
+ {
+ printf("Not enough memory available.\n");
+ FreeCircularBuffer(lpInputBuffer);
+ return;
+ }
+ lpCallbackInstanceData[i]->dwDevice = i;
+ lpCallbackInstanceData[i]->lpBuf = lpInputBuffer;
+
+ wRtn = midiInOpen((LPHMIDIIN)&hMidiIn[ndev],
+ midiinvec[i] - 1,
+ (DWORD)midiInputHandler,
+ (DWORD)lpCallbackInstanceData[ndev],
+ CALLBACK_FUNCTION);
+ if (wRtn)
+ {
+ FreeCallbackInstanceData(lpCallbackInstanceData[ndev]);
+ nt_midiinerror("midiInOpen: %s\n", wRtn);
+ }
+ else ndev++;
+ }
+
+ /* Start MIDI input.
+ */
+ for (i=0; i<ndev; i++)
+ {
+ if (hMidiIn[i])
+ midiInStart(hMidiIn[i]);
+ }
+ wNumDevices = ndev;
+}
+
+static void nt_close_midiin(void)
+{
+ unsigned int i;
+ /* Stop, reset, close MIDI input. Free callback instance data.
+ */
+
+ for (i=0; (i<wNumDevices) && (i<MAXMIDIINDEV); i++)
+ {
+ if (hMidiIn[i])
+ {
+ if (sys_verbose)
+ post("closing MIDI input %d...", i);
+ midiInStop(hMidiIn[i]);
+ midiInReset(hMidiIn[i]);
+ midiInClose(hMidiIn[i]);
+ FreeCallbackInstanceData(lpCallbackInstanceData[i]);
+ }
+ }
+
+ /* Free input buffer.
+ */
+ if (lpInputBuffer)
+ FreeCircularBuffer(lpInputBuffer);
+
+ if (sys_verbose)
+ post("...done");
+ wNumDevices = 0;
+}
+
+void inmidi_noteon(int portno, int channel, int pitch, int velo);
+void inmidi_controlchange(int portno, int channel, int ctlnumber, int value);
+void inmidi_programchange(int portno, int channel, int value);
+void inmidi_pitchbend(int portno, int channel, int value);
+void inmidi_aftertouch(int portno, int channel, int value);
+void inmidi_polyaftertouch(int portno, int channel, int pitch, int value);
+void inmidi_realtimein(int portno, int rtmsg);
+
+void sys_poll_midi(void)
+{
+ static EVENT nt_nextevent;
+ static int nt_isnextevent;
+ static double nt_nexteventtime;
+
+ while (1)
+ {
+ if (!nt_isnextevent)
+ {
+ if (!GetEvent(lpInputBuffer, &nt_nextevent)) break;
+ nt_isnextevent = 1;
+#ifdef MIDI_TIMESTAMP
+ nt_nexteventtime = nt_midigettimefor(&foo.timestamp);
+#endif
+ }
+#ifdef MIDI_TIMESTAMP
+ if (0.001 * clock_gettimesince(initsystime) >= nt_nexteventtime)
+#endif
+ {
+ int msgtype = ((nt_nextevent.data & 0xf0) >> 4) - 8;
+ int commandbyte = nt_nextevent.data & 0xff;
+ int byte1 = (nt_nextevent.data >> 8) & 0xff;
+ int byte2 = (nt_nextevent.data >> 16) & 0xff;
+ int portno = nt_nextevent.dwDevice;
+ switch (msgtype)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 6:
+ sys_midibytein(portno, commandbyte);
+ sys_midibytein(portno, byte1);
+ sys_midibytein(portno, byte2);
+ break;
+ case 4:
+ case 5:
+ sys_midibytein(portno, commandbyte);
+ sys_midibytein(portno, byte1);
+ break;
+ case 7:
+ sys_midibytein(portno, commandbyte);
+ break;
+ }
+ nt_isnextevent = 0;
+ }
+ }
+}
+
+/* ------------------- public routines -------------------------- */
+
+void sys_open_audio(int naudioindev, int *audioindev,
+ int nchindev, int *chindev, int naudiooutdev, int *audiooutdev,
+ int nchoutdev, int *choutdev, int rate) /* IOhannes */
+{
+ int inchans, outchans;
+ if (nchindev < 0)
+ inchans = (nchindev < 1 ? -1 : chindev[0]);
+ else
+ {
+ int i = nchindev;
+ int *l = chindev;
+ inchans = 0;
+ while (i--)
+ inchans += *l++;
+ }
+ if (nchoutdev<0)
+ outchans = (nchoutdev < 1 ? -1 : choutdev[0]);
+ else
+ {
+ int i = nchoutdev;
+ int *l = choutdev;
+ outchans = 0;
+ while (i--)
+ outchans += *l++;
+ }
+ if (inchans < 0)
+ inchans = DEFAULTCHANS;
+ if (outchans < 0)
+ outchans = DEFAULTCHANS;
+ if (inchans & 1)
+ {
+ post("input channels rounded up to even number");
+ inchans += 1;
+ }
+ if (outchans & 1)
+ {
+ post("output channels rounded up to even number");
+ outchans += 1;
+ }
+ if (inchans > NT_MAXCH)
+ inchans = NT_MAXCH;
+ if (outchans > NT_MAXCH)
+ outchans = NT_MAXCH;
+ if (sys_verbose)
+ post("channels in %d, out %d", inchans, outchans);
+ if (rate < 1)
+ rate = DEFAULTSRATE;
+ nt_setchsr(inchans, outchans, rate);
+ if (nt_whichapi == API_PORTAUDIO)
+ {
+ int blocksize = (nt_blocksize ? nt_blocksize : 256);
+ if (blocksize != (1 << ilog2(blocksize)))
+ post("warning: blocksize adjusted to power of 2: %d",
+ (blocksize = (1 << ilog2(blocksize))));
+ pa_open_audio(inchans, outchans, rate, sys_soundin, sys_soundout,
+ blocksize, nt_advance_samples/blocksize,
+ (naudioindev < 1 ? -1 : audioindev[0]),
+ (naudiooutdev < 1 ? -1 : audiooutdev[0]));
+ }
+ else
+ {
+ nt_nwavein = inchans / 2;
+ nt_nwaveout = outchans / 2;
+ nt_whichdac = (naudiooutdev < 1 ? (nt_nwaveout > 1 ? 0 : -1) : audiooutdev[0] - 1);
+ nt_whichadc = (naudioindev < 1 ? (nt_nwavein > 1 ? 0 : -1) : audioindev[0] - 1);
+ if (naudiooutdev > 1 || naudioindev > 1)
+ post("separate audio device choice not supported; using sequential devices.");
+ if (nt_blocksize)
+ post("warning: blocksize not settable for MMIO, just ASIO");
+ mmio_open_audio();
+ }
+}
+
+void sys_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec)
+{
+ if (nmidiout)
+ nt_open_midiout(nmidiout, midioutvec);
+ if (nmidiin)
+ {
+ post(
+ "midi input enabled; warning, don't close the DOS window directly!");
+ nt_open_midiin(nmidiin, midiinvec);
+ }
+ else post("not using MIDI input (use 'pd -midiindev 1' to override)");
+}
+
+float sys_getsr(void)
+{
+ return (sys_dacsr);
+}
+
+int sys_get_inchannels(void)
+{
+ return (2 * nt_nwavein);
+}
+
+int sys_get_outchannels(void)
+{
+ return (2 * nt_nwaveout);
+}
+
+void sys_audiobuf(int n)
+{
+ /* set the size, in msec, of the audio FIFO. It's incorrect to
+ calculate this on the basis of 44100 sample rate; really, the
+ work should have been done in nt_setchsr(). */
+ int nbuf = n * (44100./(REALDACBLKSIZE * 1000.));
+ if (nbuf >= MAXBUFFER)
+ {
+ fprintf(stderr, "pd: audio buffering maxed out to %d\n",
+ (int)(MAXBUFFER * ((REALDACBLKSIZE * 1000.)/44100.)));
+ nbuf = MAXBUFFER;
+ }
+ else if (nbuf < 4) nbuf = 4;
+ fprintf(stderr, "%d audio buffers\n", nbuf);
+ nt_naudiobuffer = nbuf;
+ if (nt_adcjitterbufsallowed > nbuf - 2)
+ nt_adcjitterbufsallowed = nbuf - 2;
+ if (nt_dacjitterbufsallowed > nbuf - 2)
+ nt_dacjitterbufsallowed = nbuf - 2;
+ sys_schedadvance = 1000 * n;
+}
+
+void sys_getmeters(float *inmax, float *outmax)
+{
+ if (inmax)
+ {
+ nt_meters = 1;
+ *inmax = nt_inmax;
+ *outmax = nt_outmax;
+ }
+ else
+ nt_meters = 0;
+ nt_inmax = nt_outmax = 0;
+}
+
+void sys_reportidle(void)
+{
+}
+
+int sys_send_dacs(void)
+{
+ if (nt_whichapi == API_PORTAUDIO)
+ return (pa_send_dacs());
+ else return (mmio_send_dacs());
+}
+
+void sys_close_audio( void)
+{
+ if (nt_whichapi == API_PORTAUDIO)
+ pa_close_audio();
+ else mmio_close_audio();
+}
+
+void sys_close_midi( void)
+{
+ nt_close_midiin();
+ nt_close_midiout();
+}
+
+void sys_setblocksize(int n)
+{
+ if (n < 1)
+ n = 1;
+ nt_blocksize = n;
+}
+
+/* ----------- public routines which are only defined for MSW/NT ---------- */
+
+/* select between MMIO and ASIO audio APIs */
+void nt_set_sound_api(int which)
+{
+ nt_whichapi = which;
+ if (sys_verbose)
+ post("nt_whichapi %d", nt_whichapi);
+}
+
+/* list the audio and MIDI device names */
+void sys_listdevs(void)
+{
+ UINT wRtn, ndevices;
+ unsigned int i;
+
+ /* for MIDI and audio in and out, get the number of devices.
+ Then get the capabilities of each device and print its description. */
+
+ ndevices = midiInGetNumDevs();
+ for (i = 0; i < ndevices; i++)
+ {
+ MIDIINCAPS micap;
+ wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) &micap,
+ sizeof(micap));
+ if (wRtn) nt_midiinerror("midiInGetDevCaps: %s\n", wRtn);
+ else fprintf(stderr,
+ "MIDI input device #%d: %s\n", i+1, micap.szPname);
+ }
+
+ ndevices = midiOutGetNumDevs();
+ for (i = 0; i < ndevices; i++)
+ {
+ MIDIOUTCAPS mocap;
+ wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap,
+ sizeof(mocap));
+ if (wRtn) nt_midiouterror("midiOutGetDevCaps: %s\n", wRtn);
+ else fprintf(stderr,
+ "MIDI output device #%d: %s\n", i+1, mocap.szPname);
+ }
+
+ if (nt_whichapi == API_PORTAUDIO)
+ {
+ pa_listdevs();
+ return;
+ }
+ ndevices = waveInGetNumDevs();
+ for (i = 0; i < ndevices; i++)
+ {
+ WAVEINCAPS wicap;
+ wRtn = waveInGetDevCaps(i, (LPWAVEINCAPS) &wicap,
+ sizeof(wicap));
+ if (wRtn) nt_waveinerror("waveInGetDevCaps: %s\n", wRtn);
+ else fprintf(stderr,
+ "audio input device #%d: %s\n", i+1, wicap.szPname);
+ }
+
+ ndevices = waveOutGetNumDevs();
+ for (i = 0; i < ndevices; i++)
+ {
+ WAVEOUTCAPS wocap;
+ wRtn = waveOutGetDevCaps(i, (LPWAVEOUTCAPS) &wocap,
+ sizeof(wocap));
+ if (wRtn) nt_waveouterror("waveOutGetDevCaps: %s\n", wRtn);
+ else fprintf(stderr,
+ "audio output device #%d: %s\n", i+1, wocap.szPname);
+ }
+}
+
+void nt_soundindev(int which)
+{
+ nt_whichadc = which - 1;
+}
+
+void nt_soundoutdev(int which)
+{
+ nt_whichdac = which - 1;
+}
+
+void glob_audio(void *dummy, t_floatarg fadc, t_floatarg fdac)
+{
+ int adc = fadc, dac = fdac;
+ if (!dac && !adc)
+ post("%d channels in, %d channels out",
+ 2 * nt_nwavein, 2 * nt_nwaveout);
+ else
+ {
+ sys_close_audio();
+ sys_open_audio(1, 0, 1, 0, /* dummy parameters */
+ 1, &adc, 1, &dac, sys_dacsr);
+ }
+}
+
diff --git a/pd/src/s_path.c b/pd/src/s_path.c
new file mode 100644
index 00000000..a61956f1
--- /dev/null
+++ b/pd/src/s_path.c
@@ -0,0 +1,279 @@
+/* Copyright (c) 1999 Guenter Geiger and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/*
+ * This file implements the loader for linux, which includes
+ * a little bit of path handling.
+ *
+ * Generalized by MSP to provide an open_via_path function
+ * and lists of files for all purposes.
+ */
+
+/* #define DEBUG(x) x */
+#define DEBUG(x)
+void readsf_banana( void); /* debugging */
+
+#include <stdlib.h>
+#ifdef UNIX
+#include <unistd.h>
+#include <sys/stat.h>
+#endif
+#ifdef NT
+#include <io.h>
+#endif
+
+#include <string.h>
+#include "m_imp.h"
+#include <stdio.h>
+#include <fcntl.h>
+
+static t_namelist *pd_path;
+
+/* Utility functions */
+
+/* copy until delimiter and return position after delimiter in string */
+/* if it was the last substring, return NULL */
+
+static const char* strtokcpy(char *to, const char *from, int delim)
+{
+ int size = 0;
+
+ while (from[size] != (char)delim && from[size] != '\0')
+ size++;
+
+ strncpy(to,from,size);
+ to[size] = '\0';
+ if (from[size] == '\0') return NULL;
+ if (size) return from+size+1;
+ else return NULL;
+}
+
+/* add a colon-separated list of names to a namelist */
+
+#ifdef NT
+#define SEPARATOR ';'
+#else
+#define SEPARATOR ':'
+#endif
+
+static t_namelist *namelist_doappend(t_namelist *listwas, const char *s)
+{
+ t_namelist *nl = listwas, *rtn = listwas, *nl2;
+ nl2 = (t_namelist *)(getbytes(sizeof(*nl)));
+ nl2->nl_next = 0;
+ nl2->nl_string = (char *)getbytes(strlen(s) + 1);
+ strcpy(nl2->nl_string, s);
+ sys_unbashfilename(nl2->nl_string, nl2->nl_string);
+ if (!nl)
+ nl = rtn = nl2;
+ else
+ {
+ while (nl->nl_next)
+ nl = nl->nl_next;
+ nl->nl_next = nl2;
+ }
+ return (rtn);
+
+}
+
+t_namelist *namelist_append(t_namelist *listwas, const char *s)
+{
+ const char *npos;
+ char temp[MAXPDSTRING];
+ t_namelist *nl = listwas, *rtn = listwas;
+
+ npos = s;
+ do
+ {
+ npos = strtokcpy(temp, npos, SEPARATOR);
+ if (! *temp) continue;
+ nl = namelist_doappend(nl, temp);
+ }
+ while (npos);
+ return (nl);
+}
+
+void namelist_free(t_namelist *listwas)
+{
+ t_namelist *nl, *nl2;
+ for (nl = listwas; nl; nl = nl2)
+ {
+ nl2 = nl->nl_next;
+ t_freebytes(nl->nl_string, strlen(nl->nl_string) + 1);
+ t_freebytes(nl, sizeof(*nl));
+ }
+}
+
+void sys_addpath(const char *p)
+{
+ pd_path = namelist_append(pd_path, p);
+}
+
+#ifdef NT
+#define NTOPENFLAG (bin ? _O_BINARY : _O_TEXT)
+#else
+#define NTOPENFLAG 0
+#endif
+
+/* search for a file in a specified directory, then along the globally
+defined search path, using ext as filename extension. Exception:
+if the 'name' starts with a slash or a letter, colon, and slash in NT,
+there is no search and instead we just try to open the file literally. The
+fd is returned, the directory ends up in the "dirresult" which must be at
+least "size" bytes. "nameresult" is set to point to the filename, which
+ends up in the same buffer as dirresult. */
+
+int open_via_path(const char *dir, const char *name, const char* ext,
+ char *dirresult, char **nameresult, unsigned int size, int bin)
+{
+ t_namelist *nl, thislist;
+ int fd = -1;
+ char listbuf[MAXPDSTRING];
+
+ if (name[0] == '/'
+#ifdef NT
+ || (name[1] == ':' && name[2] == '/')
+#endif
+ )
+ {
+ thislist.nl_next = 0;
+ thislist.nl_string = listbuf;
+ listbuf[0] = 0;
+ }
+ else
+ {
+ thislist.nl_string = listbuf;
+ thislist.nl_next = pd_path;
+ strncpy(listbuf, dir, MAXPDSTRING);
+ listbuf[MAXPDSTRING-1] = 0;
+ sys_unbashfilename(listbuf, listbuf);
+ }
+ for (nl = &thislist; nl; nl = nl->nl_next)
+ {
+ if (strlen(nl->nl_string) + strlen(name) + strlen(ext) + 4 >
+ size)
+ continue;
+ strcpy(dirresult, nl->nl_string);
+ if (*dirresult && dirresult[strlen(dirresult)-1] != '/')
+ strcat(dirresult, "/");
+ strcat(dirresult, name);
+ strcat(dirresult, ext);
+ sys_bashfilename(dirresult, dirresult);
+
+ DEBUG(post("looking for %s",dirresult));
+ /* see if we can open the file for reading */
+ if ((fd=open(dirresult,O_RDONLY | NTOPENFLAG)) >= 0)
+ {
+ /* in UNIX, further check that it's not a directory */
+#ifdef UNIX
+ struct stat statbuf;
+ int ok = ((fstat(fd, &statbuf) >= 0) &&
+ !S_ISDIR(statbuf.st_mode));
+ if (!ok)
+ {
+ if (sys_verbose) post("tried %s; stat failed or directory",
+ dirresult);
+ close (fd);
+ fd = -1;
+ }
+ else
+#endif
+ {
+ char *slash;
+ if (sys_verbose) post("tried %s and succeeded", dirresult);
+ sys_unbashfilename(dirresult, dirresult);
+ slash = strrchr(dirresult, '/');
+ if (slash)
+ {
+ *slash = 0;
+ *nameresult = slash + 1;
+ }
+ else *nameresult = dirresult;
+
+ return (fd);
+ }
+ }
+ else
+ {
+ if (sys_verbose) post("tried %s and failed", dirresult);
+ }
+ }
+ *dirresult = 0;
+ *nameresult = dirresult;
+ return (-1);
+}
+
+/* Startup file reading for linux */
+
+#ifdef __linux__
+
+#define STARTUPNAME ".pdrc"
+#define NUMARGS 1000
+
+int sys_argparse(int argc, char **argv);
+
+int sys_rcfile(void)
+{
+ FILE* file;
+ int i;
+ int k;
+ int rcargc;
+ char* rcargv[NUMARGS];
+ char* buffer;
+ char fname[MAXPDSTRING], buf[1000];
+
+ /* parse a startup file */
+
+ *fname = '\0';
+
+ strncat(fname, getenv("HOME"), MAXPDSTRING-10);
+ strcat(fname, "/");
+
+ strcat(fname, STARTUPNAME);
+
+ if (!(file = fopen(fname, "r")))
+ return 1;
+
+ post("reading startup file: %s", fname);
+
+ rcargv[0] = "."; /* this no longer matters to sys_argparse() */
+
+ for (i = 1; i < NUMARGS-1; i++)
+ {
+ if (fscanf(file, "%999s", buf) < 0)
+ break;
+ buf[1000] = 0;
+ if (!(rcargv[i] = malloc(strlen(buf) + 1)))
+ return (1);
+ strcpy(rcargv[i], buf);
+ }
+ if (i >= NUMARGS-1)
+ fprintf(stderr, "startup file too long; extra args dropped\n");
+ rcargv[i] = 0;
+
+ rcargc = i;
+
+ /* parse the options */
+
+ fclose(file);
+ if (sys_verbose)
+ {
+ if (rcargv)
+ {
+ post("startup args from RC file:");
+ for (i = 1; i < rcargc; i++)
+ post("%s", rcargv[i]);
+ }
+ else post("no RC file arguments found");
+ }
+ if (sys_argparse(rcargc, rcargv))
+ {
+ post("error parsing RC arguments");
+ return (1);
+ }
+ return (0);
+}
+#endif /* __linux__ */
+
+
diff --git a/pd/src/s_portaudio.c b/pd/src/s_portaudio.c
new file mode 100644
index 00000000..73dd55ab
--- /dev/null
+++ b/pd/src/s_portaudio.c
@@ -0,0 +1,197 @@
+/* Copyright (c) 2001 Miller Puckette and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* this file calls Ross Bencina's and Phil Burk's Portaudio package. It's
+ the main way in for Mac OS and, with Michael Casey's help, also into
+ ASIO in Windows. */
+
+/* dolist.
+ write a version of OpenAudioStream to take nchannels param
+ how do we specify latency?
+ listing devices?
+ choosing devices?
+*/
+
+#include "m_imp.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "pablio_pd.h"
+#include "portaudio.h"
+#ifndef NT
+#include "portmidi.h"
+#include "porttime.h"
+#include "pminternal.h"
+#endif
+
+ /* public interface declared in m_imp.h */
+
+ /* implementation */
+static PABLIO_Stream *pa_stream;
+static int pa_inchans, pa_outchans;
+static float *pa_soundin, *pa_soundout;
+
+#define MAX_PA_CHANS 32
+#define MAX_SAMPLES_PER_FRAME MAX_PA_CHANS * DACBLKSIZE
+
+int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin,
+ t_sample *soundout, int framesperbuf, int nbuffers,
+ int indeviceno, int outdeviceno)
+{
+ PaError err;
+ /* post("in %d out %d rate %d device %d", inchans, outchans, rate, deviceno); */
+ if (inchans != 0 && outchans != 0 && inchans != outchans)
+ error("portaudio: number of input and output channels must match");
+ if (sys_verbose)
+ post("portaudio: opening for %d channels in, %d out",
+ inchans, outchans);
+ if (inchans > MAX_PA_CHANS)
+ {
+ post("input channels reduced to maximum %d", MAX_PA_CHANS);
+ inchans = MAX_PA_CHANS;
+ }
+ if (outchans > MAX_PA_CHANS)
+ {
+ post("output channels reduced to maximum %d", MAX_PA_CHANS);
+ outchans = MAX_PA_CHANS;
+ }
+
+ if (inchans && outchans)
+ err = OpenAudioStream( &pa_stream, rate, paFloat32,
+ PABLIO_READ_WRITE, inchans, framesperbuf, nbuffers, indeviceno, outdeviceno);
+ else if (inchans)
+ err = OpenAudioStream( &pa_stream, rate, paFloat32,
+ PABLIO_READ, inchans, framesperbuf, nbuffers, indeviceno, outdeviceno);
+ else if (outchans)
+ err = OpenAudioStream( &pa_stream, rate, paFloat32,
+ PABLIO_WRITE, outchans, framesperbuf, nbuffers, indeviceno, outdeviceno);
+ else err = 0;
+ if ( err != paNoError )
+ {
+ fprintf( stderr, "Error number %d occured opening portaudio stream\n",
+ err);
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ Pa_Terminate();
+ return (1);
+ }
+ else if (sys_verbose)
+ post("... opened OK.");
+ pa_inchans = inchans;
+ pa_outchans = outchans;
+ pa_soundin = soundin;
+ pa_soundout = soundout;
+ return (0);
+}
+
+void pa_close_audio( void)
+{
+ if (pa_inchans || pa_outchans)
+ CloseAudioStream( pa_stream );
+ pa_inchans = pa_outchans = 0;
+}
+
+int pa_send_dacs(void)
+{
+ float samples[MAX_SAMPLES_PER_FRAME], *fp1, *fp2;
+ int i, j;
+ double timebefore;
+
+ timebefore = sys_getrealtime();
+ if (pa_inchans)
+ {
+ ReadAudioStream(pa_stream, samples, DACBLKSIZE);
+ for (j = 0, fp1 = pa_soundin; j < pa_inchans; j++, fp1 += DACBLKSIZE)
+ for (i = 0, fp2 = samples + j; i < DACBLKSIZE; i++,
+ fp2 += pa_inchans)
+ {
+ fp1[i] = *fp2;
+ }
+ }
+ if (pa_outchans)
+ {
+ for (j = 0, fp1 = pa_soundout; j < pa_outchans; j++, fp1 += DACBLKSIZE)
+ for (i = 0, fp2 = samples + j; i < DACBLKSIZE; i++,
+ fp2 += pa_outchans)
+ {
+ *fp2 = fp1[i];
+ fp1[i] = 0;
+ }
+ WriteAudioStream(pa_stream, samples, DACBLKSIZE);
+ }
+
+ if (sys_getrealtime() > timebefore + 0.002)
+ return (SENDDACS_SLEPT);
+ else return (SENDDACS_YES);
+}
+
+
+void pa_listdevs(void) /* lifted from pa_devs.c in portaudio */
+{
+ int i,j;
+ int numDevices;
+ const PaDeviceInfo *pdi;
+ PaError err;
+ Pa_Initialize();
+ numDevices = Pa_CountDevices();
+ if( numDevices < 0 )
+ {
+ fprintf(stderr, "ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
+ err = numDevices;
+ goto error;
+ }
+ fprintf(stderr, "Number of devices = %d\n", numDevices );
+ for( i=0; i<numDevices; i++ )
+ {
+ pdi = Pa_GetDeviceInfo( i );
+ fprintf(stderr, "---------------------------------------------- #%d",
+ i+1 );
+ if( i == Pa_GetDefaultInputDeviceID() ) fprintf(stderr, " DefaultInput");
+ if( i == Pa_GetDefaultOutputDeviceID() ) fprintf(stderr, " DefaultOutput");
+ fprintf(stderr, "\nName = %s\n", pdi->name );
+ fprintf(stderr, "Max Inputs = %d", pdi->maxInputChannels );
+ fprintf(stderr, ", Max Outputs = %d\n", pdi->maxOutputChannels );
+ if( pdi->numSampleRates == -1 )
+ {
+ fprintf(stderr, "Sample Rate Range = %f to %f\n", pdi->sampleRates[0], pdi->sampleRates[1] );
+ }
+ else
+ {
+ fprintf(stderr, "Sample Rates =");
+ for( j=0; j<pdi->numSampleRates; j++ )
+ {
+ fprintf(stderr, " %8.2f,", pdi->sampleRates[j] );
+ }
+ fprintf(stderr, "\n");
+ }
+ fprintf(stderr, "Native Sample Formats = ");
+ if( pdi->nativeSampleFormats & paInt8 ) fprintf(stderr, "paInt8, ");
+ if( pdi->nativeSampleFormats & paUInt8 ) fprintf(stderr, "paUInt8, ");
+ if( pdi->nativeSampleFormats & paInt16 ) fprintf(stderr, "paInt16, ");
+ if( pdi->nativeSampleFormats & paInt32 ) fprintf(stderr, "paInt32, ");
+ if( pdi->nativeSampleFormats & paFloat32 ) fprintf(stderr, "paFloat32, ");
+ if( pdi->nativeSampleFormats & paInt24 ) fprintf(stderr, "paInt24, ");
+ if( pdi->nativeSampleFormats & paPackedInt24 ) fprintf(stderr, "paPackedInt24, ");
+ fprintf(stderr, "\n");
+ }
+
+ fprintf(stderr, "----------------------------------------------\n");
+ goto midi;
+
+error:
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+
+ /* and MIDI */
+midi: ;
+#ifndef NT
+ for (i = 0; i < Pm_CountDevices(); i++)
+ {
+ const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
+ printf("%d: %s, %s", i, info->interf, info->name);
+ if (info->input) printf(" (input)");
+ if (info->output) printf(" (output)");
+ printf("\n");
+ }
+#endif
+}
diff --git a/pd/src/s_print.c b/pd/src/s_print.c
new file mode 100644
index 00000000..33944286
--- /dev/null
+++ b/pd/src/s_print.c
@@ -0,0 +1,150 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include "m_pd.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+void post(char *fmt, ...)
+{
+ va_list ap;
+ t_int arg[8];
+ int i;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ putc('\n', stderr);
+}
+
+void startpost(char *fmt, ...)
+{
+ va_list ap;
+ t_int arg[8];
+ int i;
+ va_start(ap, fmt);
+
+ for (i = 0 ; i < 8; i++) arg[i] = va_arg(ap, t_int);
+ va_end(ap);
+ fprintf(stderr, fmt, arg[0], arg[1], arg[2], arg[3],
+ arg[4], arg[5], arg[6], arg[7]);
+}
+
+void poststring(char *s)
+{
+ fprintf(stderr, " %s", s);
+}
+
+void postatom(int argc, t_atom *argv)
+{
+ int i;
+ for (i = 0; i < argc; i++)
+ {
+ char buf[80];
+ atom_string(argv+i, buf, 80);
+ poststring(buf);
+ }
+}
+
+void postfloat(float f)
+{
+ char buf[80];
+ t_atom a;
+ SETFLOAT(&a, f);
+ postatom(1, &a);
+}
+
+void endpost(void)
+{
+ fprintf(stderr, "\n");
+}
+
+void error(char *fmt, ...)
+{
+ va_list ap;
+ t_int arg[8];
+ int i;
+ va_start(ap, fmt);
+ fprintf(stderr, "error: ");
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ putc('\n', stderr);
+}
+
+ /* here's the good way to log errors -- keep a pointer to the
+ offending or offended object around so the user can search for it
+ later. */
+
+static void *error_object;
+static char error_string[256];
+void canvas_finderror(void *object);
+
+void pd_error(void *object, char *fmt, ...)
+{
+ va_list ap;
+ t_int arg[8];
+ int i;
+ static int saidit = 0;
+ va_start(ap, fmt);
+ vsprintf(error_string, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "error: %s\n", error_string);
+ error_object = object;
+ if (!saidit)
+ {
+ post("... you might be able to track this down from the Find menu.");
+ saidit = 1;
+ }
+}
+
+void glob_finderror(t_pd *dummy)
+{
+ if (!error_object)
+ post("no findable error yet.");
+ else
+ {
+ post("last trackable error:");
+ post("%s", error_string);
+ canvas_finderror(error_object);
+ }
+}
+
+void bug(char *fmt, ...)
+{
+ va_list ap;
+ t_int arg[8];
+ int i;
+ va_start(ap, fmt);
+
+ for (i = 0 ; i < 8; i++) arg[i] = va_arg(ap, t_int);
+ va_end(ap);
+ fprintf(stderr, "Consistency check failed: ");
+ fprintf(stderr, fmt, arg[0], arg[1], arg[2], arg[3],
+ arg[4], arg[5], arg[6], arg[7]);
+ putc('\n', stderr);
+}
+
+ /* this isn't worked out yet. */
+static char *errobject;
+static char *errstring;
+
+void sys_logerror(char *object, char *s)
+{
+ errobject = object;
+ errstring = s;
+}
+
+void sys_unixerror(char *object)
+{
+ errobject = object;
+ errstring = strerror(errno);
+}
+
+void sys_ouch(void)
+{
+ if (*errobject) error("%s: %s", errobject, errstring);
+ else error("%s", errstring);
+}
diff --git a/pd/src/s_sgi.c b/pd/src/s_sgi.c
new file mode 100644
index 00000000..82a23dfb
--- /dev/null
+++ b/pd/src/s_sgi.c
@@ -0,0 +1,433 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include "m_imp.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#ifdef HAVE_BSTRING_H
+#include <bstring.h>
+#endif
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <dmedia/audio.h>
+#include <sys/fpu.h>
+#include <dmedia/midi.h>
+int mdInit(void); /* prototype was messed up in midi.h */
+/* #include "sys/select.h" */
+
+static ALport iport;
+static ALport oport;
+static ALconfig sgi_config;
+#define DEFAULTCHANS 2
+#define SGI_MAXCH 8
+static int sys_inchannels, sys_outchannels;
+static int sys_audiobufsamps;
+int sys_schedadvance = 50000; /* scheduler advance in microseconds */
+ /* (this is set ridiculously high until we can get the real-time
+ scheduling act together.) */
+int sys_hipriority = 0;
+static int sgi_meters; /* true if we're metering */
+static float sgi_inmax; /* max input amplitude */
+static float sgi_outmax; /* max output amplitude */
+
+
+ /*
+ set the special "flush zero" but (FS, bit 24) in the
+ Control Status Register of the FPU of R4k and beyond
+ so that the result of any underflowing operation will
+ be clamped to zero, and no exception of any kind will
+ be generated on the CPU.
+
+ thanks to cpirazzi@cp.esd.sgi.com (Chris Pirazzi).
+ */
+
+static void sgi_flush_all_underflows_to_zero(void)
+{
+ union fpc_csr f;
+ f.fc_word = get_fpc_csr();
+ f.fc_struct.flush = 1;
+ set_fpc_csr(f.fc_word);
+}
+
+static void sgi_setchsr(int inchans, int outchans, int sr)
+{
+ int inbytes = inchans * (DACBLKSIZE*sizeof(float));
+ int outbytes = outchans * (DACBLKSIZE*sizeof(float));
+
+ sys_audiobufsamps = 64 * (int)(((float)sys_schedadvance)* sr / 64000000.);
+ sys_inchannels = inchans;
+ sys_outchannels = outchans;
+ sys_dacsr = sr;
+
+ if (sys_soundin)
+ free(sys_soundin);
+ sys_soundin = (t_float *)malloc(inbytes);
+ memset(sys_soundin, 0, inbytes);
+
+ if (sys_soundout)
+ free(sys_soundout);
+ sys_soundout = (t_float *)malloc(outbytes);
+ memset(sys_soundout, 0, outbytes);
+
+ memset(sys_soundout, 0, sys_outchannels * (DACBLKSIZE*sizeof(float)));
+ memset(sys_soundin, 0, sys_inchannels * (DACBLKSIZE*sizeof(float)));
+}
+
+static void sgi_open_audio(void)
+{
+ long pvbuf[4];
+ long pvbuflen;
+ /* get current sample rate -- should use this to set logical SR */
+ pvbuf[0] = AL_INPUT_RATE;
+ pvbuf[2] = AL_OUTPUT_RATE;
+ pvbuflen = 4;
+
+ ALgetparams(AL_DEFAULT_DEVICE, pvbuf, pvbuflen);
+
+ if (sys_inchannels && pvbuf[1] != sys_dacsr)
+ post("warning: input sample rate (%d) doesn't match mine (%f)\n",
+ pvbuf[1], sys_dacsr);
+
+ if (sys_outchannels && pvbuf[3] != sys_dacsr)
+ post("warning: output sample rate (%d) doesn't match mine (%f)\n",
+ pvbuf[3], sys_dacsr);
+
+ pvbuf[3] = pvbuf[1];
+ ALsetparams(AL_DEFAULT_DEVICE, pvbuf, pvbuflen);
+
+ sgi_config = ALnewconfig();
+
+ ALsetsampfmt(sgi_config, AL_SAMPFMT_FLOAT);
+
+ if (sys_outchannels)
+ {
+ ALsetchannels(sgi_config, sys_outchannels);
+ ALsetqueuesize(sgi_config, sys_audiobufsamps * sys_outchannels);
+ oport = ALopenport("the ouput port", "w", sgi_config);
+ if (!oport)
+ fprintf(stderr,"Pd: failed to open audio write port\n");
+ }
+ else oport = 0;
+ if (sys_inchannels)
+ {
+ ALsetchannels(sgi_config, sys_inchannels);
+ ALsetqueuesize(sgi_config, sys_audiobufsamps * sys_inchannels);
+ iport = ALopenport("the input port", "r", sgi_config);
+ if (!iport)
+ fprintf(stderr,"Pd: failed to open audio read port\n");
+ }
+ else iport = 0;
+}
+
+void sys_close_audio( void)
+{
+ if (iport) ALcloseport(iport);
+ if (oport) ALcloseport(oport);
+}
+
+void sys_close_midi( void)
+{
+ /* ??? */
+}
+
+t_sample *sys_soundout;
+t_sample *sys_soundin;
+float sys_dacsr;
+
+int sys_send_dacs(void)
+{
+ float buf[SGI_MAXCH * DACBLKSIZE], *fp1, *fp2, *fp3, *fp4;
+ long outfill, infill;
+ int outchannels = sys_outchannels, inchannels = sys_inchannels;
+ int i, nwait = 0, channel;
+ int outblk = DACBLKSIZE * outchannels;
+ int inblk = DACBLKSIZE * inchannels;
+ outfill = ALgetfillable(oport);
+ if (sgi_meters)
+ {
+ int i, n;
+ float maxsamp;
+ for (i = 0, n = sys_inchannels * DACBLKSIZE, maxsamp = sgi_inmax;
+ i < n; i++)
+ {
+ float f = sys_soundin[i];
+ if (f > maxsamp) maxsamp = f;
+ else if (-f > maxsamp) maxsamp = -f;
+ }
+ sgi_inmax = maxsamp;
+ for (i = 0, n = sys_outchannels * DACBLKSIZE, maxsamp = sgi_outmax;
+ i < n; i++)
+ {
+ float f = sys_soundout[i];
+ if (f > maxsamp) maxsamp = f;
+ else if (-f > maxsamp) maxsamp = -f;
+ }
+ sgi_outmax = maxsamp;
+ }
+
+ if (outfill <= outblk)
+ {
+ while ((infill = ALgetfilled(iport)) > 2*inblk)
+ {
+ if (sys_verbose) post("drop ADC buf");
+ ALreadsamps(iport, buf, inblk);
+ return (0);
+ }
+ }
+ if (outchannels)
+ {
+ for (channel = 0, fp1 = buf, fp2 = sys_soundout;
+ channel < outchannels; channel++, fp1++, fp2 += DACBLKSIZE)
+ {
+ for (i = 0, fp3 = fp1, fp4 = fp2; i < DACBLKSIZE;
+ i++, fp3 += outchannels, fp4++)
+ *fp3 = *fp4, *fp4 = 0;
+ }
+ ALwritesamps(oport, buf, outchannels* DACBLKSIZE);
+ }
+ if (inchannels)
+ {
+ if (infill > inblk)
+ ALreadsamps(iport, buf, inchannels* DACBLKSIZE);
+ else
+ {
+ if (sys_verbose) post("extra ADC buf");
+ memset(buf, 0, inblk*sizeof(float));
+ }
+ for (channel = 0, fp1 = buf, fp2 = sys_soundin;
+ channel < inchannels; channel++, fp1++, fp2 += DACBLKSIZE)
+ {
+ for (i = 0, fp3 = fp1, fp4 = fp2; i < DACBLKSIZE;
+ i++, fp3 += inchannels, fp4++)
+ *fp4 = *fp3;
+ }
+ }
+ return (1);
+}
+
+/* ------------------------- MIDI -------------------------- */
+
+#define NPORT 2
+
+static MDport sgi_inport[NPORT];
+static MDport sgi_outport[NPORT];
+
+void sgi_open_midi(int midiin, int midiout)
+{
+ int i;
+ int sgi_nports = mdInit();
+ if (sgi_nports < 0) sgi_nports = 0;
+ else if (sgi_nports > NPORT) sgi_nports = NPORT;
+ if (sys_verbose)
+ {
+ if (!sgi_nports)
+ {
+ post("no serial ports are configured for MIDI;");
+ post("if you want to use MIDI, try exiting Pd, typing");
+ post("'startmidi -d /dev/ttyd2' to a shell, and restarting Pd.");
+ }
+ else if (sgi_nports == 1)
+ post("Found one MIDI port on %s", mdGetName(0));
+ else if (sgi_nports == 2)
+ post("Found MIDI ports on %s and %s",
+ mdGetName(0), mdGetName(1));
+ }
+ if (midiin)
+ {
+ for (i = 0; i < sgi_nports; i++)
+ {
+ if (!(sgi_inport[i] = mdOpenInPort(mdGetName(i))))
+ error("MIDI input port %d: open failed", i+1);;
+ }
+ }
+ if (midiout)
+ {
+ for (i = 0; i < sgi_nports; i++)
+ {
+ if (!(sgi_outport[i] = mdOpenOutPort(mdGetName(i))))
+ error("MIDI output port %d: open failed", i+1);;
+ }
+ }
+ return;
+}
+
+void sys_putmidimess(int portno, int a, int b, int c)
+{
+ MDevent mdv;
+ if (portno >= NPORT || portno < 0 || !sgi_outport[portno]) return;
+ mdv.msg[0] = a;
+ mdv.msg[1] = b;
+ mdv.msg[2] = c;
+ mdv.msg[3] = 0;
+ mdv.sysexmsg = 0;
+ mdv.stamp = 0;
+ mdv.msglen = 0;
+ if (mdSend(sgi_outport[portno], &mdv, 1) < 0)
+ error("MIDI output error\n");
+ post("msg out %d %d %d", a, b, c);
+}
+
+void sys_putmidibyte(int portno, int foo)
+{
+ error("MIDI raw byte output not available on SGI");
+}
+
+void inmidi_noteon(int portno, int channel, int pitch, int velo);
+void inmidi_controlchange(int portno, int channel, int ctlnumber, int value);
+void inmidi_programchange(int portno, int channel, int value);
+void inmidi_pitchbend(int portno, int channel, int value);
+void inmidi_aftertouch(int portno, int channel, int value);
+void inmidi_polyaftertouch(int portno, int channel, int pitch, int value);
+
+void sys_poll_midi(void)
+{
+ int i;
+ MDport *mp;
+ for (i = 0, mp = sgi_inport; i < NPORT; i++, mp++)
+ {
+ int ret, status, b1, b2, nfds;
+ MDevent mdv;
+ fd_set inports;
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ if (!*mp) continue;
+ FD_ZERO(&inports);
+ FD_SET(mdGetFd(*mp), &inports);
+
+ if (select(mdGetFd(*mp)+1 , &inports, 0, 0, &timeout) < 0)
+ perror("midi select");
+ if (FD_ISSET(mdGetFd(*mp),&inports))
+ {
+ if (mdReceive(*mp, &mdv, 1) < 0)
+ error("failure receiving message\n");
+ else if (mdv.msg[0] == MD_SYSEX) mdFree(mdv.sysexmsg);
+
+ else
+ {
+ int status = mdv.msg[0];
+ int channel = (status & 0xf) + 1;
+ int b1 = mdv.msg[1];
+ int b2 = mdv.msg[2];
+ switch(status & 0xf0)
+ {
+ case MD_NOTEOFF:
+ inmidi_noteon(i, channel, b1, 0);
+ break;
+ case MD_NOTEON:
+ inmidi_noteon(i, channel, b1, b2);
+ break;
+ case MD_POLYKEYPRESSURE:
+ inmidi_polyaftertouch(i, channel, b1, b2);
+ break;
+ case MD_CONTROLCHANGE:
+ inmidi_controlchange(i, channel, b1, b2);
+ break;
+ case MD_PITCHBENDCHANGE:
+ inmidi_pitchbend(i, channel, ((b2 << 7) + b1));
+ break;
+ case MD_PROGRAMCHANGE:
+ inmidi_programchange(i, channel, b1);
+ break;
+ case MD_CHANNELPRESSURE:
+ inmidi_aftertouch(i, channel, b1);
+ break;
+ }
+ }
+ }
+ }
+}
+
+ /* public routines */
+
+void sys_open_audio(int naudioindev, int *audioindev, int nchindev, int *chindev,
+ int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev,
+ int rate) /* IOhannes */
+{
+ int inchans=0;
+ int outchans=0;
+ if (nchindev<0)inchans == -1;
+ else {
+ int i=nchindev;
+ int *l=chindev;
+ while(i--)inchans+=*l++;
+ }
+ if (nchoutdev<0)outchans == -1;
+ else {
+ int i=nchoutdev;
+ int *l=choutdev;
+ while(i--)outchans+=*l++;
+ }
+
+ if (inchans < 0) inchans = DEFAULTCHANS;
+ if (outchans < 0) outchans = DEFAULTCHANS;
+ if (inchans > SGI_MAXCH) inchans = SGI_MAXCH;
+ if (outchans > SGI_MAXCH) outchans = SGI_MAXCH;
+
+ sgi_setchsr(inchans, outchans, rate);
+ sgi_flush_all_underflows_to_zero();
+ sgi_open_audio();
+}
+
+void sys_open_midi(int nmidiin, int *midiinvec,
+ int nmidiout, int *midioutvec)
+{
+ sgi_open_midi(nmidiin!=0, nmidiout!=0);
+}
+
+float sys_getsr(void)
+{
+ return (sys_dacsr);
+}
+
+void sys_audiobuf(int n)
+{
+ /* set the size, in milliseconds, of the audio FIFO */
+ if (n < 5) n = 5;
+ else if (n > 5000) n = 5000;
+ fprintf(stderr, "audio buffer set to %d milliseconds\n", n);
+ sys_schedadvance = n * 1000;
+}
+
+void sys_getmeters(float *inmax, float *outmax)
+{
+ if (inmax)
+ {
+ sgi_meters = 1;
+ *inmax = sgi_inmax;
+ *outmax = sgi_outmax;
+ }
+ else
+ sgi_meters = 0;
+ sgi_inmax = sgi_outmax = 0;
+}
+
+void sys_reportidle(void)
+{
+}
+
+int sys_get_inchannels(void)
+{
+ return (sys_inchannels);
+}
+
+int sys_get_outchannels(void)
+{
+ return (sys_outchannels);
+}
+
+void sys_set_priority(int foo)
+{
+ fprintf(stderr,
+ "warning: priority boosting in IRIX not implemented yet\n");
+}
+
+void sys_setblocksize(int n)
+{
+ fprintf(stderr,
+ "warning: blocksize not settable in IRIX\n");
+}
diff --git a/pd/src/s_unix.c b/pd/src/s_unix.c
new file mode 100644
index 00000000..ee0ce160
--- /dev/null
+++ b/pd/src/s_unix.c
@@ -0,0 +1,445 @@
+/* Copyright (c) 1997-1999 Miller Puckette and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* this file is misnamed. It seems to handle clock functions and MIDI I/O;
+probably should be called "s_midi.c" */
+
+#include "m_imp.h"
+#ifdef UNIX
+#include <unistd.h>
+#include <sys/time.h>
+#ifdef HAVE_BSTRING_H
+#include <bstring.h>
+#endif
+#endif
+#ifdef NT
+#include <winsock.h>
+#include <sys/types.h>
+#include <sys/timeb.h>
+#include <wtypes.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+
+#ifdef NT
+static LARGE_INTEGER nt_inittime;
+static double nt_freq = 0;
+
+static void sys_initntclock(void)
+{
+ LARGE_INTEGER f1;
+ LARGE_INTEGER now;
+ QueryPerformanceCounter(&now);
+ if (!QueryPerformanceFrequency(&f1))
+ {
+ fprintf(stderr, "pd: QueryPerformanceFrequency failed\n");
+ f1.QuadPart = 1;
+ }
+ nt_freq = f1.QuadPart;
+ nt_inittime = now;
+}
+
+#if 0
+ /* this is a version you can call if you did the QueryPerformanceCounter
+ call yourself. Necessary for time tagging incoming MIDI at interrupt
+ level, for instance; but we're not doing that just now. */
+
+double nt_tixtotime(LARGE_INTEGER *dumbass)
+{
+ if (nt_freq == 0) sys_initntclock();
+ return (((double)(dumbass->QuadPart - nt_inittime.QuadPart)) / nt_freq);
+}
+#endif
+#endif /* NT */
+
+double sys_getrealtime(void) /* get "real time" in seconds */
+{
+#ifdef UNIX
+ static struct timeval then;
+ struct timeval now;
+ gettimeofday(&now, 0);
+ if (then.tv_sec == 0 && then.tv_usec == 0) then = now;
+ return ((now.tv_sec - then.tv_sec) +
+ (1./1000000.) * (now.tv_usec - then.tv_usec));
+#endif
+#ifdef NT
+ LARGE_INTEGER now;
+ QueryPerformanceCounter(&now);
+ if (nt_freq == 0) sys_initntclock();
+ return (((double)(now.QuadPart - nt_inittime.QuadPart)) / nt_freq);
+#endif
+}
+
+typedef struct _midiqelem
+{
+ double q_time;
+ int q_portno;
+ unsigned char q_onebyte;
+ unsigned char q_byte1;
+ unsigned char q_byte2;
+ unsigned char q_byte3;
+} t_midiqelem;
+
+#define MIDIQSIZE 1024
+
+t_midiqelem midi_outqueue[MIDIQSIZE];
+int midi_outhead, midi_outtail;
+t_midiqelem midi_inqueue[MIDIQSIZE];
+int midi_inhead, midi_intail;
+static double sys_midiinittime;
+
+ /* this is our current estimate for at what "system" real time the
+ current logical time's output should occur. */
+static double sys_dactimeminusrealtime;
+ /* same for input, should be schduler advance earlier. */
+static double sys_adctimeminusrealtime;
+
+static double sys_newdactimeminusrealtime = -1e20;
+static double sys_newadctimeminusrealtime = -1e20;
+static double sys_whenupdate;
+
+void sys_initmidiqueue( void)
+{
+ sys_midiinittime = clock_getlogicaltime();
+ sys_dactimeminusrealtime = sys_adctimeminusrealtime = 0;
+}
+
+ /* this is called from the OS dependent code from time to time when we
+ think we know the delay (outbuftime) in seconds, at which the last-output
+ audio sample will go out the door. */
+void sys_setmiditimediff(double inbuftime, double outbuftime)
+{
+ double dactimeminusrealtime =
+ .001 * clock_gettimesince(sys_midiinittime)
+ - outbuftime - sys_getrealtime();
+ double adctimeminusrealtime =
+ .001 * clock_gettimesince(sys_midiinittime)
+ + inbuftime - sys_getrealtime();
+ if (dactimeminusrealtime > sys_newdactimeminusrealtime)
+ sys_newdactimeminusrealtime = dactimeminusrealtime;
+ if (adctimeminusrealtime > sys_newadctimeminusrealtime)
+ sys_newadctimeminusrealtime = adctimeminusrealtime;
+ if (sys_getrealtime() > sys_whenupdate)
+ {
+ sys_dactimeminusrealtime = sys_newdactimeminusrealtime;
+ sys_adctimeminusrealtime = sys_newadctimeminusrealtime;
+ sys_newdactimeminusrealtime = -1e20;
+ sys_newadctimeminusrealtime = -1e20;
+ sys_whenupdate = sys_getrealtime() + 1;
+ }
+}
+
+ /* return the logical time of the DAC sample we believe is currently
+ going out, based on how much "system time" has elapsed since the
+ last time sys_setmiditimediff got called. */
+static double sys_getmidioutrealtime( void)
+{
+ return (sys_getrealtime() + sys_dactimeminusrealtime);
+}
+
+static double sys_getmidiinrealtime( void)
+{
+ return (sys_getrealtime() + sys_adctimeminusrealtime);
+}
+
+static void sys_putnext( void)
+{
+ int portno = midi_outqueue[midi_outtail].q_portno;
+ if (midi_outqueue[midi_outtail].q_onebyte)
+ sys_putmidibyte(portno, midi_outqueue[midi_outtail].q_byte1);
+ else sys_putmidimess(portno, midi_outqueue[midi_outtail].q_byte1,
+ midi_outqueue[midi_outtail].q_byte2,
+ midi_outqueue[midi_outtail].q_byte3);
+ midi_outtail = (midi_outtail + 1 == MIDIQSIZE ? 0 : midi_outtail + 1);
+}
+
+/* #define TEST_DEJITTER */
+
+void sys_pollmidioutqueue( void)
+{
+#ifdef TEST_DEJITTER
+ static int db = 0;
+#endif
+ double midirealtime = sys_getmidioutrealtime();
+#ifdef TEST_DEJITTER
+ if (midi_outhead == midi_outtail)
+ db = 0;
+#endif
+ while (midi_outhead != midi_outtail)
+ {
+#ifdef TEST_DEJITTER
+ if (!db)
+ {
+ post("out: del %f, midiRT %f logicaltime %f, RT %f dacminusRT %f",
+ (midi_outqueue[midi_outtail].q_time - midirealtime),
+ midirealtime, .001 * clock_gettimesince(sys_midiinittime),
+ sys_getrealtime(), sys_dactimeminusrealtime);
+ db = 1;
+ }
+#endif
+ if (midi_outqueue[midi_outtail].q_time <= midirealtime)
+ sys_putnext();
+ else break;
+ }
+}
+
+static void sys_queuemidimess(int portno, int onebyte, int a, int b, int c)
+{
+ t_midiqelem *midiqelem;
+ int newhead = midi_outhead +1;
+ if (newhead == MIDIQSIZE)
+ newhead = 0;
+ /* if FIFO is full flush an element to make room */
+ if (newhead == midi_outtail)
+ sys_putnext();
+ midi_outqueue[midi_outhead].q_portno = portno;
+ midi_outqueue[midi_outhead].q_onebyte = onebyte;
+ midi_outqueue[midi_outhead].q_byte1 = a;
+ midi_outqueue[midi_outhead].q_byte2 = b;
+ midi_outqueue[midi_outhead].q_byte3 = c;
+ midi_outqueue[midi_outhead].q_time =
+ .001 * clock_gettimesince(sys_midiinittime);
+ midi_outhead = newhead;
+ sys_pollmidioutqueue();
+}
+
+#define MIDI_NOTEON 144
+#define MIDI_POLYAFTERTOUCH 160
+#define MIDI_CONTROLCHANGE 176
+#define MIDI_PROGRAMCHANGE 192
+#define MIDI_AFTERTOUCH 208
+#define MIDI_PITCHBEND 224
+
+void outmidi_noteon(int portno, int channel, int pitch, int velo)
+{
+ if (pitch < 0) pitch = 0;
+ else if (pitch > 127) pitch = 127;
+ if (velo < 0) velo = 0;
+ else if (velo > 127) velo = 127;
+ sys_queuemidimess(portno, 0, MIDI_NOTEON + (channel & 0xf), pitch, velo);
+}
+
+void outmidi_controlchange(int portno, int channel, int ctl, int value)
+{
+ if (ctl < 0) ctl = 0;
+ else if (ctl > 127) ctl = 127;
+ if (value < 0) value = 0;
+ else if (value > 127) value = 127;
+ sys_queuemidimess(portno, 0, MIDI_CONTROLCHANGE + (channel & 0xf),
+ ctl, value);
+}
+
+void outmidi_programchange(int portno, int channel, int value)
+{
+ if (value < 0) value = 0;
+ else if (value > 127) value = 127;
+ sys_queuemidimess(portno, 0,
+ MIDI_PROGRAMCHANGE + (channel & 0xf), value, 0);
+}
+
+void outmidi_pitchbend(int portno, int channel, int value)
+{
+ if (value < 0) value = 0;
+ else if (value > 16383) value = 16383;
+ sys_queuemidimess(portno, 0, MIDI_PITCHBEND + (channel & 0xf),
+ (value & 127), ((value>>7) & 127));
+}
+
+void outmidi_aftertouch(int portno, int channel, int value)
+{
+ if (value < 0) value = 0;
+ else if (value > 127) value = 127;
+ sys_queuemidimess(portno, 0, MIDI_AFTERTOUCH + (channel & 0xf), value, 0);
+}
+
+void outmidi_polyaftertouch(int portno, int channel, int pitch, int value)
+{
+ if (pitch < 0) pitch = 0;
+ else if (pitch > 127) pitch = 127;
+ if (value < 0) value = 0;
+ else if (value > 127) value = 127;
+ sys_queuemidimess(portno, 0, MIDI_POLYAFTERTOUCH + (channel & 0xf),
+ pitch, value);
+}
+
+void outmidi_mclk(int portno)
+{
+ sys_queuemidimess(portno, 1, 0xf8, 0,0);
+}
+
+/* ------------------------- MIDI input queue handling ------------------ */
+typedef struct midiparser
+{
+ int mp_status;
+ int mp_sysex;
+ int mp_gotbyte1;
+ int mp_byte1;
+} t_midiparser;
+
+#define MIDINOTEOFF 0x80
+#define MIDINOTEON 0x90
+#define MIDIPOLYTOUCH 0xa0
+#define MIDICONTROLCHANGE 0xb0
+#define MIDIPROGRAMCHANGE 0xc0
+#define MIDICHANNELTOUCH 0xd0
+#define MIDIPITCHBEND 0xe0
+ /* functions in x_midi.c */
+void inmidi_realtimein(int portno, int cmd);
+void inmidi_byte(int portno, int byte);
+void inmidi_sysex(int portno, int byte);
+void inmidi_noteon(int portno, int channel, int pitch, int velo);
+void inmidi_controlchange(int portno, int channel, int ctlnumber, int value);
+void inmidi_programchange(int portno, int channel, int value);
+void inmidi_pitchbend(int portno, int channel, int value);
+void inmidi_aftertouch(int portno, int channel, int value);
+void inmidi_polyaftertouch(int portno, int channel, int pitch, int value);
+
+static void sys_dispatchnextmidiin( void)
+{
+ static t_midiparser parser[MAXMIDIINDEV], *parserp;
+ int portno = midi_inqueue[midi_intail].q_portno,
+ byte = midi_inqueue[midi_intail].q_byte1;
+ if (!midi_inqueue[midi_intail].q_onebyte)
+ bug("sys_dispatchnextmidiin");
+ if (portno < 0 || portno >= MAXMIDIINDEV)
+ bug("sys_dispatchnextmidiin 2");
+ parserp = parser + portno;
+ outlet_setstacklim();
+
+ if (byte >= 0xf8)
+ inmidi_realtimein(portno, byte);
+ else
+ {
+ inmidi_byte(portno, byte);
+ if (byte < 0xf0)
+ {
+ if (byte & 0x80)
+ {
+ parserp->mp_status = byte;
+ parserp->mp_gotbyte1 = 0;
+ }
+ else
+ {
+ int cmd = (parserp->mp_status & 0xf0);
+ int chan = (parserp->mp_status & 0xf);
+ int byte1 = parserp->mp_byte1, gotbyte1 = parserp->mp_gotbyte1;
+ switch (cmd)
+ {
+ case MIDINOTEOFF:
+ if (gotbyte1)
+ inmidi_noteon(portno, chan, byte1, 0),
+ parserp->mp_gotbyte1 = 0;
+ else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1;
+ break;
+ case MIDINOTEON:
+ if (gotbyte1)
+ inmidi_noteon(portno, chan, byte1, byte),
+ parserp->mp_gotbyte1 = 0;
+ else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1;
+ break;
+ case MIDIPOLYTOUCH:
+ if (gotbyte1)
+ inmidi_polyaftertouch(portno, chan, byte1, byte),
+ parserp->mp_gotbyte1 = 0;
+ else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1;
+ break;
+ case MIDICONTROLCHANGE:
+ if (gotbyte1)
+ inmidi_controlchange(portno, chan, byte1, byte),
+ parserp->mp_gotbyte1 = 0;
+ else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1;
+ break;
+ case MIDIPROGRAMCHANGE:
+ inmidi_programchange(portno, chan, byte);
+ break;
+ case MIDICHANNELTOUCH:
+ inmidi_aftertouch(portno, chan, byte);
+ break;
+ case MIDIPITCHBEND:
+ if (gotbyte1)
+ inmidi_pitchbend(portno, chan, ((byte << 7) + byte1)),
+ parserp->mp_gotbyte1 = 0;
+ else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1;
+ break;
+ }
+ }
+ }
+ }
+ midi_intail = (midi_intail + 1 == MIDIQSIZE ? 0 : midi_intail + 1);
+}
+
+void sys_pollmidiinqueue( void)
+{
+#ifdef TEST_DEJITTER
+ static int db = 0;
+#endif
+ double logicaltime = .001 * clock_gettimesince(sys_midiinittime);
+#ifdef TEST_DEJITTER
+ if (midi_inhead == midi_intail)
+ db = 0;
+#endif
+ while (midi_inhead != midi_intail)
+ {
+#ifdef TEST_DEJITTER
+ if (!db)
+ {
+ post("in del %f, logicaltime %f, RT %f adcminusRT %f",
+ (midi_inqueue[midi_intail].q_time - logicaltime),
+ logicaltime, sys_getrealtime(), sys_adctimeminusrealtime);
+ db = 1;
+ }
+#endif
+#if 0
+ if (midi_inqueue[midi_intail].q_time <= logicaltime - 0.007)
+ post("late %f",
+ 1000 * (logicaltime - midi_inqueue[midi_intail].q_time));
+#endif
+ if (midi_inqueue[midi_intail].q_time <= logicaltime)
+ {
+#if 0
+ post("diff %f",
+ 1000* (logicaltime - midi_inqueue[midi_intail].q_time));
+#endif
+ sys_dispatchnextmidiin();
+ }
+ else break;
+ }
+}
+
+ /* this should be called from the system dependent MIDI code when a byte
+ comes in, as a result of our calling sys_poll_midi. We stick it on a
+ timetag queue and dispatch it at the appropriate logical time. */
+
+void sys_midibytein(int portno, int byte)
+{
+ t_midiqelem *midiqelem;
+ int newhead = midi_inhead +1;
+ if (newhead == MIDIQSIZE)
+ newhead = 0;
+ /* if FIFO is full flush an element to make room */
+ if (newhead == midi_intail)
+ post("flush"), sys_dispatchnextmidiin();
+ midi_inqueue[midi_inhead].q_portno = portno;
+ midi_inqueue[midi_inhead].q_onebyte = 1;
+ midi_inqueue[midi_inhead].q_byte1 = byte;
+ midi_inqueue[midi_inhead].q_time = sys_getmidiinrealtime();
+ midi_inhead = newhead;
+ sys_pollmidiinqueue();
+}
+
+void sys_pollmidiqueue( void)
+{
+#if 0
+ static double lasttime;
+ double newtime = sys_getrealtime();
+ if (newtime - lasttime > 0.007)
+ post("delay %d", (int)(1000 * (newtime - lasttime)));
+ lasttime = newtime;
+#endif
+ sys_poll_midi(); /* OS dependent poll for MIDI input */
+ sys_pollmidioutqueue();
+ sys_pollmidiinqueue();
+}
+
diff --git a/pd/src/s_watchdog.c b/pd/src/s_watchdog.c
new file mode 100644
index 00000000..a2122824
--- /dev/null
+++ b/pd/src/s_watchdog.c
@@ -0,0 +1,47 @@
+/* Copyright (c) 1997-2000 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* This file is compiled into the separate program, "pd-watchdog," which
+tries to prevent Pd from locking up the processor if it's at realtime
+priority. Linux only. Invoked from s_inter.c. */
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ int happy = 1;
+ while (1)
+ {
+ struct timeval timout;
+ fd_set readset;
+ if (happy)
+ {
+ timout.tv_sec = 5;
+ timout.tv_usec = 0;
+ }
+ else
+ {
+ timout.tv_sec = 2;
+ timout.tv_usec = 0;
+ }
+ FD_ZERO(&readset);
+ FD_SET(0, &readset);
+ select(1, &readset, 0, 0, &timout);
+ if (FD_ISSET(0, &readset))
+ {
+ char buf[100];
+ happy = 1;
+ if (read(0, &buf, 100) <= 0)
+ return (0);
+ else continue;
+ }
+ happy = 0;
+ kill(getppid(), SIGHUP);
+ fprintf(stderr, "watchdog: signaling pd...\n");
+ }
+}
diff --git a/pd/src/t_main.c b/pd/src/t_main.c
new file mode 100644
index 00000000..d4d48c63
--- /dev/null
+++ b/pd/src/t_main.c
@@ -0,0 +1,123 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* This file should be compared with the corresponding thing in the TK
+* distribution whenever updating to newer versions of TCL/TK. */
+
+/*
+ * Copyright (c) 1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+
+#ifndef MACOSX /* linux and IRIX only; in MACOSX we don't link this in */
+#include "tk.h"
+#include <stdlib.h>
+
+/*
+ * The following variable is a special hack that is needed in order for
+ * Sun shared libraries to be used for Tcl.
+ */
+
+extern int matherr(void);
+int *tclDummyMathPtr = (int *) matherr;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * main --
+ *
+ * This is the main program for the application.
+ *
+ * Results:
+ * None: Tk_Main never returns here, so this procedure never
+ * returns either.
+ *
+ * Side effects:
+ * Whatever the application does.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void pdgui_startup(Tcl_Interp *interp);
+void pdgui_setname(char *name);
+void pdgui_setsock(int port);
+void pdgui_sethost(char *name);
+
+int
+main(int argc, char **argv)
+{
+ pdgui_setname(argv[0]);
+ if (argc >= 2)
+ {
+ pdgui_setsock(atoi(argv[1]));
+ argc--; argv++;
+ argv[0] = "Pd";
+ }
+ if (argc >= 2)
+ {
+ pdgui_sethost(argv[1]);
+ argc--; argv++;
+ argv[0] = "Pd";
+ }
+ Tk_Main(argc, argv, Tcl_AppInit);
+ return 0; /* Needed only to prevent compiler warning. */
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AppInit --
+ *
+ * This procedure performs application-specific initialization.
+ * Most applications, especially those that incorporate additional
+ * packages, will have their own version of this procedure.
+ *
+ * Results:
+ * Returns a standard Tcl completion code, and leaves an error
+ * message in interp->result if an error occurs.
+ *
+ * Side effects:
+ * Depends on the startup script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+
+int
+Tcl_AppInit(interp)
+ Tcl_Interp *interp; /* Interpreter for application. */
+{
+ Tk_Window mainwindow;
+
+ if (Tcl_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ if (Tk_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+
+ /* setup specific to pd-gui: */
+
+ pdgui_startup(interp);
+
+ /*
+ * Specify a user-specific startup file to invoke if the application
+ * is run interactively. Typically the startup file is "~/.apprc"
+ * where "app" is the name of the application. If this line is deleted
+ * then no user-specific startup file will be run under any conditions.
+ */
+
+#if 0
+ tcl_RcFileName = "~/.pdrc";
+#endif
+
+ return TCL_OK;
+}
+
+#endif /* MACOSX */
diff --git a/pd/src/t_tk.h b/pd/src/t_tk.h
new file mode 100644
index 00000000..a6943679
--- /dev/null
+++ b/pd/src/t_tk.h
@@ -0,0 +1,10 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+void pdgui_vmess(char *fmt, ...);
+void pdgui_mess(char *s);
+
+void pdgui_evalfile(char *s);
+
+#define GUISTRING 1000
diff --git a/pd/src/t_tkcmd.c b/pd/src/t_tkcmd.c
new file mode 100644
index 00000000..c2abd846
--- /dev/null
+++ b/pd/src/t_tkcmd.c
@@ -0,0 +1,367 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#ifdef UNIX /* in unix this only works first; in NT it only works last. */
+#include "tk.h"
+#endif
+
+#include "t_tk.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#ifdef UNIX
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#ifdef HAVE_BSTRING_H
+#include <bstring.h>
+#endif
+#include <sys/time.h>
+#include <errno.h>
+#endif
+#ifdef NT
+#include <winsock.h>
+#include <io.h>
+#endif
+#ifdef NT
+#pragma warning( disable : 4305 ) /* uncast const double to float */
+#pragma warning( disable : 4244 ) /* uncast double to float */
+#pragma warning( disable : 4101 ) /* unused local variables */
+#endif
+
+#ifdef NT
+#include "tk.h"
+#endif
+
+void tcl_mess(char *s);
+
+/***************** the socket setup code ********************/
+
+static int portno = 5400;
+
+ /* some installations of linux don't know about "localhost" so give
+ the loopback address; NT, on the other hand, can't understand the
+ hostname "127.0.0.1". */
+char hostname[100] =
+#ifdef __linux__
+ "127.0.0.1";
+#else
+ "localhost";
+#endif
+
+void pdgui_setsock(int port)
+{
+ portno = port;
+}
+
+void pdgui_sethost(char *name)
+{
+ strncpy(hostname, name, 100);
+ hostname[99] = 0;
+}
+
+static void pdgui_sockerror(char *s)
+{
+#ifdef NT
+ int err = WSAGetLastError();
+#endif
+#ifdef UNIX
+ int err = errno;
+#endif
+
+ fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err);
+ tcl_mess("exit\n");
+ exit(1);
+}
+
+static int sockfd;
+
+/* The "pd_suck" command, which polls the socket.
+ FIXME: if Pd sends something bigger than SOCKSIZE we're in trouble!
+ This has to be set bigger than any array update message for instance.
+*/
+#define SOCKSIZE 20000
+
+static char pd_tkbuf[SOCKSIZE+1];
+int pd_spillbytes = 0;
+
+static void pd_readsocket(ClientData cd, int mask)
+{
+ int ngot;
+ fd_set readset, writeset, exceptset;
+ struct timeval timout;
+
+ timout.tv_sec = 0;
+ timout.tv_usec = 0;
+ FD_ZERO(&writeset);
+ FD_ZERO(&readset);
+ FD_ZERO(&exceptset);
+ FD_SET(sockfd, &readset);
+ FD_SET(sockfd, &exceptset);
+
+ if (select(sockfd+1, &readset, &writeset, &exceptset, &timout) < 0)
+ perror("select");
+ if (FD_ISSET(sockfd, &exceptset) || FD_ISSET(sockfd, &readset))
+ {
+ int ret;
+ ret = recv(sockfd, pd_tkbuf + pd_spillbytes,
+ SOCKSIZE - pd_spillbytes, 0);
+ if (ret < 0) pdgui_sockerror("socket receive error");
+ else if (ret == 0)
+ {
+ /* fprintf(stderr, "read %d\n", SOCKSIZE - pd_spillbytes); */
+ fprintf(stderr, "pd_gui: pd process exited\n");
+ tcl_mess("exit\n");
+ }
+ else
+ {
+ char *lastcr = 0, *bp = pd_tkbuf, *ep = bp + (pd_spillbytes + ret);
+ int brace = 0;
+ char lastc = 0;
+ while (bp < ep)
+ {
+ char c = *bp;
+ if (c == '}' && brace) brace--;
+ else if (c == '{') brace++;
+ else if (!brace && c == '\n' && lastc != '\\') lastcr = bp;
+ lastc = c;
+ bp++;
+ }
+ if (lastcr)
+ {
+ int xtra = pd_tkbuf + pd_spillbytes + ret - (lastcr+1);
+ char bashwas = lastcr[1];
+ lastcr[1] = 0;
+ tcl_mess(pd_tkbuf);
+ lastcr[1] = bashwas;
+ if (xtra)
+ {
+ /* fprintf(stderr, "x %d\n", xtra); */
+ memmove(pd_tkbuf, lastcr+1, xtra);
+ }
+ pd_spillbytes = xtra;
+ }
+ else
+ {
+ pd_spillbytes += ret;
+ }
+ }
+ }
+}
+
+#ifndef UNIX
+ /* if we aren't UNIX, we add a tcl command to poll the
+ socket for data. */
+static int pd_pollsocketCmd(ClientData cd, Tcl_Interp *interp,
+ int argc, char **argv)
+{
+ pd_readsocket(cd, 0);
+ return (TCL_OK);
+}
+#endif
+
+void pdgui_setupsocket(void)
+{
+ struct sockaddr_in server;
+ struct hostent *hp;
+
+#ifdef NT
+ short version = MAKEWORD(2, 0);
+ WSADATA nobby;
+
+ if (WSAStartup(version, &nobby)) pdgui_sockerror("setup");
+#endif
+
+ /* create a socket */
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) pdgui_sockerror("socket");
+
+ /* connect socket using hostname provided in command line */
+ server.sin_family = AF_INET;
+
+ hp = gethostbyname(hostname);
+
+ if (hp == 0)
+ {
+ fprintf(stderr,
+ "localhost not found (inet protocol not installed?)\n");
+ exit(1);
+ }
+ memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+
+ /* assign client port number */
+ server.sin_port = htons((unsigned short)portno);
+
+ /* try to connect */
+ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0)
+ pdgui_sockerror("connecting stream socket");
+
+#ifdef UNIX
+ /* in unix we ask TK to call us back. In NT we have to poll. */
+ Tk_CreateFileHandler(sockfd, TK_READABLE | TK_EXCEPTION,
+ pd_readsocket, 0);
+#endif
+}
+
+/**************************** commands ************************/
+static char *pdgui_path;
+
+/* The "pd" command, which cats its args together and throws the result
+* at the Pd interpreter.
+*/
+#define MAXWRITE 1024
+
+static int pdCmd(ClientData cd, Tcl_Interp *interp, int argc, char **argv)
+{
+ if (argc == 2)
+ {
+ int n = strlen(argv[1]);
+ if (send(sockfd, argv[1], n, 0) < n)
+ {
+ perror("stdout");
+ tcl_mess("exit\n");
+ }
+ }
+ else
+ {
+ int i;
+ char buf[MAXWRITE];
+ buf[0] = 0;
+ for (i = 1; i < argc; i++)
+ {
+ if (strlen(argv[i]) + strlen(buf) + 2 > MAXWRITE)
+ {
+ interp->result = "pd: arg list too long";
+ return (TCL_ERROR);
+ }
+ if (i > 1) strcat(buf, " ");
+ strcat(buf, argv[i]);
+ }
+ if (send(sockfd, buf, strlen(buf), 0) < 0)
+ {
+ perror("stdout");
+ tcl_mess("exit\n");
+ }
+ }
+ return (TCL_OK);
+}
+
+/*********** "c" level access to tk functions. ******************/
+
+static Tcl_Interp *tk_myinterp;
+
+void tcl_mess(char *s)
+{
+ int result;
+ result = Tcl_Eval(tk_myinterp, s);
+ if (result != TCL_OK)
+ {
+ if (*tk_myinterp->result) printf("%s\n", tk_myinterp->result);
+ }
+}
+
+/* LATER should do a bounds check -- but how do you get printf to do that? */
+void tcl_vmess(char *fmt, ...)
+{
+ int result, i;
+ char buf[MAXWRITE];
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ vsprintf(buf, fmt, ap);
+ result = Tcl_Eval(tk_myinterp, buf);
+ if (result != TCL_OK)
+ {
+ if (*tk_myinterp->result) printf("%s\n", tk_myinterp->result);
+ }
+ va_end(ap);
+}
+
+#ifdef UNIX
+void pdgui_doevalfile(Tcl_Interp *interp, char *s)
+{
+ char buf[GUISTRING];
+ sprintf(buf, "set pd_guidir \"%s\"\n", pdgui_path);
+ tcl_mess(buf);
+ strcpy(buf, pdgui_path);
+ strcat(buf, "/bin/");
+ strcat(buf, s);
+ if (Tcl_EvalFile(interp, buf) != TCL_OK)
+ {
+ char buf2[1000];
+ sprintf(buf2, "puts [concat tcl: %s: can't open script]\n",
+ buf);
+ tcl_mess(buf2);
+ }
+}
+
+void pdgui_evalfile(char *s)
+{
+ pdgui_doevalfile(tk_myinterp, s);
+}
+#endif
+
+void pdgui_startup(Tcl_Interp *interp)
+{
+
+ /* save pointer to the main interpreter */
+ tk_myinterp = interp;
+
+
+ /* add our own TK commands */
+ Tcl_CreateCommand(interp, "pd", pdCmd, (ClientData)NULL,
+ (Tcl_CmdDeleteProc *)NULL);
+#ifndef UNIX
+ Tcl_CreateCommand(interp, "pd_pollsocket", pd_pollsocketCmd,
+ (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
+#endif
+ pdgui_setupsocket();
+
+ /* read in the startup file */
+#if !defined(NT) && !defined(MACOSX)
+ pdgui_evalfile("pd.tk");
+#endif
+}
+
+#ifdef UNIX
+void pdgui_setname(char *s)
+{
+ char *t;
+ char *str;
+ int n;
+ if (t = strrchr(s, '/')) str = s, n = (t-s) + 1;
+ else str = "./", n = 2;
+ if (n > GUISTRING-100) n = GUISTRING-100;
+ pdgui_path = malloc(n+9);
+
+ strncpy(pdgui_path, str, n);
+ while (strlen(pdgui_path) > 0 && pdgui_path[strlen(pdgui_path)-1] == '/')
+ pdgui_path[strlen(pdgui_path)-1] = 0;
+ if (t = strrchr(pdgui_path, '/'))
+ *t = 0;
+}
+#endif
+
+int Pdtcl_Init(Tcl_Interp *interp)
+{
+ char *myvalue = Tcl_GetVar(interp, "argv", 0);
+ int myportno;
+ if (myvalue && (myportno = atoi(myvalue)) > 1)
+ pdgui_setsock(myportno);
+ tk_myinterp = interp;
+ pdgui_startup(interp);
+ interp->result = "loaded pdtcl_init";
+
+ return (TCL_OK);
+}
+
+int Pdtcl_SafeInit(Tcl_Interp *interp) {
+ fprintf(stderr, "Pdtcl_Safeinit 51\n");
+ return (TCL_OK);
+}
+
diff --git a/pd/src/u_main.tk b/pd/src/u_main.tk
new file mode 100644
index 00000000..a19b0951
--- /dev/null
+++ b/pd/src/u_main.tk
@@ -0,0 +1,2570 @@
+set pd_nt 0
+# (The above is 0 for unix, 1 for microsoft, and 2 for Mac OSX. The first
+# line is automatically munged by the relevant makefiles.)
+
+# Copyright (c) 1997-1999 Miller Puckette.
+# For information on usage and redistribution, and for a DISCLAIMER OF ALL
+# WARRANTIES, see the file, "LICENSE.txt," in this distribution.
+
+# changed by Thomas Musil 09.2001
+# between "pdtk_graph_dialog -- dialog window for graphs"
+# and "pdtk_array_dialog -- dialog window for arrays"
+# a new dialogbox was inserted, named:
+# "pdtk_iemgui_dialog -- dialog window for iem guis"
+#
+# there are 2 new features: 1.) line-delete-protection in edit-menue
+#
+# 2.) there are all iem-guis in a seperated put-gui-menue
+#
+# all this changes are labeled with #######iemlib##########
+
+if {$pd_nt == 1} {
+ global pd_guidir
+ set pd_gui2 [string range $argv0 0 [expr [string last \\ $argv0 ] - 1]]
+ regsub -all \\\\ $pd_gui2 / pd_gui3
+ set pd_guidir $pd_gui3/..
+ load $pd_guidir/bin/pdtcl
+}
+
+if {$pd_nt == 2} {
+ global pd_guidir
+ set pd_gui2 [string range $argv0 0 [expr [string last / $argv0 ] - 1]]
+ set pd_guidir $pd_gui2/..
+ load $pd_guidir/bin/pdtcl
+}
+
+# it's unfortunate but we seem to have to turn off global bindings
+# for Text objects to get control-s and control-t to do what we want for
+# "text" dialogs below. Also we have to get rid of tab's changing the focus.
+
+bind all <Key-Tab> ""
+bind all <Shift-Key-Tab> ""
+bind Text <Control-t> {}
+bind Text <Control-s> {}
+# puts stderr [bind all]
+
+################## set up main window #########################
+frame .mbar -relief raised -bd 2
+canvas .dummy -height 1c -width 1c
+frame .controls
+pack .mbar .controls .dummy -side top -fill x
+menubutton .mbar.file -text File -menu .mbar.file.menu
+menubutton .mbar.find -text Find -menu .mbar.find.menu
+menubutton .mbar.windows -text Windows -menu .mbar.windows.menu
+menubutton .mbar.audio -text Audio -menu .mbar.audio.menu
+menubutton .mbar.help -text Help -menu .mbar.help.menu
+pack .mbar.file .mbar.find .mbar.windows .mbar.audio -side left
+pack .mbar.help -side right
+menu .mbar.file.menu
+menu .mbar.find.menu
+menu .mbar.windows.menu -postcommand [concat pdtk_fixwindowmenu]
+menu .mbar.audio.menu
+menu .mbar.help.menu
+
+set ctrls_audio_on 0
+set ctrls_meter_on 0
+set ctrls_inlevel 0
+set ctrls_outlevel 0
+
+frame .controls.switches
+checkbutton .controls.switches.audiobutton -text {compute audio} \
+ -variable ctrls_audio_on \
+ -anchor w \
+ -command {pd [concat pd dsp $ctrls_audio_on \;]}
+
+checkbutton .controls.switches.meterbutton -text {peak meters} \
+ -variable ctrls_meter_on \
+ -anchor w \
+ -command {pd [concat pd meters $ctrls_meter_on \;]}
+
+pack .controls.switches.meterbutton .controls.switches.audiobutton -side left
+
+frame .controls.in
+label .controls.in.label -text IN
+entry .controls.in.level -textvariable ctrls_inlevel -width 3
+button .controls.in.clip -text {CLIP} -state disabled
+pack .controls.in.label .controls.in.level .controls.in.clip -side top
+
+frame .controls.out
+label .controls.out.label -text OUT
+entry .controls.out.level -textvariable ctrls_outlevel -width 3
+button .controls.out.clip -text {CLIP} -state disabled
+pack .controls.out.label .controls.out.level .controls.out.clip -side top
+
+button .controls.dio -text "DIO\nerrors" \
+ -command {pd [concat pd audiostatus \;]}
+
+pack .controls.switches -side bottom
+pack .controls.in .controls.out -side left
+pack .controls.dio -side right
+
+bind . <Control-Key> {pdtk_pd_ctrlkey %W %K 0}
+bind . <Control-Shift-Key> {pdtk_pd_ctrlkey %W %K 1}
+
+
+############### set up global variables ################################
+
+set untitled_number 1
+set untitled_directory [pwd]
+set saveas_client doggy
+set pd_opendir $untitled_directory
+############iemlib##################
+# need it to know, if new or open file
+set iem_new_open_flag "open"
+############iemlib##################
+
+################ utility functions #########################
+
+proc pdtk_enquote {x} {
+ set foo [string map {"," "" ";" "" \" ""} $x]
+ set foo2 [string map {" " "\\ "} $foo]
+ concat $foo2
+}
+
+proc pdtk_debug {x} {
+ tk_messageBox -message $x -type ok
+}
+
+proc pdtk_watchdog {} {
+ pd [concat pd ping \;]
+ after 2000 {pdtk_watchdog}
+}
+
+proc pdtk_check {x message} {
+ set answer [tk_messageBox \-message $x \-type yesno \-icon question]
+ switch $answer {
+ yes {pd $message} }
+# no {tk_messageBox \-message "cancelled" \-type ok}
+}
+
+set menu_windowlist {}
+
+proc pdtk_fixwindowmenu {} {
+ global menu_windowlist
+ .mbar.windows.menu delete 0 end
+ foreach i $menu_windowlist {
+ .mbar.windows.menu add command -label [lindex $i 0] \
+ -command [concat menu_domenuwindow [lindex $i 1]]
+ }
+}
+
+############### the "New" menu command ########################
+proc menu_new {} {
+ global untitled_number
+ global untitled_directory
+############iemlib##################
+ global iem_new_open_flag
+
+ set iem_new_open_flag "new"
+############iemlib##################
+ pd [concat pd filename Untitled-$untitled_number $untitled_directory \;]
+ pd {
+ #N canvas;
+ #X pop 1;
+ }
+ set untitled_number [expr $untitled_number + 1]
+}
+
+################## the "Open" menu command #########################
+
+proc menu_open {} {
+ global pd_opendir
+ global pd_nt
+############iemlib##################
+ global iem_new_open_flag
+
+ set iem_new_open_flag "open"
+############iemlib##################
+
+# workaround -- initialdir doesn't work on MACOSX yet ---
+ if {$pd_nt == 2} {
+ cd $pd_opendir
+ set filename [tk_getOpenFile -defaultextension .pd \
+ -filetypes { {{pd files} {.pd}} {{max files} {.pat}}} ]
+ } else {
+ set filename [tk_getOpenFile -defaultextension .pd \
+ -filetypes { {{pd files} {.pd}} {{max files} {.pat}}} \
+ -initialdir $pd_opendir]
+ }
+# puts stderr $filename
+ if {$filename != ""} {
+ set directory [string range $filename 0 \
+ [expr [string last / $filename ] - 1]]
+ set pd_opendir $directory
+ set basename [string range $filename \
+ [expr [string last / $filename ] + 1] end]
+
+# pd_debug [concat file $filename base $basename dir $directory]
+
+ pd [concat pd open [pdtk_enquote $basename] \
+ [pdtk_enquote $directory]\;]
+ }
+}
+
+################## the "Message" menu command #########################
+proc menu_send {} {
+ toplevel .sendpanel
+ entry .sendpanel.entry -textvariable send_textvariable
+ pack .sendpanel.entry -side bottom -fill both -ipadx 100
+ .sendpanel.entry select from 0
+ .sendpanel.entry select adjust end
+ bind .sendpanel.entry <KeyPress-Return> {
+ pd [concat $send_textvariable \;]
+ after 50 {destroy .sendpanel}
+ }
+ focus .sendpanel.entry
+}
+
+################## the "Quit" menu command #########################
+proc menu_really_quit {} {pd {pd quit;}}
+
+proc menu_quit {} {pdtk_check {Really quit?} {pd quit;}}
+
+######### the "Pd" menu command, which puts the Pd window on top ########
+proc menu_pop_pd {} {raise .}
+
+######### the "audio" menu command ###############
+proc menu_audio {flag} {pd [concat pd dsp $flag \;]}
+
+######### the "documentation" menu command ###############
+
+set doc_number 1
+
+proc menu_opentext {filename} {
+ global doc_number
+ global pd_guidir
+ global pd_myversion
+ set name [format ".help%d" $doc_number]
+ toplevel $name
+ text $name.text -relief raised -bd 2 -font -*-courier-bold--normal--12-* \
+ -yscrollcommand "$name.scroll set" -background white
+ scrollbar $name.scroll -command "$name.text yview"
+ pack $name.scroll -side right -fill y
+ pack $name.text -side left -fill both -expand 1
+
+ set f [open $filename]
+ while {![eof $f]} {
+ set bigstring [read $f 1000]
+ regsub -all PD_BASEDIR $bigstring $pd_guidir bigstring2
+ regsub -all PD_VERSION $bigstring2 $pd_myversion bigstring3
+ $name.text insert end $bigstring3
+ }
+ close $f
+ set doc_number [expr $doc_number + 1]
+}
+
+set help_directory $pd_guidir/doc
+
+proc menu_documentation {} {
+ global help_directory
+ global pd_nt
+############iemlib##################
+ global iem_new_open_flag
+
+ set iem_new_open_flag "open"
+############iemlib##################
+
+ if {$pd_nt == 2} {
+ cd $help_directory
+ set filename [tk_getOpenFile -defaultextension .pd \
+ -filetypes { {{documentation} {.pd .txt .htm}} } ]
+ } else {
+ set filename [tk_getOpenFile -defaultextension .pd \
+ -filetypes { {{documentation} {.pd .txt .htm}} } \
+ -initialdir $help_directory]
+ }
+
+ if {$filename != ""} {
+ if {[string first .txt $filename] >= 0} {
+ menu_opentext $filename
+ } elseif {[string first .htm $filename] >= 0} {
+ if {$pd_nt == 0} {
+#I wish I could get this to run in the background; the "&" doesn't do it:
+ exec sh -c \
+ [format "mozilla file:%s || netscape file:%s &\n" \
+ $filename $filename]
+ } else {
+ tk_messageBox -message \
+ {sorry -- can't open htm files yet; open this manually} \
+ -type ok
+ }
+ } else {
+ set help_directory [string range $filename 0 \
+ [expr [string last / $filename ] - 1]]
+ set basename [string range $filename \
+ [expr [string last / $filename ] + 1] end]
+ pd [concat pd open [pdtk_enquote $basename] \
+ [pdtk_enquote $help_directory] \;]
+ }
+ }
+}
+
+proc menu_doc_open {subdir basename} {
+ global pd_guidir
+############iemlib##################
+ global iem_new_open_flag
+
+ set iem_new_open_flag "open"
+############iemlib##################
+
+ set dirname $pd_guidir/$subdir
+
+ if {[string first .txt $basename] >= 0} {
+ menu_opentext $dirname/$basename
+ } else {
+ pd [concat pd open [pdtk_enquote $basename] \
+ [pdtk_enquote $dirname] \;]
+ }
+}
+
+#################### the "File" menu for the Pd window ##############
+.mbar.file.menu add command -label New -command {menu_new} \
+ -accelerator "Ctrl+n"
+.mbar.file.menu add command -label Open -command {menu_open} \
+ -accelerator "Ctrl+o"
+.mbar.file.menu add command -label Message -command {menu_send} \
+ -accelerator "Ctrl+m"
+.mbar.file.menu add separator
+.mbar.file.menu add command -label Quit -command {menu_quit} \
+ -accelerator "Ctrl+q"
+
+#################### the "Find" menu for the Pd window ##############
+.mbar.find.menu add command -label {last error?} -command {menu_finderror}
+
+#################### the "Audio" menu for the Pd window ##############
+.mbar.audio.menu add command -label On -accelerator "Ctrl+/" \
+ -command {menu_audio 1}
+.mbar.audio.menu add command -label Off -accelerator "Ctrl+." \
+ -command {menu_audio 0}
+
+#################### the "Help" menu for the Pd window ##############
+.mbar.help.menu add command -label {About Pd} \
+ -command {menu_doc_open doc/1.manual 1.introduction.txt}
+.mbar.help.menu add command -label {Test Audio and MIDI} \
+ -command {menu_doc_open doc/7.stuff/tools testtone.pd}
+.mbar.help.menu add command -label {Load Meter} \
+ -command {menu_doc_open doc/7.stuff/tools load-meter.pd}
+.mbar.help.menu add command -label {Pure Documentation...} \
+ -command {menu_documentation}
+
+########### functions for menu functions on document windows ########
+
+proc menu_save {name} {
+ pdtk_canvas_checkgeometry $name
+ pd [concat $name menusave \;]
+}
+
+proc menu_saveas {name} {
+ pdtk_canvas_checkgeometry $name
+ pd [concat $name menusaveas \;]
+}
+
+proc menu_print {name} {
+ $name.c postscript -file x.ps
+}
+
+proc menu_close {name} {
+ pd [concat $name menuclose \;]
+}
+
+proc menu_cut {name} {
+ pd [concat $name cut \;]
+}
+
+proc menu_copy {name} {
+ pd [concat $name copy \;]
+}
+
+proc menu_paste {name} {
+ pd [concat $name paste \;]
+}
+
+proc menu_duplicate {name} {
+ pd [concat $name duplicate \;]
+}
+
+proc menu_selectall {name} {
+ pd [concat $name selectall \;]
+}
+
+proc menu_texteditor {name} {
+ pd [concat $name texteditor \;]
+}
+
+proc menu_font {name} {
+ pd [concat $name menufont \;]
+}
+
+proc menu_tidyup {name} {
+ pd [concat $name tidy \;]
+}
+
+proc menu_editmode {name} {
+ pd [concat $name editmode 0 \;]
+}
+
+proc menu_object {name accel} {
+ pd [concat $name obj $accel \;]
+}
+
+proc menu_message {name accel} {
+ pd [concat $name msg $accel \;]
+}
+
+proc menu_floatatom {name accel} {
+ pd [concat $name floatatom $accel \;]
+}
+
+proc menu_symbolatom {name accel} {
+ pd [concat $name symbolatom $accel \;]
+}
+
+proc menu_comment {name accel} {
+ pd [concat $name text $accel \;]
+}
+
+proc menu_graph {name} {
+ pd [concat $name graph \;]
+}
+
+proc menu_array {name} {
+ pd [concat $name menuarray \;]
+}
+
+############iemlib##################
+proc menu_bng {name accel} {
+ pd [concat $name bng $accel \;]
+}
+
+proc menu_toggle {name accel} {
+ pd [concat $name toggle $accel \;]
+}
+
+proc menu_numbox {name accel} {
+ pd [concat $name numbox $accel \;]
+}
+
+proc menu_vslider {name accel} {
+ pd [concat $name vslider $accel \;]
+}
+
+proc menu_hslider {name accel} {
+ pd [concat $name hslider $accel \;]
+}
+
+proc menu_hdial {name accel} {
+ pd [concat $name hdial $accel \;]
+}
+
+proc menu_vdial {name accel} {
+ pd [concat $name vdial $accel \;]
+}
+
+proc menu_vumeter {name accel} {
+ pd [concat $name vumeter $accel \;]
+}
+
+proc menu_mycnv {name accel} {
+ pd [concat $name mycnv $accel \;]
+}
+
+proc menu_protectmode {name} {
+ pd [concat $name protectmode 0 \;]
+}
+
+############iemlib##################
+
+proc menu_windowparent {name} {
+ pd [concat $name findparent \;]
+}
+
+proc menu_findagain {name} {
+ pd [concat $name findagain \;]
+}
+
+proc menu_finderror {} {
+ pd [concat pd finderror \;]
+}
+
+proc menu_domenuwindow {i} {
+ raise $i
+}
+
+proc menu_fixwindowmenu {name} {
+ global menu_windowlist
+ $name.m.windows.m add command
+ $name.m.windows.m delete 4 end
+ foreach i $menu_windowlist {
+ $name.m.windows.m add command -label [lindex $i 0] \
+ -command [concat menu_domenuwindow [lindex $i 1]]
+ }
+}
+
+################## the "find" menu item ###################
+
+set find_canvas nobody
+set find_string ""
+set find_count 1
+
+proc find_apply {name} {
+ global find_string
+ global find_canvas
+ regsub -all \; $find_string " _semi_ " find_string2
+ regsub -all \, $find_string2 " _comma_ " find_string3
+# puts stderr [concat $find_canvas find $find_string3 \
+# \;]
+ pd [concat $find_canvas find $find_string3 \
+ \;]
+ after 50 destroy $name
+}
+
+proc find_cancel {name} {
+ after 50 destroy $name
+}
+
+proc menu_findobject {canvas} {
+ global find_string
+ global find_canvas
+ global find_count
+
+ set name [format ".find%d" $find_count]
+ set find_count [expr $find_count + 1]
+
+ set find_canvas $canvas
+
+ toplevel $name
+
+ label $name.label -text {find...}
+ pack $name.label -side top
+
+ entry $name.entry -textvariable find_string
+ pack $name.entry -side top
+
+ frame $name.buttonframe
+ pack $name.buttonframe -side bottom -fill x -pady 2m
+ button $name.buttonframe.cancel -text {Cancel}\
+ -command "find_cancel $name"
+ button $name.buttonframe.ok -text {OK}\
+ -command "find_apply $name"
+ pack $name.buttonframe.cancel -side left -expand 1
+ pack $name.buttonframe.ok -side left -expand 1
+
+ $name.entry select from 0
+ $name.entry select adjust end
+ bind $name.entry <KeyPress-Return> [ concat find_apply $name]
+ focus $name.entry
+}
+
+
+############# pdtk_canvas_new -- create a new canvas ###############
+proc pdtk_canvas_new {name width height geometry} {
+ global pd_opendir
+ global iem_new_open_flag
+
+ toplevel $name
+ frame $name.m -relief raised -bd 2
+# puts stderr [concat geometry: $geometry]
+ wm geometry $name $geometry
+ canvas $name.c -width $width -height $height -background white \
+ -yscrollcommand "$name.scrollvert set" \
+ -xscrollcommand "$name.scrollhort set" \
+ -scrollregion [concat 0 0 $width $height]
+
+ scrollbar $name.scrollvert -command "$name.c yview"
+ scrollbar $name.scrollhort -command "$name.c xview" \
+ -orient horizontal
+
+ pack $name.m -side top -fill x
+ pack $name.scrollhort -side bottom -fill x
+ pack $name.scrollvert -side right -fill y
+ pack $name.c -side left -expand 1 -fill both
+ wm minsize $name 1 1
+ wm geometry $name $geometry
+
+# the file menu
+
+ menubutton $name.m.file -text File -menu $name.m.file.m
+ pack $name.m.file -side left
+ menu $name.m.file.m
+
+ $name.m.file.m add command -label New -command {menu_new} \
+ -accelerator "Ctrl+n"
+
+ $name.m.file.m add command -label Open -command {menu_open} \
+ -accelerator "Ctrl+o"
+
+ $name.m.file.m add command -label Message -command {menu_send} \
+ -accelerator "Ctrl+m"
+
+ $name.m.file.m add separator
+ $name.m.file.m add command -label Save -command [concat menu_save $name] \
+ -accelerator "Ctrl+s"
+
+ $name.m.file.m add command -label Close \
+ -command [concat menu_close $name] \
+ -accelerator "Ctrl+w"
+
+ $name.m.file.m add command -label "Save as..." \
+ -command [concat menu_saveas $name] \
+ -accelerator "Ctrl+S"
+
+ $name.m.file.m add command -label Print -command [concat menu_print $name] \
+ -accelerator "Ctrl+p"
+
+ $name.m.file.m add separator
+
+ $name.m.file.m add command -label Quit -command {menu_quit} \
+ -accelerator "Ctrl+q"
+
+# the edit menu
+ menubutton $name.m.edit -text Edit -menu $name.m.edit.m
+ pack $name.m.edit -side left
+ menu $name.m.edit.m
+
+
+ $name.m.edit.m add command -label Cut -command [concat menu_cut $name] \
+ -accelerator "Ctrl+x"
+
+ $name.m.edit.m add command -label Copy -command [concat menu_copy $name] \
+ -accelerator "Ctrl+c"
+
+ $name.m.edit.m add command -label Paste \
+ -command [concat menu_paste $name] \
+ -accelerator "Ctrl+v"
+
+ $name.m.edit.m add command -label Duplicate \
+ -command [concat menu_duplicate $name] \
+ -accelerator "Ctrl+d"
+
+ $name.m.edit.m add command -label {Select all} \
+ -command [concat menu_selectall $name] \
+ -accelerator "Ctrl+a"
+
+ $name.m.edit.m add command -label {Text Editor} \
+ -command [concat menu_texteditor $name] \
+ -accelerator "Ctrl+t"
+
+ $name.m.edit.m add command -label Font \
+ -command [concat menu_font $name]
+
+ $name.m.edit.m add command -label {Tidy Up} \
+ -command [concat menu_tidyup $name]
+
+ $name.m.edit.m add separator
+
+############iemlib##################
+# instead of "red = #BC3C60" we take "grey85", so there is no difference,
+# if widget is selected or not.
+
+ $name.m.edit.m add checkbutton -label "Edit mode" \
+ -indicatoron true -selectcolor grey85 \
+ -command [concat menu_editmode $name] \
+ -accelerator "Ctrl+e"
+
+
+
+ $name.m.edit.m add checkbutton -label "Protect" \
+ -indicatoron true -selectcolor grey85 \
+ -command [concat menu_protectmode $name] \
+ -accelerator "Ctrl+r"
+
+ if { $iem_new_open_flag == "open" } {
+ $name.m.edit.m entryconfigure "Edit mode" -indicatoron false }
+ $name.m.edit.m entryconfigure "Protect" -indicatoron false
+
+############iemlib##################
+
+# the put menu
+ menubutton $name.m.put -text Put -menu $name.m.put.m
+ pack $name.m.put -side left
+ menu $name.m.put.m
+
+ $name.m.put.m add command -label Object \
+ -command [concat menu_object $name 0] \
+ -accelerator "Ctrl+1"
+
+ $name.m.put.m add command -label Message \
+ -command [concat menu_message $name 0] \
+ -accelerator "Ctrl+2"
+
+ $name.m.put.m add command -label Number \
+ -command [concat menu_floatatom $name 0] \
+ -accelerator "Ctrl+3"
+
+ $name.m.put.m add command -label Symbol \
+ -command [concat menu_symbolatom $name 0] \
+ -accelerator "Ctrl+4"
+
+ $name.m.put.m add command -label Comment \
+ -command [concat menu_comment $name 0] \
+ -accelerator "Ctrl+5"
+
+############iemlib##################
+
+ $name.m.put.m add command -label Bang \
+ -command [concat menu_bng $name 0] \
+ -accelerator "Alt+b"
+
+ $name.m.put.m add command -label Toggle \
+ -command [concat menu_toggle $name 0] \
+ -accelerator "Alt+t"
+
+ $name.m.put.m add command -label Number2 \
+ -command [concat menu_numbox $name 0] \
+ -accelerator "Alt+n"
+
+ $name.m.put.m add command -label Vslider \
+ -command [concat menu_vslider $name 0] \
+ -accelerator "Alt+v"
+
+ $name.m.put.m add command -label Hslider \
+ -command [concat menu_hslider $name 0] \
+ -accelerator "Alt+h"
+
+ $name.m.put.m add command -label Vdial \
+ -command [concat menu_vdial $name 0] \
+ -accelerator "Alt+d"
+
+ $name.m.put.m add command -label Hdial \
+ -command [concat menu_hdial $name 0] \
+ -accelerator "Alt+i"
+
+ $name.m.put.m add command -label VU \
+ -command [concat menu_vumeter $name 0] \
+ -accelerator "Alt+u"
+
+ $name.m.put.m add command -label Canvas \
+ -command [concat menu_mycnv $name 0] \
+ -accelerator "Alt+c"
+
+############iemlib##################
+
+ $name.m.put.m add command -label Graph \
+ -command [concat menu_graph $name]
+
+ $name.m.put.m add command -label Array \
+ -command [concat menu_array $name]
+
+
+
+# the find menu
+ menubutton $name.m.find -text Find -menu $name.m.find.m
+ pack $name.m.find -side left
+ menu $name.m.find.m
+ $name.m.find.m add command -label {Find...} -accelerator "Ctrl+f" \
+ -command [concat menu_findobject $name]
+ $name.m.find.m add command -label {Find Again} -accelerator "Ctrl+g" \
+ -command [concat menu_findagain $name]
+ $name.m.find.m add command -label {Find last error} \
+ -command [concat menu_finderror]
+
+# the window menu
+ menubutton $name.m.windows -text Windows -menu $name.m.windows.m
+ pack $name.m.windows -side left
+ menu $name.m.windows.m -postcommand [concat menu_fixwindowmenu $name]
+ $name.m.windows.m add command -label {parent window}\
+ -command [concat menu_windowparent $name]
+ $name.m.windows.m add command -label {Pd window} -command menu_pop_pd
+ $name.m.windows.m add separator
+
+# the audio menu
+ menubutton $name.m.audio -text Audio -menu $name.m.audio.m
+ pack $name.m.audio -side left
+ menu $name.m.audio.m
+ $name.m.audio.m add command -label On -accelerator "Ctrl+/" \
+ -command {menu_audio 1}
+ $name.m.audio.m add command -label Off -accelerator "Ctrl+." \
+ -command {menu_audio 0}
+
+# the help menu
+ menubutton $name.m.help -text Help -menu $name.m.help.m
+ pack $name.m.help -side right
+ menu $name.m.help.m
+ $name.m.help.m add command -label {Getting Started} \
+ -command {menu_doc_open doc/1.manual 1.introduction.txt}
+ $name.m.help.m add command -label {Test Audio and MIDI} \
+ -command {menu_doc_open doc/7.stuff/tools testtone.pd}
+ $name.m.help.m add command -label {Load Meter} \
+ -command {menu_doc_open doc/7.stuff/tools load-meter.pd}
+ $name.m.help.m add command -label {Pure Documentation} \
+ -command {menu_documentation}
+
+# the popup menu
+ menu $name.popup -tearoff false
+ $name.popup add command -label {Properties} \
+ -command [concat popup_action $name 0]
+ $name.popup add command -label {Open} \
+ -command [concat popup_action $name 1]
+ $name.popup add command -label {Help} \
+ -command [concat popup_action $name 2]
+
+# WM protocol
+ wm protocol $name WM_DELETE_WINDOW [concat menu_close $name]
+
+# bindings.
+# this is idiotic -- how do you just sense what mod keys are down and
+# pass them on? I can't find it anywhere.
+# Here we encode shift as 1, control 2, alt 4, in agreement
+# with definitions in g_canvas.c. The third button gets "8" but we don't
+# bother with modifiers there.
+# We don't handle multiple clicks yet.
+
+ bind $name.c <Button> {pdtk_canvas_click %W %x %y %b 0}
+ bind $name.c <Shift-Button> {pdtk_canvas_click %W %x %y %b 1}
+ bind $name.c <Control-Button> {pdtk_canvas_click %W %x %y %b 2}
+ bind $name.c <Control-Shift-Button> {pdtk_canvas_click %W %x %y %b 3}
+ bind $name.c <Alt-Button> {pdtk_canvas_click %W %x %y %b 4}
+ bind $name.c <Alt-Shift-Button> {pdtk_canvas_click %W %x %y %b 5}
+ bind $name.c <Alt-Control-Button> {pdtk_canvas_click %W %x %y %b 6}
+ bind $name.c <Alt-Control-Shift-Button> {pdtk_canvas_click %W %x %y %b 7}
+ bind $name.c <Button-3> {pdtk_canvas_click %W %x %y %b 8}
+
+ bind $name.c <ButtonRelease> {pdtk_canvas_mouseup %W %x %y %b}
+ bind $name.c <Control-Key> {pdtk_canvas_ctrlkey %W %K 0}
+ bind $name.c <Control-Shift-Key> {pdtk_canvas_ctrlkey %W %K 1}
+ bind $name.c <Alt-Key> {pdtk_canvas_altkey %W %K %A}
+# bind $name.c <Mod1-Key> {puts stderr [concat mod1 %W %K %A]}
+ bind $name.c <Key> {pdtk_canvas_key %W %K %A}
+ bind $name.c <KeyRelease> {pdtk_canvas_keyup %W %K %A}
+ bind $name.c <Motion> {pdtk_canvas_motion %W %x %y 0}
+ bind $name.c <Alt-Motion> {pdtk_canvas_motion %W %x %y 4}
+ bind $name.c <Map> {pdtk_canvas_map %W %s}
+# bind $name.c <Unmap> {puts stderr map}
+ focus $name.c
+# puts stderr "all done"
+# after 1 [concat raise $name]
+}
+
+#################### event binding procedures ################
+
+#get the name of the toplevel window for a canvas; this is also
+#the name of the canvas object in Pd.
+
+proc canvastosym {name} {
+ string range $name 0 [expr [string length $name] - 3]
+}
+
+set pdtk_lastcanvasconfigured ""
+set pdtk_lastcanvasconfiguration ""
+
+proc pdtk_canvas_checkgeometry {topname} {
+ set boo [winfo geometry $topname.c]
+ set boo2 [wm geometry $topname]
+ global pdtk_lastcanvasconfigured
+ global pdtk_lastcanvasconfiguration
+ if {$topname != $pdtk_lastcanvasconfigured || \
+ $boo != $pdtk_lastcanvasconfiguration} {
+ set pdtk_lastcanvasconfigured $topname
+ set pdtk_lastcanvasconfiguration $boo
+ pd $topname relocate $boo $boo2 \;
+ }
+}
+
+proc pdtk_canvas_click {name x y b f} {
+# puts stderr [concat got $f]
+ pd [canvastosym $name] mouse [$name canvasx $x] [$name canvasy $y] $b $f \;
+}
+
+proc pdtk_canvas_shiftclick {name x y b} {
+ pd [canvastosym $name] mouse [$name canvasx $x] [$name canvasy $y] $b 1 \;
+}
+
+proc pdtk_canvas_ctrlclick {name x y b} {
+ pd [canvastosym $name] mouse [$name canvasx $x] [$name canvasy $y] $b 2 \;
+}
+
+proc pdtk_canvas_altclick {name x y b} {
+ pd [canvastosym $name] mouse [$name canvasx $x] [$name canvasy $y] $b 3 \;
+}
+
+proc pdtk_canvas_dblclick {name x y b} {
+ pd [canvastosym $name] mouse [$name canvasx $x] [$name canvasy $y] $b 4 \;
+}
+
+set pdtk_canvas_mouseup_name 0
+set pdtk_canvas_mouseup_xminval 0
+set pdtk_canvas_mouseup_xmaxval 0
+set pdtk_canvas_mouseup_yminval 0
+set pdtk_canvas_mouseup_ymaxval 0
+
+proc pdtk_canvas_mouseup {name x y b} {
+ pd [concat [canvastosym $name] mouseup [$name canvasx $x] \
+ [$name canvasy $y] $b \;]
+
+# we use the mouseup event to update scrollbar ranges and recheck the
+# geometry of the window since I haven't taken the time to figure out
+# how to do it right.
+
+ global pdtk_canvas_mouseup_name
+ global pdtk_canvas_mouseup_xminval
+ global pdtk_canvas_mouseup_xmaxval
+ global pdtk_canvas_mouseup_yminval
+ global pdtk_canvas_mouseup_ymaxval
+
+ set size [$name bbox all]
+ if {$size != ""} {
+ set xminval 0
+ set yminval 0
+ set xmaxval 100
+ set ymaxval 100
+ set x1 [lindex $size 0]
+ set x2 [lindex $size 2]
+ set y1 [lindex $size 1]
+ set y2 [lindex $size 3]
+
+ if {$x1 < 0} {set xminval $x1}
+ if {$y1 < 0} {set yminval $y1}
+
+ if {$x2 > 100} {set xmaxval $x2}
+ if {$y2 > 100} {set ymaxval $y2}
+
+ if {$pdtk_canvas_mouseup_name != $name || \
+ $pdtk_canvas_mouseup_xminval != $xminval || \
+ $pdtk_canvas_mouseup_xmaxval != $xmaxval || \
+ $pdtk_canvas_mouseup_yminval != $yminval || \
+ $pdtk_canvas_mouseup_ymaxval != $ymaxval } {
+
+ set newsize "$xminval $yminval $xmaxval $ymaxval"
+ $name configure -scrollregion $newsize
+ set pdtk_canvas_mouseup_name $name
+ set pdtk_canvas_mouseup_xminval $xminval
+ set pdtk_canvas_mouseup_xmaxval $xmaxval
+ set pdtk_canvas_mouseup_yminval $yminval
+ set pdtk_canvas_mouseup_ymaxval $ymaxval
+ }
+
+ }
+ pdtk_canvas_checkgeometry [canvastosym $name]
+}
+
+proc pdtk_canvas_key {name key iso} {
+# puts stderr [concat down key= $key iso= $iso]
+# .controls.switches.meterbutton configure -text $key
+# HACK for MAC OSX -- backspace seems different; I don't understand why.
+# invesigate this LATER...
+ global pd_nt
+ if {$pd_nt == 2} {
+ if {$key == "BackSpace"} {
+ set key 8
+ set keynum 8
+ }
+ if {$key == "Delete"} {
+ set key 8
+ set keynum 8
+ }
+ }
+ if {$iso != ""} {
+ scan $iso %c keynum
+ pd [canvastosym $name] key 1 $keynum \;
+ } else {
+ pd [canvastosym $name] key 1 $key \;
+ }
+}
+
+proc pdtk_canvas_keyup {name key iso} {
+# puts stderr [concat up key= $key iso= $iso]
+ if {$iso != ""} {
+ scan $iso %c keynum
+ pd [canvastosym $name] key 0 $keynum \;
+ } else {
+ pd [canvastosym $name] key 0 $key \;
+ }
+}
+
+proc pdtk_canvas_altkey {name key iso} {
+# puts stderr [concat alt-key $iso]
+############iemlib##################
+ set topname [string trimright $name .c]
+ if {$key == "b" || $key == "B"} {menu_bng $topname 1}
+ if {$key == "t" || $key == "T"} {menu_toggle $topname 1}
+ if {$key == "n" || $key == "N"} {menu_numbox $topname 1}
+ if {$key == "v" || $key == "V"} {menu_vslider $topname 1}
+ if {$key == "h" || $key == "H"} {menu_hslider $topname 1}
+ if {$key == "i" || $key == "I"} {menu_hdial $topname 1}
+ if {$key == "d" || $key == "D"} {menu_vdial $topname 1}
+ if {$key == "u" || $key == "U"} {menu_vumeter $topname 1}
+ if {$key == "c" || $key == "C"} {menu_mycnv $topname 1}
+############iemlib##################
+}
+
+proc pdtk_canvas_ctrlkey {name key shift} {
+# first get rid of ".c" suffix; we'll refer to the toplevel instead
+ set topname [string trimright $name .c]
+# puts stderr [concat ctrl-key $key $topname]
+
+ if {$key == "n" || $key == "N"} {menu_new}
+ if {$key == "o" || $key == "O"} {menu_open}
+ if {$key == "m" || $key == "M"} {menu_send}
+ if {$key == "q" || $key == "Q"} {
+ if {$shift == 1} {menu_really_quit} else {menu_quit}
+ }
+ if {$key == "s" || $key == "S"} {
+ if {$shift == 1} {menu_saveas $topname} else {menu_save $topname}
+ }
+ if {$key == "w" || $key == "W"} {menu_close $topname}
+ if {$key == "p" || $key == "P"} {menu_print $topname}
+ if {$key == "x" || $key == "X"} {menu_cut $topname}
+ if {$key == "c" || $key == "C"} {menu_copy $topname}
+ if {$key == "v" || $key == "V"} {menu_paste $topname}
+ if {$key == "d" || $key == "D"} {menu_duplicate $topname}
+ if {$key == "a" || $key == "A"} {menu_selectall $topname}
+ if {$key == "t" || $key == "T"} {menu_texteditor $topname}
+ if {$key == "f" || $key == "F"} {menu_findobject $topname}
+ if {$key == "g" || $key == "G"} {menu_findagain $topname}
+ if {$key == "1"} {menu_object $topname 1}
+ if {$key == "2"} {menu_message $topname 1}
+ if {$key == "3"} {menu_floatatom $topname 1}
+ if {$key == "4"} {menu_symbolatom $topname 1}
+ if {$key == "5"} {menu_comment $topname 1}
+ if {$key == "slash"} {menu_audio 1}
+ if {$key == "period"} {menu_audio 0}
+ if {$key == "e" || $key == "E"} {menu_editmode $topname}
+############iemlib##################
+ if {$key == "r" || $key == "R"} {menu_protectmode $topname}
+############iemlib##################
+}
+
+proc pdtk_canvas_motion {name x y mods} {
+# puts stderr [concat [canvastosym $name] $name $x $y]
+ pd [canvastosym $name] motion [$name canvasx $x] [$name canvasy $y] $mods \;
+}
+
+# "map" event tells us when the canvas becomes visible (arg is "0") or
+# invisible (arg is ""). Invisibility means the Window Manager has minimized
+# us. We don't get a final "unmap" event when we destroy the window.
+proc pdtk_canvas_map {name arg} {
+ if {$arg == "0"} {
+ pd [canvastosym $name] map 1 \;
+ } else {
+ pd [canvastosym $name] map 0 \;
+ }
+}
+
+set saveas_dir nowhere
+
+############ pdtk_canvas_saveas -- run a saveas dialog ##############
+
+proc pdtk_canvas_saveas {name initfile initdir} {
+ global pd_nt
+ if {$pd_nt == 2} {
+ cd $initdir
+ set filename [tk_getSaveFile -initialfile $initfile \
+ -defaultextension .pd \
+ -filetypes { {{pd files} {.pd}} {{max files} {.pat}} }]
+ } else {
+ set filename [tk_getSaveFile -initialfile $initfile \
+ -initialdir $initdir -defaultextension .pd \
+ -filetypes { {{pd files} {.pd}} {{max files} {.pat}} }]
+ }
+ if {$filename != ""} {
+ set directory [string range $filename 0 \
+ [expr [string last / $filename ] - 1]]
+ set basename [string range $filename \
+ [expr [string last / $filename ] + 1] end]
+ pd [concat $name savetofile [pdtk_enquote $basename] \
+ [pdtk_enquote $directory] \;]
+# pd [concat $name savetofile $basename $directory \;]
+ }
+}
+
+############ pdtk_canvas_dofont -- run a font and resize dialog #########
+
+set fontsize 0
+set stretchval 0
+set whichstretch 0
+
+proc dofont_apply {name} {
+ global fontsize
+ global stretchval
+ global whichstretch
+ set cmd [concat $name font $fontsize $stretchval $whichstretch \;]
+# puts stderr $cmd
+ pd $cmd
+}
+
+proc dofont_cancel {name} {
+ set cmd [concat $name cancel \;]
+# puts stderr $cmd
+ pd $cmd
+}
+
+proc pdtk_canvas_dofont {name initsize} {
+
+ global fontsize
+ set fontsize $initsize
+
+ global stretchval
+ set stretchval 100
+
+ global whichstretch
+ set whichstretch 1
+
+ toplevel $name
+ wm title $name {FONT BOMB}
+ wm protocol $name WM_DELETE_WINDOW [concat dofont_cancel $name]
+
+ frame $name.buttonframe
+ pack $name.buttonframe -side bottom -fill x -pady 2m
+ button $name.buttonframe.cancel -text {Cancel}\
+ -command "dofont_cancel $name"
+ button $name.buttonframe.ok -text {Do it}\
+ -command "dofont_apply $name"
+ pack $name.buttonframe.cancel -side left -expand 1
+ pack $name.buttonframe.ok -side left -expand 1
+
+ frame $name.radiof
+ pack $name.radiof -side left
+
+ label $name.radiof.label -text {Font Size:}
+ pack $name.radiof.label -side top
+
+ radiobutton $name.radiof.radio8 -value 8 -variable fontsize -text "8"
+ radiobutton $name.radiof.radio10 -value 10 -variable fontsize -text "10"
+ radiobutton $name.radiof.radio12 -value 12 -variable fontsize -text "12"
+ radiobutton $name.radiof.radio16 -value 16 -variable fontsize -text "16"
+ radiobutton $name.radiof.radio24 -value 24 -variable fontsize -text "24"
+ radiobutton $name.radiof.radio36 -value 36 -variable fontsize -text "36"
+ pack $name.radiof.radio8 -side top -anchor w
+ pack $name.radiof.radio10 -side top -anchor w
+ pack $name.radiof.radio12 -side top -anchor w
+ pack $name.radiof.radio16 -side top -anchor w
+ pack $name.radiof.radio24 -side top -anchor w
+ pack $name.radiof.radio36 -side top -anchor w
+
+ frame $name.stretchf
+ pack $name.stretchf -side left
+
+ label $name.stretchf.label -text {Stretch:}
+ pack $name.stretchf.label -side top
+
+ entry $name.stretchf.entry -textvariable stretchval -width 5
+ pack $name.stretchf.entry -side left
+
+ radiobutton $name.stretchf.radio1 \
+ -value 1 -variable whichstretch -text "X and Y"
+ radiobutton $name.stretchf.radio2 \
+ -value 2 -variable whichstretch -text "X only"
+ radiobutton $name.stretchf.radio3 \
+ -value 3 -variable whichstretch -text "Y only"
+
+ pack $name.stretchf.radio1 -side top -anchor w
+ pack $name.stretchf.radio2 -side top -anchor w
+ pack $name.stretchf.radio3 -side top -anchor w
+
+}
+
+############ pdtk_gatom_dialog -- run a gatom dialog #########
+
+set gatomwidth 0
+set gatomlo 0
+set gatomhi 0
+
+proc dogatom_apply {name} {
+ global gatomwidth gatomlo gatomhi
+ set cmd [concat $name param $gatomwidth $gatomlo $gatomhi \;]
+# puts stderr $cmd
+ pd $cmd
+}
+
+proc dogatom_cancel {name} {
+ set cmd [concat $name cancel \;]
+# puts stderr $cmd
+ pd $cmd
+}
+
+proc dogatom_ok {name} {
+ dogatom_apply $name
+ dogatom_cancel $name
+}
+
+proc pdtk_gatom_dialog {name initwidth initlo inithi} {
+
+ global gatomwidth gatomlo gatomhi
+ set gatomwidth $initwidth
+ set gatomlo $initlo
+ set gatomhi $inithi
+
+ toplevel $name
+ wm title $name {Atom}
+ wm protocol $name WM_DELETE_WINDOW [concat dogatom_cancel $name]
+
+ frame $name.buttonframe
+ pack $name.buttonframe -side bottom -fill x -pady 2m
+ button $name.buttonframe.cancel -text {Cancel}\
+ -command "dogatom_cancel $name"
+ button $name.buttonframe.ok -text {Apply}\
+ -command "dogatom_apply $name"
+ pack $name.buttonframe.cancel -side left -expand 1
+ pack $name.buttonframe.ok -side left -expand 1
+
+ frame $name.paramhi
+ pack $name.paramhi -side bottom
+ label $name.paramhi.entryname -text "upper limit"
+ entry $name.paramhi.entry -textvariable gatomhi -width 8
+ pack $name.paramhi.entryname $name.paramhi.entry -side left
+
+ frame $name.paramlo
+ pack $name.paramlo -side bottom
+ label $name.paramlo.entryname -text "lower limit"
+ entry $name.paramlo.entry -textvariable gatomlo -width 8
+ pack $name.paramlo.entryname $name.paramlo.entry -side left
+
+ frame $name.params
+ pack $name.params -side bottom
+ label $name.params.entryname -text width
+ entry $name.params.entry -textvariable gatomwidth -width 4
+ pack $name.params.entryname $name.params.entry -side left
+
+ bind $name.paramhi.entry <KeyPress-Return> [concat dogatom_ok $name]
+ bind $name.paramlo.entry <KeyPress-Return> [concat dogatom_ok $name]
+ bind $name.params.entry <KeyPress-Return> [concat dogatom_ok $name]
+ $name.params.entry select from 0
+ $name.params.entry select adjust end
+ focus $name.params.entry
+}
+
+############ pdtk_canvas_popup -- popup menu for canvas #########
+
+set popup_xpix 0
+set popup_ypix 0
+
+proc popup_action {name action} {
+ global popup_xpix popup_ypix
+ set cmd [concat $name done-popup $action $popup_xpix $popup_ypix \;]
+# puts stderr $cmd
+ pd $cmd
+}
+
+proc pdtk_canvas_popup {name xpix ypix canprop canopen} {
+ global popup_xpix popup_ypix
+ set popup_xpix $xpix
+ set popup_ypix $ypix
+ if {$canprop == 0} {$name.popup entryconfigure 0 -state disabled}
+ if {$canprop == 1} {$name.popup entryconfigure 0 -state active}
+ if {$canopen == 0} {$name.popup entryconfigure 1 -state disabled}
+ if {$canopen == 1} {$name.popup entryconfigure 1 -state active}
+ tk_popup $name.popup [expr $xpix + [winfo rootx $name.c]] \
+ [expr $ypix + [winfo rooty $name.c]] 0
+}
+
+############ pdtk_graph_dialog -- dialog window for graphs #########
+
+# the graph and array dialogs can come up in many copies; but in TK the easiest
+# way to get data from an "entry", etc., is to set an associated variable
+# name. This is especially true for grouped "radio buttons". So we have
+# to synthesize variable names for each instance of the dialog. The dialog
+# gets a TK pathname $id, from which it strips the leading "." to make a
+# variable suffix $vid. Then you can get the actual value out by asking for
+# [eval concat $$variablename]. There should be an easier way but I don't see
+# it yet.
+
+proc graph_apply {id} {
+# strip "." from the TK id to make a variable name suffix
+ set vid [string trimleft $id .]
+# for each variable, make a local variable to hold its name...
+ set var_graph_x1 [concat graph_x1_$vid]
+ global $var_graph_x1
+ set var_graph_x2 [concat graph_x2_$vid]
+ global $var_graph_x2
+ set var_graph_xpix [concat graph_xpix_$vid]
+ global $var_graph_xpix
+ set var_graph_y1 [concat graph_y1_$vid]
+ global $var_graph_y1
+ set var_graph_y2 [concat graph_y2_$vid]
+ global $var_graph_y2
+ set var_graph_ypix [concat graph_ypix_$vid]
+ global $var_graph_ypix
+
+ pd [concat $id dialog \
+ [eval concat $$var_graph_x1] \
+ [eval concat $$var_graph_y1] \
+ [eval concat $$var_graph_x2] \
+ [eval concat $$var_graph_y2] \
+ [eval concat $$var_graph_xpix] \
+ [eval concat $$var_graph_ypix] \
+ \;]
+}
+
+proc graph_cancel {id} {
+ set cmd [concat $id cancel \;]
+# puts stderr $cmd
+ pd $cmd
+}
+
+proc graph_ok {id} {
+ graph_apply $id
+ graph_cancel $id
+}
+
+proc pdtk_graph_dialog {id x1 y1 x2 y2 xpix ypix} {
+ set vid [string trimleft $id .]
+ set var_graph_x1 [concat graph_x1_$vid]
+ global $var_graph_x1
+ set var_graph_x2 [concat graph_x2_$vid]
+ global $var_graph_x2
+ set var_graph_xpix [concat graph_xpix_$vid]
+ global $var_graph_xpix
+ set var_graph_y1 [concat graph_y1_$vid]
+ global $var_graph_y1
+ set var_graph_y2 [concat graph_y2_$vid]
+ global $var_graph_y2
+ set var_graph_ypix [concat graph_ypix_$vid]
+ global $var_graph_ypix
+
+ set $var_graph_x1 $x1
+ set $var_graph_x2 $x2
+ set $var_graph_xpix $xpix
+ set $var_graph_y1 $y1
+ set $var_graph_y2 $y2
+ set $var_graph_ypix $ypix
+
+ toplevel $id
+ wm title $id {graph}
+ wm protocol $id WM_DELETE_WINDOW [concat graph_cancel $id]
+
+ label $id.label -text {GRAPH BOUNDS}
+ pack $id.label -side top
+
+ frame $id.buttonframe
+ pack $id.buttonframe -side bottom -fill x -pady 2m
+ button $id.buttonframe.cancel -text {Cancel}\
+ -command "graph_cancel $id"
+ button $id.buttonframe.apply -text {Apply}\
+ -command "graph_apply $id"
+ button $id.buttonframe.ok -text {OK}\
+ -command "graph_ok $id"
+ pack $id.buttonframe.cancel -side left -expand 1
+ pack $id.buttonframe.apply -side left -expand 1
+ pack $id.buttonframe.ok -side left -expand 1
+
+ frame $id.xrangef
+ pack $id.xrangef -side top
+
+ label $id.xrangef.l1 -text "X from:"
+ entry $id.xrangef.x1 -textvariable $var_graph_x1 -width 7
+ label $id.xrangef.l2 -text "to:"
+ entry $id.xrangef.x2 -textvariable $var_graph_x2 -width 7
+ label $id.xrangef.l3 -text "screen width:"
+ entry $id.xrangef.xpix -textvariable $var_graph_xpix -width 7
+ pack $id.xrangef.l1 $id.xrangef.x1 \
+ $id.xrangef.l2 $id.xrangef.x2 \
+ $id.xrangef.l3 $id.xrangef.xpix -side left
+
+ frame $id.yrangef
+ pack $id.yrangef -side top
+
+# dig in the following that the upper bound is labeled y1 but the variable is
+# y2, etc. This is to deal with the inconsistent use of "upper and lower"
+# graph bounds... in the dialog the upper Y bound is the lower valued Y pixel.
+ label $id.yrangef.l1 -text "Y from:"
+ entry $id.yrangef.y1 -textvariable $var_graph_y2 -width 7
+ label $id.yrangef.l2 -text "to:"
+ entry $id.yrangef.y2 -textvariable $var_graph_y1 -width 7
+ label $id.yrangef.l3 -text "screen height:"
+ entry $id.yrangef.ypix -textvariable $var_graph_ypix -width 7
+ pack $id.yrangef.l1 $id.yrangef.y1 \
+ $id.yrangef.l2 $id.yrangef.y2 \
+ $id.yrangef.l3 $id.yrangef.ypix -side left
+
+ bind $id.xrangef.x1 <KeyPress-Return> [concat graph_ok $id]
+ bind $id.xrangef.x2 <KeyPress-Return> [concat graph_ok $id]
+ bind $id.xrangef.xpix <KeyPress-Return> [concat graph_ok $id]
+ bind $id.yrangef.y1 <KeyPress-Return> [concat graph_ok $id]
+ bind $id.yrangef.y2 <KeyPress-Return> [concat graph_ok $id]
+ bind $id.yrangef.ypix <KeyPress-Return> [concat graph_ok $id]
+ $id.xrangef.x2 select from 0
+ $id.xrangef.x2 select adjust end
+ focus $id.xrangef.x2
+}
+
+# begin of change "iemlib"
+############ pdtk_iemgui_dialog -- dialog window for iem guis #########
+
+set iemgui_define_min_flashhold 50
+set iemgui_define_min_flashbreak 10
+set iemgui_define_min_fontsize 4
+
+proc iemgui_clip_dim {id} {
+ set vid [string trimleft $id .]
+
+ set var_iemgui_wdt [concat iemgui_wdt_$vid]
+ global $var_iemgui_wdt
+ set var_iemgui_min_wdt [concat iemgui_min_wdt_$vid]
+ global $var_iemgui_min_wdt
+ set var_iemgui_hgt [concat iemgui_hgt_$vid]
+ global $var_iemgui_hgt
+ set var_iemgui_min_hgt [concat iemgui_min_hgt_$vid]
+ global $var_iemgui_min_hgt
+
+ if {[eval concat $$var_iemgui_wdt] < [eval concat $$var_iemgui_min_wdt]} {
+ set $var_iemgui_wdt [eval concat $$var_iemgui_min_wdt]
+ $id.dim.w_ent configure -textvariable $var_iemgui_wdt
+ }
+ if {[eval concat $$var_iemgui_hgt] < [eval concat $$var_iemgui_min_hgt]} {
+ set $var_iemgui_hgt [eval concat $$var_iemgui_min_hgt]
+ $id.dim.h_ent configure -textvariable $var_iemgui_hgt
+ }
+}
+
+proc iemgui_clip_num {id} {
+ set vid [string trimleft $id .]
+
+ set var_iemgui_num [concat iemgui_num_$vid]
+ global $var_iemgui_num
+
+ if {[eval concat $$var_iemgui_num] > 2000} {
+ set $var_iemgui_num 2000
+ $id.para.num_ent configure -textvariable $var_iemgui_num
+ }
+ if {[eval concat $$var_iemgui_num] < 1} {
+ set $var_iemgui_num 1
+ $id.para.num_ent configure -textvariable $var_iemgui_num
+ }
+}
+
+proc iemgui_sched_rng {id} {
+ set vid [string trimleft $id .]
+
+ set var_iemgui_min_rng [concat iemgui_min_rng_$vid]
+ global $var_iemgui_min_rng
+ set var_iemgui_max_rng [concat iemgui_max_rng_$vid]
+ global $var_iemgui_max_rng
+ set var_iemgui_rng_sch [concat iemgui_rng_sch_$vid]
+ global $var_iemgui_rng_sch
+
+ global iemgui_define_min_flashhold
+ global iemgui_define_min_flashbreak
+
+ if {[eval concat $$var_iemgui_rng_sch] == 2} {
+ if {[eval concat $$var_iemgui_max_rng] < [eval concat $$var_iemgui_min_rng]} {
+ set hhh [eval concat $$var_iemgui_min_rng]
+ set $var_iemgui_min_rng [eval concat $$var_iemgui_max_rng]
+ set $var_iemgui_max_rng $hhh
+ $id.rng.max_ent configure -textvariable $var_iemgui_max_rng
+ $id.rng.min_ent configure -textvariable $var_iemgui_min_rng }
+ if {[eval concat $$var_iemgui_max_rng] < $iemgui_define_min_flashhold} {
+ set $var_iemgui_max_rng $iemgui_define_min_flashhold
+ $id.rng.max_ent configure -textvariable $var_iemgui_max_rng
+ }
+ if {[eval concat $$var_iemgui_min_rng] < $iemgui_define_min_flashbreak} {
+ set $var_iemgui_min_rng $iemgui_define_min_flashbreak
+ $id.rng.min_ent configure -textvariable $var_iemgui_min_rng
+ }
+ }
+ if {[eval concat $$var_iemgui_rng_sch] == 1} {
+ if {[eval concat $$var_iemgui_min_rng] == 0.0} {
+ set $var_iemgui_min_rng 1.0
+ $id.rng.min_ent configure -textvariable $var_iemgui_min_rng
+ }
+ }
+}
+
+proc iemgui_verify_rng {id} {
+ set vid [string trimleft $id .]
+
+ set var_iemgui_min_rng [concat iemgui_min_rng_$vid]
+ global $var_iemgui_min_rng
+ set var_iemgui_max_rng [concat iemgui_max_rng_$vid]
+ global $var_iemgui_max_rng
+ set var_iemgui_lin0_log1 [concat iemgui_lin0_log1_$vid]
+ global $var_iemgui_lin0_log1
+
+ if {[eval concat $$var_iemgui_lin0_log1] == 1} {
+ if {[eval concat $$var_iemgui_max_rng] == 0.0 && [eval concat $$var_iemgui_min_rng] == 0.0} {
+ set $var_iemgui_max_rng 1.0
+ $id.rng.max_ent configure -textvariable $var_iemgui_max_rng
+ }
+ if {[eval concat $$var_iemgui_max_rng] > 0} {
+ if {[eval concat $$var_iemgui_min_rng] <= 0} {
+ set $var_iemgui_min_rng [expr [eval concat $$var_iemgui_max_rng] * 0.01]
+ $id.rng.min_ent configure -textvariable $var_iemgui_min_rng
+ }
+ } else {
+ if {[eval concat $$var_iemgui_min_rng] > 0} {
+ set $var_iemgui_max_rng [expr [eval concat $$var_iemgui_min_rng] * 0.01]
+ $id.rng.max_ent configure -textvariable $var_iemgui_max_rng
+ }
+ }
+ }
+}
+
+proc iemgui_clip_fontsize {id} {
+ set vid [string trimleft $id .]
+
+ set var_iemgui_gn_fs [concat iemgui_gn_fs_$vid]
+ global $var_iemgui_gn_fs
+
+ global iemgui_define_min_fontsize
+
+ if {[eval concat $$var_iemgui_gn_fs] < $iemgui_define_min_fontsize} {
+ set $var_iemgui_gn_fs $iemgui_define_min_fontsize
+ $id.gnfs.fs_ent configure -textvariable $var_iemgui_gn_fs
+ }
+}
+
+proc iemgui_set_col_example {id} {
+ set vid [string trimleft $id .]
+
+ set var_iemgui_bcol [concat iemgui_bcol_$vid]
+ global $var_iemgui_bcol
+ set var_iemgui_fcol [concat iemgui_fcol_$vid]
+ global $var_iemgui_fcol
+ set var_iemgui_lcol [concat iemgui_lcol_$vid]
+ global $var_iemgui_lcol
+
+ $id.col_example_choose.lb_bk configure \
+ -background [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -activebackground [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -foreground [format "#%6.6x" [eval concat $$var_iemgui_lcol]] \
+ -activeforeground [format "#%6.6x" [eval concat $$var_iemgui_lcol]]
+
+ if { [eval concat $$var_iemgui_fcol] >= 0 } {
+ $id.col_example_choose.fr_bk configure \
+ -background [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -activebackground [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -foreground [format "#%6.6x" [eval concat $$var_iemgui_fcol]] \
+ -activeforeground [format "#%6.6x" [eval concat $$var_iemgui_fcol]]
+ } else {
+ $id.col_example_choose.fr_bk configure \
+ -background [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -activebackground [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -foreground [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -activeforeground [format "#%6.6x" [eval concat $$var_iemgui_bcol]]}
+}
+
+proc iemgui_preset_col {id presetcol} {
+ set vid [string trimleft $id .]
+
+ set var_iemgui_l2_f1_b0 [concat iemgui_l2_f1_b0_$vid]
+ global $var_iemgui_l2_f1_b0
+ set var_iemgui_bcol [concat iemgui_bcol_$vid]
+ global $var_iemgui_bcol
+ set var_iemgui_fcol [concat iemgui_fcol_$vid]
+ global $var_iemgui_fcol
+ set var_iemgui_lcol [concat iemgui_lcol_$vid]
+ global $var_iemgui_lcol
+
+ if { [eval concat $$var_iemgui_l2_f1_b0] == 0 } { set $var_iemgui_bcol $presetcol }
+ if { [eval concat $$var_iemgui_l2_f1_b0] == 1 } { set $var_iemgui_fcol $presetcol }
+ if { [eval concat $$var_iemgui_l2_f1_b0] == 2 } { set $var_iemgui_lcol $presetcol }
+ iemgui_set_col_example $id
+}
+
+proc iemgui_choose_col_bkfrlb {id} {
+ set vid [string trimleft $id .]
+
+ set var_iemgui_l2_f1_b0 [concat iemgui_l2_f1_b0_$vid]
+ global $var_iemgui_l2_f1_b0
+ set var_iemgui_bcol [concat iemgui_bcol_$vid]
+ global $var_iemgui_bcol
+ set var_iemgui_fcol [concat iemgui_fcol_$vid]
+ global $var_iemgui_fcol
+ set var_iemgui_lcol [concat iemgui_lcol_$vid]
+ global $var_iemgui_lcol
+
+ if {[eval concat $$var_iemgui_l2_f1_b0] == 0} {
+ set $var_iemgui_bcol [expr [eval concat $$var_iemgui_bcol] & 0xFCFCFC]
+ set helpstring [tk_chooseColor -title "Background-Color" -initialcolor [format "#%6.6x" [eval concat $$var_iemgui_bcol]]]
+ if { $helpstring != "" } {
+ set $var_iemgui_bcol [string replace $helpstring 0 0 "0x"]
+ set $var_iemgui_bcol [expr [eval concat $$var_iemgui_bcol] & 0xFCFCFC] }
+ }
+ if {[eval concat $$var_iemgui_l2_f1_b0] == 1} {
+ set $var_iemgui_fcol [expr [eval concat $$var_iemgui_fcol] & 0xFCFCFC]
+ set helpstring [tk_chooseColor -title "Front-Color" -initialcolor [format "#%6.6x" [eval concat $$var_iemgui_fcol]]]
+ if { $helpstring != "" } {
+ set $var_iemgui_fcol [string replace $helpstring 0 0 "0x"]
+ set $var_iemgui_fcol [expr [eval concat $$var_iemgui_fcol] & 0xFCFCFC] }
+ }
+ if {[eval concat $$var_iemgui_l2_f1_b0] == 2} {
+ set $var_iemgui_lcol [expr [eval concat $$var_iemgui_lcol] & 0xFCFCFC]
+ set helpstring [tk_chooseColor -title "Label-Color" -initialcolor [format "#%6.6x" [eval concat $$var_iemgui_lcol]]]
+ if { $helpstring != "" } {
+ set $var_iemgui_lcol [string replace $helpstring 0 0 "0x"]
+ set $var_iemgui_lcol [expr [eval concat $$var_iemgui_lcol] & 0xFCFCFC] }
+ }
+ iemgui_set_col_example $id
+}
+
+proc iemgui_lilo {id} {
+ set vid [string trimleft $id .]
+
+ set var_iemgui_lin0_log1 [concat iemgui_lin0_log1_$vid]
+ global $var_iemgui_lin0_log1
+ set var_iemgui_lilo0 [concat iemgui_lilo0_$vid]
+ global $var_iemgui_lilo0
+ set var_iemgui_lilo1 [concat iemgui_lilo1_$vid]
+ global $var_iemgui_lilo1
+
+ iemgui_sched_rng $id
+
+ if {[eval concat $$var_iemgui_lin0_log1] == 0} {
+ set $var_iemgui_lin0_log1 1
+ $id.para.lilo configure -text [eval concat $$var_iemgui_lilo1]
+ iemgui_verify_rng $id
+ iemgui_sched_rng $id
+ } else {
+ set $var_iemgui_lin0_log1 0
+ $id.para.lilo configure -text [eval concat $$var_iemgui_lilo0]
+ }
+}
+
+proc iemgui_toggle_font {id} {
+ set vid [string trimleft $id .]
+
+ set var_iemgui_gn_f [concat iemgui_gn_f_$vid]
+ global $var_iemgui_gn_f
+
+ set $var_iemgui_gn_f [expr [eval concat $$var_iemgui_gn_f] + 1]
+ if {[eval concat $$var_iemgui_gn_f] > 2} {set $var_iemgui_gn_f 0}
+ if {[eval concat $$var_iemgui_gn_f] == 0} {$id.gnfs.fb configure -text "courier" -font {courier 10 bold}}
+ if {[eval concat $$var_iemgui_gn_f] == 1} {$id.gnfs.fb configure -text "helvetica" -font {helvetica 10 bold}}
+ if {[eval concat $$var_iemgui_gn_f] == 2} {$id.gnfs.fb configure -text "times" -font {times 10 bold}}
+}
+
+proc iemgui_lb {id} {
+ set vid [string trimleft $id .]
+
+ set var_iemgui_loadbang [concat iemgui_loadbang_$vid]
+ global $var_iemgui_loadbang
+
+ if {[eval concat $$var_iemgui_loadbang] == 0} {
+ set $var_iemgui_loadbang 1
+ $id.para.lb configure -text "init"
+ } else {
+ set $var_iemgui_loadbang 0
+ $id.para.lb configure -text "no init"
+ }
+}
+
+proc iemgui_stdy_jmp {id} {
+ set vid [string trimleft $id .]
+
+ set var_iemgui_steady [concat iemgui_steady_$vid]
+ global $var_iemgui_steady
+
+ if {[eval concat $$var_iemgui_steady]} {
+ set $var_iemgui_steady 0
+ $id.para.stdy_jmp configure -text "jump on click"
+ } else {
+ set $var_iemgui_steady 1
+ $id.para.stdy_jmp configure -text "steady on click"
+ }
+}
+
+proc iemgui_apply {id} {
+ set vid [string trimleft $id .]
+
+ set var_iemgui_wdt [concat iemgui_wdt_$vid]
+ global $var_iemgui_wdt
+ set var_iemgui_min_wdt [concat iemgui_min_wdt_$vid]
+ global $var_iemgui_min_wdt
+ set var_iemgui_hgt [concat iemgui_hgt_$vid]
+ global $var_iemgui_hgt
+ set var_iemgui_min_hgt [concat iemgui_min_hgt_$vid]
+ global $var_iemgui_min_hgt
+ set var_iemgui_min_rng [concat iemgui_min_rng_$vid]
+ global $var_iemgui_min_rng
+ set var_iemgui_max_rng [concat iemgui_max_rng_$vid]
+ global $var_iemgui_max_rng
+ set var_iemgui_lin0_log1 [concat iemgui_lin0_log1_$vid]
+ global $var_iemgui_lin0_log1
+ set var_iemgui_lilo0 [concat iemgui_lilo0_$vid]
+ global $var_iemgui_lilo0
+ set var_iemgui_lilo1 [concat iemgui_lilo1_$vid]
+ global $var_iemgui_lilo1
+ set var_iemgui_loadbang [concat iemgui_loadbang_$vid]
+ global $var_iemgui_loadbang
+ set var_iemgui_num [concat iemgui_num_$vid]
+ global $var_iemgui_num
+ set var_iemgui_steady [concat iemgui_steady_$vid]
+ global $var_iemgui_steady
+ set var_iemgui_snd [concat iemgui_snd_$vid]
+ global $var_iemgui_snd
+ set var_iemgui_rcv [concat iemgui_rcv_$vid]
+ global $var_iemgui_rcv
+ set var_iemgui_gui_nam [concat iemgui_gui_nam_$vid]
+ global $var_iemgui_gui_nam
+ set var_iemgui_gn_dx [concat iemgui_gn_dx_$vid]
+ global $var_iemgui_gn_dx
+ set var_iemgui_gn_dy [concat iemgui_gn_dy_$vid]
+ global $var_iemgui_gn_dy
+ set var_iemgui_gn_f [concat iemgui_gn_f_$vid]
+ global $var_iemgui_gn_f
+ set var_iemgui_gn_fs [concat iemgui_gn_fs_$vid]
+ global $var_iemgui_gn_fs
+ set var_iemgui_bcol [concat iemgui_bcol_$vid]
+ global $var_iemgui_bcol
+ set var_iemgui_fcol [concat iemgui_fcol_$vid]
+ global $var_iemgui_fcol
+ set var_iemgui_lcol [concat iemgui_lcol_$vid]
+ global $var_iemgui_lcol
+
+ iemgui_clip_dim $id
+ iemgui_clip_num $id
+ iemgui_sched_rng $id
+ iemgui_verify_rng $id
+ iemgui_sched_rng $id
+ iemgui_clip_fontsize $id
+
+ if {[eval concat $$var_iemgui_snd] == ""} {set hhhsnd "empty"} else {set hhhsnd [eval concat $$var_iemgui_snd]}
+ if {[eval concat $$var_iemgui_rcv] == ""} {set hhhrcv "empty"} else {set hhhrcv [eval concat $$var_iemgui_rcv]}
+ if {[eval concat $$var_iemgui_gui_nam] == ""} {set hhhgui_nam "empty"
+ } else {
+ set hhhgui_nam [eval concat $$var_iemgui_gui_nam]}
+
+ if {[string index $hhhsnd 0] == "$"} {
+ set hhhsnd [string replace $hhhsnd 0 0 #] }
+ if {[string index $hhhrcv 0] == "$"} {
+ set hhhrcv [string replace $hhhrcv 0 0 #] }
+ if {[string index $hhhgui_nam 0] == "$"} {
+ set hhhgui_nam [string replace $hhhgui_nam 0 0 #] }
+
+ pd [concat $id dialog \
+ [eval concat $$var_iemgui_wdt] \
+ [eval concat $$var_iemgui_hgt] \
+ [eval concat $$var_iemgui_min_rng] \
+ [eval concat $$var_iemgui_max_rng] \
+ [eval concat $$var_iemgui_lin0_log1] \
+ [eval concat $$var_iemgui_loadbang] \
+ [eval concat $$var_iemgui_num] \
+ $hhhsnd \
+ $hhhrcv \
+ $hhhgui_nam \
+ [eval concat $$var_iemgui_gn_dx] \
+ [eval concat $$var_iemgui_gn_dy] \
+ [eval concat $$var_iemgui_gn_f] \
+ [eval concat $$var_iemgui_gn_fs] \
+ [eval concat $$var_iemgui_bcol] \
+ [eval concat $$var_iemgui_fcol] \
+ [eval concat $$var_iemgui_lcol] \
+ [eval concat $$var_iemgui_steady] \
+ \;]
+}
+
+proc iemgui_cancel {id} {pd [concat $id cancel \;]}
+
+proc iemgui_ok {id} {
+ iemgui_apply $id
+ iemgui_cancel $id
+}
+
+proc pdtk_iemgui_dialog {id mainheader \
+ dim_header wdt min_wdt wdt_label hgt min_hgt hgt_label \
+ rng_header min_rng min_rng_label max_rng max_rng_label rng_sched \
+ lin0_log1 lilo0_label lilo1_label loadbang steady num_label num \
+ snd rcv \
+ gui_name \
+ gn_dx gn_dy \
+ gn_f gn_fs \
+ bcol fcol lcol} {
+
+ set vid [string trimleft $id .]
+
+ set var_iemgui_wdt [concat iemgui_wdt_$vid]
+ global $var_iemgui_wdt
+ set var_iemgui_min_wdt [concat iemgui_min_wdt_$vid]
+ global $var_iemgui_min_wdt
+ set var_iemgui_hgt [concat iemgui_hgt_$vid]
+ global $var_iemgui_hgt
+ set var_iemgui_min_hgt [concat iemgui_min_hgt_$vid]
+ global $var_iemgui_min_hgt
+ set var_iemgui_min_rng [concat iemgui_min_rng_$vid]
+ global $var_iemgui_min_rng
+ set var_iemgui_max_rng [concat iemgui_max_rng_$vid]
+ global $var_iemgui_max_rng
+ set var_iemgui_rng_sch [concat iemgui_rng_sch_$vid]
+ global $var_iemgui_rng_sch
+ set var_iemgui_lin0_log1 [concat iemgui_lin0_log1_$vid]
+ global $var_iemgui_lin0_log1
+ set var_iemgui_lilo0 [concat iemgui_lilo0_$vid]
+ global $var_iemgui_lilo0
+ set var_iemgui_lilo1 [concat iemgui_lilo1_$vid]
+ global $var_iemgui_lilo1
+ set var_iemgui_loadbang [concat iemgui_loadbang_$vid]
+ global $var_iemgui_loadbang
+ set var_iemgui_num [concat iemgui_num_$vid]
+ global $var_iemgui_num
+ set var_iemgui_steady [concat iemgui_steady_$vid]
+ global $var_iemgui_steady
+ set var_iemgui_snd [concat iemgui_snd_$vid]
+ global $var_iemgui_snd
+ set var_iemgui_rcv [concat iemgui_rcv_$vid]
+ global $var_iemgui_rcv
+ set var_iemgui_gui_nam [concat iemgui_gui_nam_$vid]
+ global $var_iemgui_gui_nam
+ set var_iemgui_gn_dx [concat iemgui_gn_dx_$vid]
+ global $var_iemgui_gn_dx
+ set var_iemgui_gn_dy [concat iemgui_gn_dy_$vid]
+ global $var_iemgui_gn_dy
+ set var_iemgui_gn_f [concat iemgui_gn_f_$vid]
+ global $var_iemgui_gn_f
+ set var_iemgui_gn_fs [concat iemgui_gn_fs_$vid]
+ global $var_iemgui_gn_fs
+ set var_iemgui_l2_f1_b0 [concat iemgui_l2_f1_b0_$vid]
+ global $var_iemgui_l2_f1_b0
+ set var_iemgui_bcol [concat iemgui_bcol_$vid]
+ global $var_iemgui_bcol
+ set var_iemgui_fcol [concat iemgui_fcol_$vid]
+ global $var_iemgui_fcol
+ set var_iemgui_lcol [concat iemgui_lcol_$vid]
+ global $var_iemgui_lcol
+
+ set $var_iemgui_wdt $wdt
+ set $var_iemgui_min_wdt $min_wdt
+ set $var_iemgui_hgt $hgt
+ set $var_iemgui_min_hgt $min_hgt
+ set $var_iemgui_min_rng $min_rng
+ set $var_iemgui_max_rng $max_rng
+ set $var_iemgui_rng_sch $rng_sched
+ set $var_iemgui_lin0_log1 $lin0_log1
+ set $var_iemgui_lilo0 $lilo0_label
+ set $var_iemgui_lilo1 $lilo1_label
+ set $var_iemgui_loadbang $loadbang
+ set $var_iemgui_num $num
+ set $var_iemgui_steady $steady
+ if {$snd == "empty"} {set $var_iemgui_snd [format ""]
+ } else {set $var_iemgui_snd [format "%s" $snd]}
+ if {$rcv == "empty"} {set $var_iemgui_rcv [format ""]
+ } else {set $var_iemgui_rcv [format "%s" $rcv]}
+ if {$gui_name == "empty"} {set $var_iemgui_gui_nam [format ""]
+ } else {set $var_iemgui_gui_nam [format "%s" $gui_name]}
+
+ if {[string index [eval concat $$var_iemgui_snd] 0] == "#"} {
+ set $var_iemgui_snd [string replace [eval concat $$var_iemgui_snd] 0 0 $] }
+ if {[string index [eval concat $$var_iemgui_rcv] 0] == "#"} {
+ set $var_iemgui_rcv [string replace [eval concat $$var_iemgui_rcv] 0 0 $] }
+ if {[string index [eval concat $$var_iemgui_gui_nam] 0] == "#"} {
+ set $var_iemgui_gui_nam [string replace [eval concat $$var_iemgui_gui_nam] 0 0 $] }
+ set $var_iemgui_gn_dx $gn_dx
+ set $var_iemgui_gn_dy $gn_dy
+ set $var_iemgui_gn_f $gn_f
+ set $var_iemgui_gn_fs $gn_fs
+
+ set $var_iemgui_bcol $bcol
+ set $var_iemgui_fcol $fcol
+ set $var_iemgui_lcol $lcol
+
+ set $var_iemgui_l2_f1_b0 0
+
+ toplevel $id
+ wm title $id [format "%s-PROPERTIES" $mainheader]
+ wm protocol $id WM_DELETE_WINDOW [concat iemgui_cancel $id]
+
+ frame $id.dim
+ pack $id.dim -side top
+ label $id.dim.head -text $dim_header
+ label $id.dim.w_lab -text $wdt_label -width 6
+ entry $id.dim.w_ent -textvariable $var_iemgui_wdt -width 5
+ label $id.dim.dummy1 -text " " -width 10
+ label $id.dim.h_lab -text $hgt_label -width 6
+ entry $id.dim.h_ent -textvariable $var_iemgui_hgt -width 5
+ pack $id.dim.head -side top
+ pack $id.dim.w_lab $id.dim.w_ent $id.dim.dummy1 -side left
+ if { $hgt_label != "empty" } {
+ pack $id.dim.h_lab $id.dim.h_ent -side left}
+
+ frame $id.rng
+ pack $id.rng -side top
+ label $id.rng.head -text $rng_header
+ label $id.rng.min_lab -text $min_rng_label -width 6
+ entry $id.rng.min_ent -textvariable $var_iemgui_min_rng -width 9
+ label $id.rng.dummy1 -text " " -width 1
+ label $id.rng.max_lab -text $max_rng_label -width 8
+ entry $id.rng.max_ent -textvariable $var_iemgui_max_rng -width 9
+ if { $rng_header != "empty" } {
+ pack $id.rng.head -side top
+ if { $min_rng_label != "empty" } {
+ pack $id.rng.min_lab $id.rng.min_ent -side left}
+ if { $max_rng_label != "empty" } {
+ pack $id.rng.dummy1 \
+ $id.rng.max_lab $id.rng.max_ent -side left} }
+
+ if { [eval concat $$var_iemgui_lin0_log1] >= 0 || [eval concat $$var_iemgui_loadbang] >= 0 || [eval concat $$var_iemgui_num] > 0 || [eval concat $$var_iemgui_steady] >= 0 } {
+ label $id.space1 -text "---------------------------------"
+ pack $id.space1 -side top }
+
+ frame $id.para
+ pack $id.para -side top
+ label $id.para.dummy2 -text "" -width 1
+ label $id.para.dummy3 -text "" -width 1
+ if {[eval concat $$var_iemgui_lin0_log1] == 0} {
+ button $id.para.lilo -text [eval concat $$var_iemgui_lilo0] -width 5 -command "iemgui_lilo $id" }
+ if {[eval concat $$var_iemgui_lin0_log1] == 1} {
+ button $id.para.lilo -text [eval concat $$var_iemgui_lilo1] -width 5 -command "iemgui_lilo $id" }
+ if {[eval concat $$var_iemgui_loadbang] == 0} {
+ button $id.para.lb -text "no init" -width 5 -command "iemgui_lb $id" }
+ if {[eval concat $$var_iemgui_loadbang] == 1} {
+ button $id.para.lb -text "init" -width 5 -command "iemgui_lb $id" }
+ label $id.para.num_lab -text $num_label -width 9
+ entry $id.para.num_ent -textvariable $var_iemgui_num -width 4
+ if {[eval concat $$var_iemgui_steady] == 0} {
+ button $id.para.stdy_jmp -text "jump on click" -width 11 -command "iemgui_stdy_jmp $id" }
+ if {[eval concat $$var_iemgui_steady] == 1} {
+ button $id.para.stdy_jmp -text "steady on click" -width 11 -command "iemgui_stdy_jmp $id" }
+ if {[eval concat $$var_iemgui_lin0_log1] >= 0} {
+ pack $id.para.lilo -side left -expand 1}
+ if {[eval concat $$var_iemgui_loadbang] >= 0} {
+ pack $id.para.dummy2 $id.para.lb -side left -expand 1}
+ if {[eval concat $$var_iemgui_num] > 0} {
+ pack $id.para.dummy3 $id.para.num_lab $id.para.num_ent -side left -expand 1}
+ if {[eval concat $$var_iemgui_steady] >= 0} {
+ pack $id.para.dummy3 $id.para.stdy_jmp -side left -expand 1}
+ if { $snd != "nosndno" || $rcv != "norcvno" } {
+ label $id.space2 -text "---------------------------------"
+ pack $id.space2 -side top }
+
+ frame $id.snd
+ pack $id.snd -side top
+ label $id.snd.dummy1 -text "" -width 2
+ label $id.snd.lab -text "send-symbol:" -width 12
+ entry $id.snd.ent -textvariable $var_iemgui_snd -width 20
+ if { $snd != "nosndno" } {
+ pack $id.snd.dummy1 $id.snd.lab $id.snd.ent -side left}
+
+ frame $id.rcv
+ pack $id.rcv -side top
+ label $id.rcv.lab -text "receive-symbol:" -width 15
+ entry $id.rcv.ent -textvariable $var_iemgui_rcv -width 20
+ if { $rcv != "norcvno" } {
+ pack $id.rcv.lab $id.rcv.ent -side left}
+
+ frame $id.gnam
+ pack $id.gnam -side top
+ label $id.gnam.head -text "--------------label:---------------"
+ label $id.gnam.dummy1 -text "" -width 1
+ label $id.gnam.lab -text "name:" -width 6
+ entry $id.gnam.ent -textvariable $var_iemgui_gui_nam -width 29
+ label $id.gnam.dummy2 -text "" -width 1
+ pack $id.gnam.head -side top
+ pack $id.gnam.dummy1 $id.gnam.lab $id.gnam.ent $id.gnam.dummy2 -side left
+
+ frame $id.gnxy
+ pack $id.gnxy -side top
+ label $id.gnxy.x_lab -text "x_off:" -width 6
+ entry $id.gnxy.x_ent -textvariable $var_iemgui_gn_dx -width 5
+ label $id.gnxy.dummy1 -text " " -width 10
+ label $id.gnxy.y_lab -text "y_off:" -width 6
+ entry $id.gnxy.y_ent -textvariable $var_iemgui_gn_dy -width 5
+ pack $id.gnxy.x_lab $id.gnxy.x_ent $id.gnxy.dummy1 \
+ $id.gnxy.y_lab $id.gnxy.y_ent -side left
+
+ frame $id.gnfs
+ pack $id.gnfs -side top
+ label $id.gnfs.f_lab -text "font:" -width 6
+ if {[eval concat $$var_iemgui_gn_f] == 0} {
+ button $id.gnfs.fb -text "courier" -font {courier 10 bold} -width 7 -command "iemgui_toggle_font $id" }
+ if {[eval concat $$var_iemgui_gn_f] == 1} {
+ button $id.gnfs.fb -text "helvetica" -font {helvetica 10 bold} -width 7 -command "iemgui_toggle_font $id" }
+ if {[eval concat $$var_iemgui_gn_f] == 2} {
+ button $id.gnfs.fb -text "times" -font {times 10 bold} -width 7 -command "iemgui_toggle_font $id" }
+ label $id.gnfs.dummy1 -text "" -width 1
+ label $id.gnfs.fs_lab -text "fontsize:" -width 8
+ entry $id.gnfs.fs_ent -textvariable $var_iemgui_gn_fs -width 5
+ pack $id.gnfs.f_lab $id.gnfs.fb $id.gnfs.dummy1 \
+ $id.gnfs.fs_lab $id.gnfs.fs_ent -side left
+
+ label $id.col_head -text "--------------colors:--------------"
+ pack $id.col_head -side top
+
+ frame $id.col_select
+ pack $id.col_select -side top
+ radiobutton $id.col_select.radio0 -value 0 -variable $var_iemgui_l2_f1_b0 \
+ -text "backgd" -width 5
+ radiobutton $id.col_select.radio1 -value 1 -variable $var_iemgui_l2_f1_b0 \
+ -text "front" -width 5
+ radiobutton $id.col_select.radio2 -value 2 -variable $var_iemgui_l2_f1_b0 \
+ -text "label" -width 5
+ if { [eval concat $$var_iemgui_fcol] >= 0 } {
+ pack $id.col_select.radio0 $id.col_select.radio1 $id.col_select.radio2 -side left
+ } else {pack $id.col_select.radio0 $id.col_select.radio2 -side left}
+
+ frame $id.col_example_choose
+ pack $id.col_example_choose -side top
+ button $id.col_example_choose.but -text "compose color" -width 10 \
+ -command "iemgui_choose_col_bkfrlb $id"
+ label $id.col_example_choose.dummy1 -text "" -width 1
+ if { [eval concat $$var_iemgui_fcol] >= 0 } {
+ button $id.col_example_choose.fr_bk -text "o=||=o" -width 5 \
+ -background [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -activebackground [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -foreground [format "#%6.6x" [eval concat $$var_iemgui_fcol]] \
+ -activeforeground [format "#%6.6x" [eval concat $$var_iemgui_fcol]] -pady 2
+ } else {
+ button $id.col_example_choose.fr_bk -text "o=||=o" -width 5 \
+ -background [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -activebackground [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -foreground [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -activeforeground [format "#%6.6x" [eval concat $$var_iemgui_bcol]] -pady 2}
+ button $id.col_example_choose.lb_bk -text "testlabel" -width 7 \
+ -background [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -activebackground [format "#%6.6x" [eval concat $$var_iemgui_bcol]] \
+ -foreground [format "#%6.6x" [eval concat $$var_iemgui_lcol]] \
+ -activeforeground [format "#%6.6x" [eval concat $$var_iemgui_lcol]] -pady 2
+
+ pack $id.col_example_choose.but $id.col_example_choose.dummy1 \
+ $id.col_example_choose.fr_bk $id.col_example_choose.lb_bk -side left
+
+ label $id.space3 -text "------or click color preset:-------"
+ pack $id.space3 -side top
+
+ frame $id.bcol
+ pack $id.bcol -side top
+ foreach i { 0 1 2 3 4 5 6 7 8 9 } hexcol { 16579836 14737632 12369084 \
+ 16572640 16572608 16579784 14220504 14220540 14476540 16308476 } {
+ button $id.bcol.c$i -background [format "#%6.6x" $hexcol] \
+ -activebackground [format "#%6.6x" $hexcol] \
+ -font {courier 2 normal} -padx 7 -pady 6 \
+ -command [format "iemgui_preset_col %s %d" $id $hexcol] }
+ pack $id.bcol.c0 $id.bcol.c1 $id.bcol.c2 $id.bcol.c3 $id.bcol.c4 \
+ $id.bcol.c5 $id.bcol.c6 $id.bcol.c7 $id.bcol.c8 $id.bcol.c9 -side left
+
+ frame $id.fcol
+ pack $id.fcol -side top
+ foreach i { 0 1 2 3 4 5 6 7 8 9 } hexcol { 10526880 8158332 6316128 \
+ 16525352 16559172 15263784 1370132 2684148 3952892 16003312 } {
+ button $id.fcol.c$i -background [format "#%6.6x" $hexcol] \
+ -activebackground [format "#%6.6x" $hexcol] \
+ -font {courier 2 normal} -padx 7 -pady 6 \
+ -command [format "iemgui_preset_col %s %d" $id $hexcol] }
+ pack $id.fcol.c0 $id.fcol.c1 $id.fcol.c2 $id.fcol.c3 $id.fcol.c4 \
+ $id.fcol.c5 $id.fcol.c6 $id.fcol.c7 $id.fcol.c8 $id.fcol.c9 -side left
+
+ frame $id.lcol
+ pack $id.lcol -side top
+ foreach i { 0 1 2 3 4 5 6 7 8 9 } hexcol { 4210752 2105376 0 \
+ 9177096 5779456 7874580 2641940 17488 5256 5767248 } {
+ button $id.lcol.c$i -background [format "#%6.6x" $hexcol] \
+ -activebackground [format "#%6.6x" $hexcol] \
+ -font {courier 2 normal} -padx 7 -pady 6 \
+ -command [format "iemgui_preset_col %s %d" $id $hexcol] }
+ pack $id.lcol.c0 $id.lcol.c1 $id.lcol.c2 $id.lcol.c3 $id.lcol.c4 \
+ $id.lcol.c5 $id.lcol.c6 $id.lcol.c7 $id.lcol.c8 $id.lcol.c9 -side left
+
+
+ label $id.space4 -text "---------------------------------"
+ pack $id.space4 -side top
+
+ frame $id.cao
+ pack $id.cao -side top
+ button $id.cao.cancel -text {Cancel} -width 6 \
+ -command "iemgui_cancel $id"
+ label $id.cao.dummy1 -text "" -width 3
+ button $id.cao.apply -text {Apply} -width 6 \
+ -command "iemgui_apply $id"
+ label $id.cao.dummy2 -text "" -width 3
+ button $id.cao.ok -text {OK} -width 6 \
+ -command "iemgui_ok $id"
+ pack $id.cao.cancel $id.cao.dummy1 \
+ $id.cao.apply $id.cao.dummy2 \
+ $id.cao.ok -side left
+
+ label $id.space5 -text ""
+ pack $id.space5 -side top
+
+ bind $id.dim.w_ent <KeyPress-Return> [concat iemgui_ok $id]
+ bind $id.dim.h_ent <KeyPress-Return> [concat iemgui_ok $id]
+ bind $id.rng.min_ent <KeyPress-Return> [concat iemgui_ok $id]
+ bind $id.rng.max_ent <KeyPress-Return> [concat iemgui_ok $id]
+ bind $id.para.num_ent <KeyPress-Return> [concat iemgui_ok $id]
+ bind $id.snd.ent <KeyPress-Return> [concat iemgui_ok $id]
+ bind $id.rcv.ent <KeyPress-Return> [concat iemgui_ok $id]
+ bind $id.gnam.ent <KeyPress-Return> [concat iemgui_ok $id]
+ bind $id.gnxy.x_ent <KeyPress-Return> [concat iemgui_ok $id]
+ bind $id.gnxy.y_ent <KeyPress-Return> [concat iemgui_ok $id]
+ bind $id.gnfs.fs_ent <KeyPress-Return> [concat iemgui_ok $id]
+ bind $id.cao.ok <KeyPress-Return> [concat iemgui_ok $id]
+
+ $id.dim.w_ent select from 0
+ $id.dim.w_ent select adjust end
+ focus $id.dim.w_ent
+}
+# end of change "iemlib"
+
+############ pdtk_array_dialog -- dialog window for arrays #########
+proc array_apply {id} {
+# strip "." from the TK id to make a variable name suffix
+ set vid [string trimleft $id .]
+# for each variable, make a local variable to hold its name...
+ set var_array_name [concat array_name_$vid]
+ global $var_array_name
+ set var_array_n [concat array_n_$vid]
+ global $var_array_n
+ set var_array_saveit [concat array_saveit_$vid]
+ global $var_array_saveit
+ set var_array_otherflag [concat array_otherflag_$vid]
+ global $var_array_otherflag
+ set mofo [eval concat $$var_array_name]
+ if {[string index $mofo 0] == "$"} {
+ set mofo [string replace $mofo 0 0 #] }
+
+ pd [concat $id arraydialog $mofo \
+ [eval concat $$var_array_n] \
+ [eval concat $$var_array_saveit] \
+ [eval concat $$var_array_otherflag] \
+ \;]
+}
+
+proc array_cancel {id} {
+ set cmd [concat $id cancel \;]
+ pd $cmd
+}
+
+proc array_ok {id} {
+ array_apply $id
+ array_cancel $id
+}
+
+proc pdtk_array_dialog {id name n saveit newone} {
+ set vid [string trimleft $id .]
+
+ set var_array_name [concat array_name_$vid]
+ global $var_array_name
+ set var_array_n [concat array_n_$vid]
+ global $var_array_n
+ set var_array_saveit [concat array_saveit_$vid]
+ global $var_array_saveit
+ set var_array_otherflag [concat array_otherflag_$vid]
+ global $var_array_otherflag
+
+ set $var_array_name $name
+ set $var_array_n $n
+ set $var_array_saveit $saveit
+ set $var_array_otherflag 0
+
+ toplevel $id
+ wm title $id {array}
+ wm protocol $id WM_DELETE_WINDOW [concat array_cancel $id]
+
+ frame $id.name
+ pack $id.name -side top
+ label $id.name.label -text "name"
+ entry $id.name.entry -textvariable $var_array_name
+ pack $id.name.label $id.name.entry -side left
+
+ frame $id.n
+ pack $id.n -side top
+ label $id.n.label -text "size"
+ entry $id.n.entry -textvariable $var_array_n
+ pack $id.n.label $id.n.entry -side left
+
+ checkbutton $id.saveme -text {save contents} -variable $var_array_saveit \
+ -anchor w
+ pack $id.saveme -side top
+
+ if {$newone != 0} {
+ frame $id.radio
+ pack $id.radio -side top
+ radiobutton $id.radio.radio0 -value 0 \
+ -variable $var_array_otherflag \
+ -text "in new graph"
+ radiobutton $id.radio.radio1 -value 1 \
+ -variable $var_array_otherflag \
+ -text "in last graph"
+ pack $id.radio.radio0 -side top -anchor w
+ pack $id.radio.radio1 -side top -anchor w
+ } else {
+ checkbutton $id.deleteme -text {delete me} \
+ -variable $var_array_otherflag -anchor w
+ pack $id.deleteme -side top
+ }
+ frame $id.buttonframe
+ pack $id.buttonframe -side bottom -fill x -pady 2m
+ button $id.buttonframe.cancel -text {Cancel}\
+ -command "array_cancel $id"
+ if {$newone == 0} {button $id.buttonframe.apply -text {Apply}\
+ -command "array_apply $id"}
+ button $id.buttonframe.ok -text {OK}\
+ -command "array_ok $id"
+ pack $id.buttonframe.cancel -side left -expand 1
+ if {$newone == 0} {pack $id.buttonframe.apply -side left -expand 1}
+ pack $id.buttonframe.ok -side left -expand 1
+
+ bind $id.name.entry <KeyPress-Return> [concat array_ok $id]
+ bind $id.n.entry <KeyPress-Return> [concat array_ok $id]
+ $id.name.entry select from 0
+ $id.name.entry select adjust end
+ focus $id.name.entry
+}
+
+############ pdtk_canvas_dialog -- dialog window for canvass #########
+proc canvas_apply {id} {
+# strip "." from the TK id to make a variable name suffix
+ set vid [string trimleft $id .]
+# for each variable, make a local variable to hold its name...
+ set var_canvas_xscale [concat canvas_xscale_$vid]
+ global $var_canvas_xscale
+ set var_canvas_yscale [concat canvas_yscale_$vid]
+ global $var_canvas_yscale
+ set var_canvas_graphme [concat canvas_graphme_$vid]
+ global $var_canvas_graphme
+# set var_canvas_stretch [concat canvas_stretch_$vid]
+# global $var_canvas_stretch
+ pd [concat $id donecanvasdialog \
+ [eval concat $$var_canvas_xscale] \
+ [eval concat $$var_canvas_yscale] \
+ [eval concat $$var_canvas_graphme] \
+ \;]
+}
+
+proc canvas_cancel {id} {
+ set cmd [concat $id cancel \;]
+ pd $cmd
+}
+
+proc canvas_ok {id} {
+ canvas_apply $id
+ canvas_cancel $id
+}
+
+proc pdtk_canvas_dialog {id xscale yscale graphme stretch} {
+ set vid [string trimleft $id .]
+
+ set var_canvas_xscale [concat canvas_xscale_$vid]
+ global $var_canvas_xscale
+ set var_canvas_yscale [concat canvas_yscale_$vid]
+ global $var_canvas_yscale
+ set var_canvas_graphme [concat canvas_graphme_$vid]
+ global $var_canvas_graphme
+# set var_canvas_stretch [concat canvas_stretch_$vid]
+# global $var_canvas_stretch
+
+ set $var_canvas_xscale $xscale
+ set $var_canvas_yscale $yscale
+ set $var_canvas_graphme $graphme
+# set $var_canvas_stretch $stretch
+
+ toplevel $id
+ wm title $id {canvas}
+ wm protocol $id WM_DELETE_WINDOW [concat canvas_cancel $id]
+
+ frame $id.xscale
+ pack $id.xscale -side top
+ label $id.xscale.label -text "X units per pixel"
+ entry $id.xscale.entry -textvariable $var_canvas_xscale -width 10
+ pack $id.xscale.label $id.xscale.entry -side left
+
+ frame $id.yscale
+ pack $id.yscale -side top
+ label $id.yscale.label -text "Y units per pixel"
+ entry $id.yscale.entry -textvariable $var_canvas_yscale -width 10
+ pack $id.yscale.label $id.yscale.entry -side left
+
+ checkbutton $id.graphme -text {graph on parent} \
+ -variable $var_canvas_graphme -anchor w
+ pack $id.graphme -side top
+
+# checkbutton $id.stretch -text {stretch on resize} \
+# -variable $var_canvas_stretch -anchor w
+# pack $id.stretch -side top
+
+
+ frame $id.buttonframe
+ pack $id.buttonframe -side bottom -fill x -pady 2m
+ button $id.buttonframe.cancel -text {Cancel}\
+ -command "canvas_cancel $id"
+ button $id.buttonframe.apply -text {Apply}\
+ -command "canvas_apply $id"
+ button $id.buttonframe.ok -text {OK}\
+ -command "canvas_ok $id"
+ pack $id.buttonframe.cancel -side left -expand 1
+ pack $id.buttonframe.apply -side left -expand 1
+ pack $id.buttonframe.ok -side left -expand 1
+
+ bind $id.xscale.entry <KeyPress-Return> [concat canvas_ok $id]
+ bind $id.yscale.entry <KeyPress-Return> [concat canvas_ok $id]
+ $id.xscale.entry select from 0
+ $id.xscale.entry select adjust end
+ focus $id.xscale.entry
+}
+
+############ pdtk_data_dialog -- run a data dialog #########
+proc dodata_send {name} {
+# puts stderr [$name.text get 0.0 end]
+
+ for {set i 1} {[$name.text compare [concat $i.0 + 3 chars] < end]} \
+ {incr i 1} {
+# puts stderr [concat it's [$name.text get $i.0 [expr $i + 1].0]]
+ set cmd [concat $name data [$name.text get $i.0 [expr $i + 1].0] \;]
+# puts stderr $cmd
+ pd $cmd
+ }
+ set cmd [concat $name end \;]
+# puts stderr $cmd
+ pd $cmd
+}
+
+proc dodata_cancel {name} {
+ set cmd [concat $name cancel \;]
+# puts stderr $cmd
+ pd $cmd
+}
+
+proc dodata_ok {name} {
+ dodata_send $name
+ dodata_cancel $name
+}
+
+proc pdtk_data_dialog {name stuff} {
+
+ toplevel $name
+ wm title $name {Atom}
+ wm protocol $name WM_DELETE_WINDOW [concat dodata_cancel $name]
+
+ frame $name.buttonframe
+ pack $name.buttonframe -side bottom -fill x -pady 2m
+ button $name.buttonframe.send -text {Send (Ctrl s)}\
+ -command [concat dodata_send $name]
+ button $name.buttonframe.ok -text {OK (Ctrl t)}\
+ -command [concat dodata_ok $name]
+ pack $name.buttonframe.send -side left -expand 1
+ pack $name.buttonframe.ok -side left -expand 1
+
+ text $name.text -relief raised -bd 2 -height 40 -width 60 \
+ -yscrollcommand "$name.scroll set" -font -*-courier-bold--normal--12-*
+ scrollbar $name.scroll -command "$name.text yview"
+ pack $name.scroll -side right -fill y
+ pack $name.text -side left -fill both -expand 1
+ $name.text insert end $stuff
+ focus $name.text
+ bind $name.text <Control-t> [concat dodata_ok $name]
+ bind $name.text <Control-s> [concat dodata_send $name]
+}
+
+############ check or uncheck the "edit" menu item ##############
+#####################iemlib#######################
+proc pdtk_canvas_editval {name value} {
+ if { $value } {
+ $name.m.edit.m entryconfigure "Edit mode" -indicatoron true
+ } else {
+ $name.m.edit.m entryconfigure "Edit mode" -indicatoron false
+ }
+}
+
+proc pdtk_canvas_protectval {name value} {
+ if { $value } {
+ $name.m.edit.m entryconfigure "Protect" -indicatoron true
+ } else {
+ $name.m.edit.m entryconfigure "Protect" -indicatoron false
+ }
+}
+#####################iemlib#######################
+
+############ pdtk_text_new -- create a new text object #2###########
+proc pdtk_text_new {canvasname myname x y text font color} {
+# if {$font < 13} {set fontname [format -*-courier-bold----%d-* $font]}
+# if {$font >= 13} {set fontname [format -*-courier-----%d-* $font]}
+ $canvasname create text $x $y \
+ -font [format -*-courier-bold--normal--%d-* $font] \
+ -tags $myname -text $text -fill $color -anchor nw
+# pd [concat $myname size [$canvasname bbox $myname] \;]
+}
+
+################ pdtk_text_set -- change the text ##################
+proc pdtk_text_set {canvasname myname text} {
+ $canvasname itemconfig $myname -text $text
+# pd [concat $myname size [$canvasname bbox $myname] \;]
+}
+
+############### event binding procedures for Pd window ################
+
+proc pdtk_pd_ctrlkey {name key shift} {
+# puts stderr [concat key $key shift $shift]
+# .dummy itemconfig goo -text [concat ---> control-key event $key];
+ if {$key == "n" || $key == "N"} {menu_new}
+ if {$key == "o" || $key == "O"} {menu_open}
+ if {$key == "m" || $key == "M"} {menu_send}
+ if {$key == "q" || $key == "Q"} {
+ if {$shift == 1} {menu_really_quit} else {menu_quit}
+ }
+ if {$key == "slash"} {menu_audio 1}
+ if {$key == "period"} {menu_audio 0}
+}
+
+######### startup function. ##############
+# Tell pd the current directory; this is used in case the command line
+# asked pd to open something. Also, get character width and height for
+# font sizes 8, 10, 12, 14, 16, and 24.
+
+proc pdtk_pd_startup {version} {
+ global pd_myversion
+ set pd_myversion $version
+
+ set width1 [font measure -*-courier-bold--normal--8-* x]
+ set height1 [lindex [font metrics -*-courier-bold--normal--8-*] 5]
+
+ set width2 [font measure -*-courier-bold--normal--10-* x]
+ set height2 [lindex [font metrics -*-courier-bold--normal--10-*] 5]
+
+ set width3 [font measure -*-courier-bold--normal--12-* x]
+ set height3 [lindex [font metrics -*-courier-bold--normal--12-*] 5]
+
+ set width4 [font measure -*-courier-bold--normal--14-* x]
+ set height4 [lindex [font metrics -*-courier-bold--normal--14-*] 5]
+
+ set width5 [font measure -*-courier-bold--normal--16-* x]
+ set height5 [lindex [font metrics -*-courier-bold--normal--16-*] 5]
+
+ set width6 [font measure -*-courier-bold--normal--24-* x]
+ set height6 [lindex [font metrics -*-courier-bold--normal--24-*] 5]
+
+ set width7 [font measure -*-courier-bold--normal--36-* x]
+ set height7 [lindex [font metrics -*-courier-bold--normal--36-*] 5]
+
+ pd [concat pd init [pdtk_enquote [pwd]] \
+ 8 $width1 $height1 \
+ 10 $width2 $height2 \
+ 12 $width3 $height3 \
+ 14 $width4 $height4 \
+ 16 $width5 $height5 \
+ 24 $width6 $height6 \
+ 36 $width7 $height7 \
+ \;];
+}
+
+##################### DSP ON/OFF, METERS, DIO ERROR ###################
+proc pdtk_pd_dsp {value} {
+ global ctrls_audio_on
+ if {$value == "ON"} {set ctrls_audio_on 1} else {set ctrls_audio_on 0}
+# puts stderr [concat its $ctrls_audio_on]
+}
+
+proc pdtk_pd_meters {indb outdb inclip outclip} {
+# puts stderr [concat meters $indb $outdb $inclip $outclip]
+ global ctrls_inlevel ctrls_outlevel
+ set ctrls_inlevel $indb
+ if {$inclip == 1} {
+ .controls.in.clip configure -background red
+ } else {
+ .controls.in.clip configure -background grey
+ }
+ set ctrls_outlevel $outdb
+ if {$outclip == 1} {
+ .controls.out.clip configure -background red
+ } else {
+ .controls.out.clip configure -background grey
+ }
+
+}
+
+proc pdtk_pd_dio {red} {
+# puts stderr [concat dio $red]
+ if {$red == 1} {
+ .controls.dio configure -background red -activebackground red
+ } else {
+ .controls.dio configure -background grey -activebackground lightgrey
+ }
+
+}
+
+############# text editing from the "edit" menu ###################
+set edit_number 1
+
+proc texteditor_send {name} {
+ set topname [string trimright $name .text]
+ for {set i 0} \
+ {[$name compare [concat 0.0 + [expr $i + 1] chars] < end]} \
+ {incr i 1} {
+ set cha [$name get [concat 0.0 + $i chars]]
+ scan $cha %c keynum
+ pd [concat pd key 1 $keynum \;]
+ }
+}
+
+proc texteditor_ok {name} {
+ set topname [string trimright $name .text]
+ texteditor_send $name
+ destroy $topname
+}
+
+
+proc pdtk_pd_texteditor {stuff} {
+ global edit_number
+ set name [format ".text%d" $edit_number]
+ set edit_number [expr $edit_number + 1]
+
+ toplevel $name
+ wm title $name {TEXT}
+
+ frame $name.buttons
+ pack $name.buttons -side bottom -fill x -pady 2m
+ button $name.buttons.send -text {Send (Ctrl s)}\
+ -command "texteditor_send $name.text"
+ button $name.buttons.ok -text {OK (Ctrl t)}\
+ -command "texteditor_ok $name.text"
+ pack $name.buttons.send -side left -expand 1
+ pack $name.buttons.ok -side left -expand 1
+
+ text $name.text -relief raised -bd 2 -height 12 -width 60 \
+ -yscrollcommand "$name.scroll set" -font -*-courier-bold--normal--12-*
+ scrollbar $name.scroll -command "$name.text yview"
+ pack $name.scroll -side right -fill y
+ pack $name.text -side left -fill both -expand 1
+ $name.text insert end $stuff
+ focus $name.text
+ bind $name.text <Control-t> {texteditor_ok %W}
+ bind $name.text <Control-s> {texteditor_send %W}
+}
+
+############# open and save dialogs for objects in Pd ##########
+
+proc pdtk_openpanel {target} {
+ global pd_opendir
+ global pd_nt
+ if {$pd_nt == 2} {
+ cd $pd_opendir
+ set filename [tk_getOpenFile ]
+ } else {
+ set filename [tk_getOpenFile \
+ -initialdir $pd_opendir]
+ }
+ if {$filename != ""} {
+ set directory [string range $filename 0 \
+ [expr [string last / $filename ] - 1]]
+ set pd_opendir $directory
+
+ pd [concat $target symbol [pdtk_enquote $filename] \;]
+ }
+}
+
+proc pdtk_savepanel {target} {
+ set filename [tk_getSaveFile]
+ if {$filename != ""} {
+ pd [concat $target symbol [pdtk_enquote $filename] \;]
+ }
+}
+
+########################### comport hack ########################
+
+set com1 0
+set com2 0
+set com3 0
+set com4 0
+
+proc com1_open {} {
+ global com1
+ set com1 [open com1 w]
+ .dummy itemconfig goo -text $com1
+ fconfigure $com1 -buffering none
+ fconfigure $com1 -mode 19200,e,8,2
+}
+
+proc com1_send {str} {
+ global com1
+ puts -nonewline $com1 $str
+}
+
+
+############# start a polling process to watch the socket ##############
+# this is needed for nt, and presumably for Mac as well.
+# in UNIX this is handled by a tcl callback (set up in t_tkcmd.c)
+
+if {$pd_nt == 1} {
+ proc polleofloop {} {
+ pd_pollsocket
+ after 20 polleofloop
+ }
+
+ polleofloop
+}
+
diff --git a/pd/src/u_pdreceive.c b/pd/src/u_pdreceive.c
new file mode 100644
index 00000000..8d3f83e9
--- /dev/null
+++ b/pd/src/u_pdreceive.c
@@ -0,0 +1,305 @@
+/* Copyright (c) 2000 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in the Pd distribution. */
+
+/* the "pdreceive" command. This is a standalone program that receives messages
+from Pd via the netsend/netreceive ("FUDI") protocol, and copies them to
+standard output. */
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#ifdef UNIX
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#define SOCKET_ERROR -1
+#else
+#include <winsock.h>
+#endif
+
+typedef struct _fdpoll
+{
+ int fdp_fd;
+ char *fdp_inbuf;
+ int fdp_inhead;
+ int fdp_intail;
+ int fdp_udp;
+} t_fdpoll;
+
+static int nfdpoll;
+static t_fdpoll *fdpoll;
+static int maxfd;
+static int sockfd;
+static int protocol;
+
+static void sockerror(char *s);
+static void closesocket(int fd);
+static void dopoll(void);
+#define BUFSIZE 4096
+
+int main(int argc, char **argv)
+{
+ int portno;
+ struct sockaddr_in server;
+ int nretry = 10;
+ if (argc < 2 || sscanf(argv[1], "%d", &portno) < 1 || portno <= 0)
+ goto usage;
+ if (argc >= 3)
+ {
+ if (!strcmp(argv[2], "tcp"))
+ protocol = SOCK_STREAM;
+ else if (!strcmp(argv[2], "udp"))
+ protocol = SOCK_DGRAM;
+ else goto usage;
+ }
+ else protocol = SOCK_STREAM;
+ sockfd = socket(AF_INET, protocol, 0);
+ if (sockfd < 0)
+ {
+ sockerror("socket()");
+ exit(1);
+ }
+ maxfd = sockfd + 1;
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_ANY;
+
+#ifdef IRIX
+ /* this seems to work only in IRIX but is unnecessary in
+ Linux. Not sure what NT needs in place of this. */
+ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
+ post("setsockopt failed\n");
+#endif
+
+ /* assign client port number */
+ server.sin_port = htons((unsigned short)portno);
+
+ /* name the socket */
+ if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
+ {
+ sockerror("bind");
+ closesocket(sockfd);
+ return (0);
+ }
+ if (protocol == SOCK_STREAM)
+ {
+ if (listen(sockfd, 5) < 0)
+ {
+ sockerror("listen");
+ closesocket(sockfd);
+ exit(1);
+ }
+ }
+ /* now loop forever selecting on sockets */
+ while (1)
+ dopoll();
+
+usage:
+ fprintf(stderr, "usage: pdreceive <portnumber> [udp|tcp]\n");
+ fprintf(stderr, "(default is tcp)\n");
+ exit(1);
+}
+
+static void addport(int fd)
+{
+ int nfd = nfdpoll;
+ t_fdpoll *fp;
+ fdpoll = (t_fdpoll *)realloc(fdpoll,
+ (nfdpoll+1) * sizeof(t_fdpoll));
+ fp = fdpoll + nfdpoll;
+ fp->fdp_fd = fd;
+ nfdpoll++;
+ if (fd >= maxfd) maxfd = fd + 1;
+ fp->fdp_inhead = fp->fdp_intail = 0;
+ if (!(fp->fdp_inbuf = malloc(BUFSIZE)))
+ {
+ fprintf(stderr, "out of memory");
+ exit(1);
+ }
+ printf("number_connected %d;\n", nfdpoll);
+}
+
+static void rmport(t_fdpoll *x)
+{
+ int nfd = nfdpoll;
+ int i, size = nfdpoll * sizeof(t_fdpoll);
+ t_fdpoll *fp;
+ for (i = nfdpoll, fp = fdpoll; i--; fp++)
+ {
+ if (fp == x)
+ {
+ closesocket(fp->fdp_fd);
+ free(fp->fdp_inbuf);
+ while (i--)
+ {
+ fp[0] = fp[1];
+ fp++;
+ }
+ fdpoll = (t_fdpoll *)realloc(fdpoll,
+ (nfdpoll-1) * sizeof(t_fdpoll));
+ nfdpoll--;
+ printf("number_connected %d;\n", nfdpoll);
+ return;
+ }
+ }
+ fprintf(stderr, "warning: item removed from poll list but not found");
+}
+
+static void doconnect(void)
+{
+ int fd = accept(sockfd, 0, 0);
+ if (fd < 0)
+ perror("accept");
+ else addport(fd);
+}
+
+static void udpread(void)
+{
+ char buf[BUFSIZE];
+ int ret = recv(sockfd, buf, BUFSIZE, 0);
+ if (ret < 0)
+ {
+ sockerror("recv (udp)");
+ close(sockfd);
+ exit(1);
+ }
+ else if (ret > 0)
+ {
+ if (write(1, buf, ret) < ret)
+ {
+ perror("write");
+ exit(1);
+ }
+ }
+}
+
+static int tcpmakeoutput(t_fdpoll *x)
+{
+ char messbuf[BUFSIZE+1], *bp = messbuf;
+ int indx;
+ int inhead = x->fdp_inhead;
+ int intail = x->fdp_intail;
+ char *inbuf = x->fdp_inbuf;
+ if (intail == inhead)
+ return (0);
+ for (indx = intail; indx != inhead; indx = (indx+1)&(BUFSIZE-1))
+ {
+ /* search for a semicolon. */
+ char c = *bp++ = inbuf[indx];
+ if (c == ';')
+ {
+ intail = (indx+1)&(BUFSIZE-1);
+ if (inbuf[intail] == '\n')
+ intail = (intail+1)&(BUFSIZE-1);
+ *bp++ = '\n';
+ write(1, messbuf, bp - messbuf);
+ x->fdp_inhead = inhead;
+ x->fdp_intail = intail;
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static void tcpread(t_fdpoll *x)
+{
+ char *semi;
+ int readto =
+ (x->fdp_inhead >= x->fdp_intail ? BUFSIZE : x->fdp_intail-1);
+ int ret;
+
+ /* the input buffer might be full. If so, drop the whole thing */
+ if (readto == x->fdp_inhead)
+ {
+ fprintf(stderr, "pd: dropped message from gui\n");
+ x->fdp_inhead = x->fdp_intail = 0;
+ readto = BUFSIZE;
+ }
+ else
+ {
+ ret = recv(x->fdp_fd, x->fdp_inbuf + x->fdp_inhead,
+ readto - x->fdp_inhead, 0);
+ if (ret < 0)
+ {
+ sockerror("recv (tcp)");
+ rmport(x);
+ }
+ else if (ret == 0)
+ rmport(x);
+ else
+ {
+ x->fdp_inhead += ret;
+ if (x->fdp_inhead >= BUFSIZE)
+ x->fdp_inhead = 0;
+ while (tcpmakeoutput(x))
+ ;
+ }
+ }
+}
+
+static void dopoll(void)
+{
+ int i;
+ t_fdpoll *fp;
+ fd_set readset, writeset, exceptset;
+ FD_ZERO(&writeset);
+ FD_ZERO(&readset);
+ FD_ZERO(&exceptset);
+
+ FD_SET(sockfd, &readset);
+ if (protocol == SOCK_STREAM)
+ {
+ for (fp = fdpoll, i = nfdpoll; i--; fp++)
+ FD_SET(fp->fdp_fd, &readset);
+ }
+ if (select(maxfd+1, &readset, &writeset, &exceptset, 0) < 0)
+ {
+ perror("select");
+ exit(1);
+ }
+ if (protocol == SOCK_STREAM)
+ {
+ for (i = 0; i < nfdpoll; i++)
+ if (FD_ISSET(fdpoll[i].fdp_fd, &readset))
+ tcpread(&fdpoll[i]);
+ if (FD_ISSET(sockfd, &readset))
+ doconnect();
+ }
+ else
+ {
+ if (FD_ISSET(sockfd, &readset))
+ udpread();
+ }
+}
+
+
+static void sockerror(char *s)
+{
+#ifdef NT
+ int err = WSAGetLastError();
+ if (err == 10054) return;
+ else if (err == 10044)
+ {
+ fprintf(stderr,
+ "Warning: you might not have TCP/IP \"networking\" turned on\n");
+ }
+#endif
+#ifdef UNIX
+ int err = errno;
+#endif
+ fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err);
+}
+
+static void closesocket(int fd)
+{
+#ifdef UNIX
+ close(fd);
+#endif
+#ifdef NT
+ closesocket(fd);
+#endif
+}
diff --git a/pd/src/u_pdsend.c b/pd/src/u_pdsend.c
new file mode 100644
index 00000000..87e7150d
--- /dev/null
+++ b/pd/src/u_pdsend.c
@@ -0,0 +1,150 @@
+/* Copyright (c) 2000 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in the Pd distribution. */
+
+/* the "pdsend" command. This is a standalone program that forwards messages
+from its standard input to Pd via the netsend/netreceive ("FUDI") protocol. */
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#ifdef UNIX
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#define SOCKET_ERROR -1
+#else
+#include <winsock.h>
+#endif
+
+void sockerror(char *s);
+void closesocket(int fd);
+#define BUFSIZE 4096
+
+int main(int argc, char **argv)
+{
+ int sockfd, portno, protocol;
+ struct sockaddr_in server;
+ struct hostent *hp;
+ char *hostname;
+ int nretry = 10;
+ if (argc < 2 || sscanf(argv[1], "%d", &portno) < 1 || portno <= 0)
+ goto usage;
+ if (argc >= 3)
+ hostname = argv[2];
+ else hostname = "127.0.0.1";
+ if (argc >= 4)
+ {
+ if (!strcmp(argv[3], "tcp"))
+ protocol = SOCK_STREAM;
+ else if (!strcmp(argv[3], "udp"))
+ protocol = SOCK_DGRAM;
+ else goto usage;
+ }
+ else protocol = SOCK_STREAM;
+
+ sockfd = socket(AF_INET, protocol, 0);
+ if (sockfd < 0)
+ {
+ sockerror("socket()");
+ exit(1);
+ }
+ /* connect socket using hostname provided in command line */
+ server.sin_family = AF_INET;
+ hp = gethostbyname(hostname);
+ if (hp == 0)
+ {
+ fprintf(stderr, "%s: unknown host\n", hostname);
+ closesocket(sockfd);
+ exit(1);
+ }
+ memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+
+ /* assign client port number */
+ server.sin_port = htons((unsigned short)portno);
+
+#if 0 /* try this again for 4.0; this crashed my RH 6.2 machine!) */
+
+ /* try to connect. */
+ for (nretry = 0; nretry < (protocol == SOCK_STREAM ? 10 : 1); nretry++)
+
+ {
+ if (nretry > 0)
+ {
+ sleep (nretry < 5 ? 1 : 5);
+ fprintf(stderr, "retrying...");
+ }
+ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) >= 0)
+ goto connected;
+ sockerror("connect");
+ }
+ closesocket(sockfd);
+ exit(1);
+connected: ;
+#else
+ /* try to connect. */
+ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0)
+ {
+ sockerror("connect");
+ closesocket(sockfd);
+ exit(1);
+ }
+#endif
+ /* now loop reading stdin and sending it to socket */
+ while (1)
+ {
+ char buf[BUFSIZE], *bp, nsent, nsend;
+ if (!fgets(buf, BUFSIZE, stdin))
+ break;
+ nsend = strlen(buf);
+ for (bp = buf, nsent = 0; nsent < nsend;)
+ {
+ int res = send(sockfd, buf, nsend-nsent, 0);
+ if (res < 0)
+ {
+ sockerror("send");
+ goto done;
+ }
+ nsent += res;
+ bp += res;
+ }
+ }
+done:
+ if (ferror(stdin))
+ perror("stdin");
+ exit (0);
+usage:
+ fprintf(stderr, "usage: pdsend <portnumber> [host] [udp|tcp]\n");
+ fprintf(stderr, "(default is localhost and tcp)\n");
+ exit(1);
+}
+
+void sockerror(char *s)
+{
+#ifdef NT
+ int err = WSAGetLastError();
+ if (err == 10054) return;
+ else if (err == 10044)
+ {
+ fprintf(stderr,
+ "Warning: you might not have TCP/IP \"networking\" turned on\n");
+ }
+#endif
+#ifdef UNIX
+ int err = errno;
+#endif
+ fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err);
+}
+
+void closesocket(int fd)
+{
+#ifdef UNIX
+ close(fd);
+#endif
+#ifdef NT
+ closesocket(fd);
+#endif
+}
diff --git a/pd/src/x_acoustics.c b/pd/src/x_acoustics.c
new file mode 100644
index 00000000..ee7250df
--- /dev/null
+++ b/pd/src/x_acoustics.c
@@ -0,0 +1,193 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* utility functions for signals
+*/
+
+#include "m_pd.h"
+#include <math.h>
+#define LOGTEN 2.302585092994
+
+float mtof(float f)
+{
+ if (f <= -1500) return(0);
+ else if (f > 1499) return(mtof(1499));
+ else return (8.17579891564 * exp(.0577622650 * f));
+}
+
+float ftom(float f)
+{
+ return (f > 0 ? 17.3123405046 * log(.12231220585 * f) : -1500);
+}
+
+float powtodb(float f)
+{
+ if (f <= 0) return (0);
+ else
+ {
+ float val = 100 + 10./LOGTEN * log(f);
+ return (val < 0 ? 0 : val);
+ }
+}
+
+float rmstodb(float f)
+{
+ if (f <= 0) return (0);
+ else
+ {
+ float val = 100 + 20./LOGTEN * log(f);
+ return (val < 0 ? 0 : val);
+ }
+}
+
+float dbtopow(float f)
+{
+ if (f <= 0)
+ return(0);
+ else
+ {
+ if (f > 870)
+ f = 870;
+ return (exp((LOGTEN * 0.1) * (f-100.)));
+ }
+}
+
+float dbtorms(float f)
+{
+ if (f <= 0)
+ return(0);
+ else
+ {
+ if (f > 485)
+ f = 485;
+ }
+ return (exp((LOGTEN * 0.05) * (f-100.)));
+}
+
+/* ------------- corresponding objects ----------------------- */
+
+static t_class *mtof_class;
+
+static void *mtof_new(void)
+{
+ t_object *x = (t_object *)pd_new(mtof_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+static void mtof_float(t_object *x, t_float f)
+{
+ outlet_float(x->ob_outlet, mtof(f));
+}
+
+
+static t_class *ftom_class;
+
+static void *ftom_new(void)
+{
+ t_object *x = (t_object *)pd_new(ftom_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+static void ftom_float(t_object *x, t_float f)
+{
+ outlet_float(x->ob_outlet, ftom(f));
+}
+
+
+static t_class *rmstodb_class;
+
+static void *rmstodb_new(void)
+{
+ t_object *x = (t_object *)pd_new(rmstodb_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+static void rmstodb_float(t_object *x, t_float f)
+{
+ outlet_float(x->ob_outlet, rmstodb(f));
+}
+
+
+static t_class *powtodb_class;
+
+static void *powtodb_new(void)
+{
+ t_object *x = (t_object *)pd_new(powtodb_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+static void powtodb_float(t_object *x, t_float f)
+{
+ outlet_float(x->ob_outlet, powtodb(f));
+}
+
+
+static t_class *dbtopow_class;
+
+static void *dbtopow_new(void)
+{
+ t_object *x = (t_object *)pd_new(dbtopow_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+static void dbtopow_float(t_object *x, t_float f)
+{
+ outlet_float(x->ob_outlet, dbtopow(f));
+}
+
+
+static t_class *dbtorms_class;
+
+static void *dbtorms_new(void)
+{
+ t_object *x = (t_object *)pd_new(dbtorms_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+static void dbtorms_float(t_object *x, t_float f)
+{
+ outlet_float(x->ob_outlet, dbtorms(f));
+}
+
+
+void x_acoustics_setup(void)
+{
+ t_symbol *s = gensym("acoustics.pd");
+ mtof_class = class_new(gensym("mtof"), mtof_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(mtof_class, (t_method)mtof_float);
+ class_sethelpsymbol(mtof_class, s);
+
+ ftom_class = class_new(gensym("ftom"), ftom_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(ftom_class, (t_method)ftom_float);
+ class_sethelpsymbol(ftom_class, s);
+
+ powtodb_class = class_new(gensym("powtodb"), powtodb_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(powtodb_class, (t_method)powtodb_float);
+ class_sethelpsymbol(powtodb_class, s);
+
+ rmstodb_class = class_new(gensym("rmstodb"), rmstodb_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(rmstodb_class, (t_method)rmstodb_float);
+ class_sethelpsymbol(rmstodb_class, s);
+
+ dbtopow_class = class_new(gensym("dbtopow"), dbtopow_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(dbtopow_class, (t_method)dbtopow_float);
+ class_sethelpsymbol(dbtopow_class, s);
+
+ dbtorms_class = class_new(gensym("dbtorms"), dbtorms_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(dbtorms_class, (t_method)dbtorms_float);
+ class_sethelpsymbol(dbtorms_class, s);
+}
+
diff --git a/pd/src/x_arithmetic.c b/pd/src/x_arithmetic.c
new file mode 100644
index 00000000..f975f542
--- /dev/null
+++ b/pd/src/x_arithmetic.c
@@ -0,0 +1,898 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* arithmetic: binops ala C language. The 4 functions and relationals are
+done on floats; the logical and bitwise binops convert their
+inputs to int and their outputs back to float. */
+
+#include "m_pd.h"
+#include <math.h>
+
+
+/* NT and OSX don't appear to have single-precision ANSI math */
+#if defined(NT) || defined(MACOSX)
+#define sinf sin
+#define cosf cos
+#define atanf atan
+#define atan2f atan2
+#define sqrtf sqrt
+#define logf log
+#define expf exp
+#define fabsf fabs
+#define powf pow
+#endif
+
+typedef struct _binop
+{
+ t_object x_obj;
+ t_float x_f1;
+ t_float x_f2;
+} t_binop;
+
+/* ------------------ binop1: +, -, *, / ----------------------------- */
+
+static void *binop1_new(t_class *floatclass, t_floatarg f)
+{
+ t_binop *x = (t_binop *)pd_new(floatclass);
+ outlet_new(&x->x_obj, &s_float);
+ floatinlet_new(&x->x_obj, &x->x_f2);
+ x->x_f1 = 0;
+ x->x_f2 = f;
+ return (x);
+}
+
+/* --------------------- addition ------------------------------- */
+
+static t_class *binop1_plus_class;
+
+static void *binop1_plus_new(t_floatarg f)
+{
+ return (binop1_new(binop1_plus_class, f));
+}
+
+static void binop1_plus_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_f1 + x->x_f2);
+}
+
+static void binop1_plus_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) + x->x_f2);
+}
+
+/* --------------------- subtraction ------------------------------- */
+
+static t_class *binop1_minus_class;
+
+static void *binop1_minus_new(t_floatarg f)
+{
+ return (binop1_new(binop1_minus_class, f));
+}
+
+static void binop1_minus_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_f1 - x->x_f2);
+}
+
+static void binop1_minus_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) - x->x_f2);
+}
+
+/* --------------------- multiplication ------------------------------- */
+
+static t_class *binop1_times_class;
+
+static void *binop1_times_new(t_floatarg f)
+{
+ return (binop1_new(binop1_times_class, f));
+}
+
+static void binop1_times_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_f1 * x->x_f2);
+}
+
+static void binop1_times_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) * x->x_f2);
+}
+
+/* --------------------- division ------------------------------- */
+
+static t_class *binop1_div_class;
+
+static void *binop1_div_new(t_floatarg f)
+{
+ return (binop1_new(binop1_div_class, f));
+}
+
+static void binop1_div_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet,
+ (x->x_f2 != 0 ? x->x_f1 / x->x_f2 : 0));
+}
+
+static void binop1_div_float(t_binop *x, t_float f)
+{
+ x->x_f1 = f;
+ outlet_float(x->x_obj.ob_outlet,
+ (x->x_f2 != 0 ? x->x_f1 / x->x_f2 : 0));
+}
+
+/* ------------------------ pow -------------------------------- */
+
+static t_class *binop1_pow_class;
+
+static void *binop1_pow_new(t_floatarg f)
+{
+ return (binop1_new(binop1_pow_class, f));
+}
+
+static void binop1_pow_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet,
+ (x->x_f1 > 0 ? powf(x->x_f1, x->x_f2) : 0));
+}
+
+static void binop1_pow_float(t_binop *x, t_float f)
+{
+ x->x_f1 = f;
+ outlet_float(x->x_obj.ob_outlet,
+ (x->x_f1 > 0 ? powf(x->x_f1, x->x_f2) : 0));
+}
+
+/* ------------------------ max -------------------------------- */
+
+static t_class *binop1_max_class;
+
+static void *binop1_max_new(t_floatarg f)
+{
+ return (binop1_new(binop1_max_class, f));
+}
+
+static void binop1_max_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet,
+ (x->x_f1 > x->x_f2 ? x->x_f1 : x->x_f2));
+}
+
+static void binop1_max_float(t_binop *x, t_float f)
+{
+ x->x_f1 = f;
+ outlet_float(x->x_obj.ob_outlet,
+ (x->x_f1 > x->x_f2 ? x->x_f1 : x->x_f2));
+}
+
+/* ------------------------ min -------------------------------- */
+
+static t_class *binop1_min_class;
+
+static void *binop1_min_new(t_floatarg f)
+{
+ return (binop1_new(binop1_min_class, f));
+}
+
+static void binop1_min_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet,
+ (x->x_f1 < x->x_f2 ? x->x_f1 : x->x_f2));
+}
+
+static void binop1_min_float(t_binop *x, t_float f)
+{
+ x->x_f1 = f;
+ outlet_float(x->x_obj.ob_outlet,
+ (x->x_f1 < x->x_f2 ? x->x_f1 : x->x_f2));
+}
+
+/* ------------------ binop2: ==, !=, >, <, >=, <=. -------------------- */
+
+static void *binop2_new(t_class *floatclass, t_floatarg f)
+{
+ t_binop *x = (t_binop *)pd_new(floatclass);
+ outlet_new(&x->x_obj, &s_float);
+ floatinlet_new(&x->x_obj, &x->x_f2);
+ x->x_f1 = 0;
+ x->x_f2 = f;
+ return (x);
+}
+
+/* --------------------- == ------------------------------- */
+
+static t_class *binop2_ee_class;
+
+static void *binop2_ee_new(t_floatarg f)
+{
+ return (binop2_new(binop2_ee_class, f));
+}
+
+static void binop2_ee_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_f1 == x->x_f2);
+}
+
+static void binop2_ee_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) == x->x_f2);
+}
+
+/* --------------------- != ------------------------------- */
+
+static t_class *binop2_ne_class;
+
+static void *binop2_ne_new(t_floatarg f)
+{
+ return (binop2_new(binop2_ne_class, f));
+}
+
+static void binop2_ne_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_f1 != x->x_f2);
+}
+
+static void binop2_ne_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) != x->x_f2);
+}
+
+/* --------------------- > ------------------------------- */
+
+static t_class *binop2_gt_class;
+
+static void *binop2_gt_new(t_floatarg f)
+{
+ return (binop2_new(binop2_gt_class, f));
+}
+
+static void binop2_gt_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_f1 > x->x_f2);
+}
+
+static void binop2_gt_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) > x->x_f2);
+}
+
+/* --------------------- < ------------------------------- */
+
+static t_class *binop2_lt_class;
+
+static void *binop2_lt_new(t_floatarg f)
+{
+ return (binop2_new(binop2_lt_class, f));
+}
+
+static void binop2_lt_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_f1 < x->x_f2);
+}
+
+static void binop2_lt_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) < x->x_f2);
+}
+
+/* --------------------- >= ------------------------------- */
+
+static t_class *binop2_ge_class;
+
+static void *binop2_ge_new(t_floatarg f)
+{
+ return (binop2_new(binop2_ge_class, f));
+}
+
+static void binop2_ge_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_f1 >= x->x_f2);
+}
+
+static void binop2_ge_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) >= x->x_f2);
+}
+
+/* --------------------- <= ------------------------------- */
+
+static t_class *binop2_le_class;
+
+static void *binop2_le_new(t_floatarg f)
+{
+ return (binop2_new(binop2_le_class, f));
+}
+
+static void binop2_le_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_f1 <= x->x_f2);
+}
+
+static void binop2_le_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) <= x->x_f2);
+}
+
+/* ------------- binop3: &, |, &&, ||, <<, >>, %, mod, div ------------------ */
+
+static void *binop3_new(t_class *fixclass, t_floatarg f)
+{
+ t_binop *x = (t_binop *)pd_new(fixclass);
+ outlet_new(&x->x_obj, &s_float);
+ floatinlet_new(&x->x_obj, &x->x_f2);
+ x->x_f1 = 0;
+ x->x_f2 = f;
+ return (x);
+}
+
+/* --------------------------- & ---------------------------- */
+
+static t_class *binop3_ba_class;
+
+static void *binop3_ba_new(t_floatarg f)
+{
+ return (binop3_new(binop3_ba_class, f));
+}
+
+static void binop2_ba_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) & (int)(x->x_f2));
+}
+
+static void binop2_ba_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) & (int)(x->x_f2));
+}
+
+/* --------------------------- && ---------------------------- */
+
+static t_class *binop3_la_class;
+
+static void *binop3_la_new(t_floatarg f)
+{
+ return (binop3_new(binop3_la_class, f));
+}
+
+static void binop2_la_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) && (int)(x->x_f2));
+}
+
+static void binop2_la_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) && (int)(x->x_f2));
+}
+
+/* --------------------------- | ---------------------------- */
+
+static t_class *binop3_bo_class;
+
+static void *binop3_bo_new(t_floatarg f)
+{
+ return (binop3_new(binop3_bo_class, f));
+}
+
+static void binop2_bo_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) | (int)(x->x_f2));
+}
+
+static void binop2_bo_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) | (int)(x->x_f2));
+}
+
+/* --------------------------- || ---------------------------- */
+
+static t_class *binop3_lo_class;
+
+static void *binop3_lo_new(t_floatarg f)
+{
+ return (binop3_new(binop3_lo_class, f));
+}
+
+static void binop2_lo_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) || (int)(x->x_f2));
+}
+
+static void binop2_lo_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) || (int)(x->x_f2));
+}
+
+/* --------------------------- << ---------------------------- */
+
+static t_class *binop3_ls_class;
+
+static void *binop3_ls_new(t_floatarg f)
+{
+ return (binop3_new(binop3_ls_class, f));
+}
+
+static void binop2_ls_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) << (int)(x->x_f2));
+}
+
+static void binop2_ls_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) << (int)(x->x_f2));
+}
+
+/* --------------------------- >> ---------------------------- */
+
+static t_class *binop3_rs_class;
+
+static void *binop3_rs_new(t_floatarg f)
+{
+ return (binop3_new(binop3_rs_class, f));
+}
+
+static void binop2_rs_bang(t_binop *x)
+{
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) >> (int)(x->x_f2));
+}
+
+static void binop2_rs_float(t_binop *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) >> (int)(x->x_f2));
+}
+
+/* --------------------------- % ---------------------------- */
+
+static t_class *binop3_pc_class;
+
+static void *binop3_pc_new(t_floatarg f)
+{
+ return (binop3_new(binop3_pc_class, f));
+}
+
+static void binop2_pc_bang(t_binop *x)
+{
+ int n2 = x->x_f2;
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) % (n2 ? n2 : 1));
+}
+
+static void binop2_pc_float(t_binop *x, t_float f)
+{
+ int n2 = x->x_f2;
+ outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) % (n2 ? n2 : 1));
+}
+
+/* --------------------------- mod ---------------------------- */
+
+static t_class *binop3_mod_class;
+
+static void *binop3_mod_new(t_floatarg f)
+{
+ return (binop3_new(binop3_mod_class, f));
+}
+
+static void binop3_mod_bang(t_binop *x)
+{
+ int n2 = x->x_f2, result;
+ if (n2 < 0) n2 = -n2;
+ else if (!n2) n2 = 1;
+ result = ((int)(x->x_f1)) % n2;
+ if (result < 0) result += n2;
+ outlet_float(x->x_obj.ob_outlet, (t_float)result);
+}
+
+static void binop3_mod_float(t_binop *x, t_float f)
+{
+ x->x_f1 = f;
+ binop3_mod_bang(x);
+}
+
+/* --------------------------- div ---------------------------- */
+
+static t_class *binop3_div_class;
+
+static void *binop3_div_new(t_floatarg f)
+{
+ return (binop3_new(binop3_div_class, f));
+}
+
+static void binop3_div_bang(t_binop *x)
+{
+ int n1 = x->x_f1, n2 = x->x_f2, result;
+ if (n2 < 0) n2 = -n2;
+ else if (!n2) n2 = 1;
+ if (n1 < 0) n1 -= (n2-1);
+ result = n1 / n2;
+ outlet_float(x->x_obj.ob_outlet, (t_float)result);
+}
+
+static void binop3_div_float(t_binop *x, t_float f)
+{
+ x->x_f1 = f;
+ binop3_div_bang(x);
+}
+
+/* -------------------- mathematical functions ------------------ */
+
+static t_class *sin_class; /* ----------- sin --------------- */
+
+static void *sin_new(void)
+{
+ t_object *x = (t_object *)pd_new(sin_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+static void sin_float(t_object *x, t_float f)
+{
+ outlet_float(x->ob_outlet, sinf(f));
+}
+
+static t_class *cos_class; /* ----------- cos --------------- */
+
+static void *cos_new(void)
+{
+ t_object *x = (t_object *)pd_new(cos_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+static void cos_float(t_object *x, t_float f)
+{
+ outlet_float(x->ob_outlet, cosf(f));
+}
+
+static t_class *tan_class; /* ----------- tan --------------- */
+
+static void *tan_new(void)
+{
+ t_object *x = (t_object *)pd_new(tan_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+static void tan_float(t_object *x, t_float f)
+{
+ float c = cosf(f);
+ float t = (c == 0 ? 0 : sinf(f)/c);
+ outlet_float(x->ob_outlet, t);
+}
+
+static t_class *atan_class; /* ----------- atan --------------- */
+
+static void *atan_new(void)
+{
+ t_object *x = (t_object *)pd_new(atan_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+static void atan_float(t_object *x, t_float f)
+{
+ outlet_float(x->ob_outlet, atanf(f));
+}
+
+static t_class *atan2_class; /* ----------- atan2 --------------- */
+
+typedef struct _atan2
+{
+ t_object x_ob;
+ float x_y;
+} t_atan2;
+
+static void *atan2_new(void)
+{
+ t_atan2 *x = (t_atan2 *)pd_new(atan2_class);
+ floatinlet_new(&x->x_ob, &x->x_y);
+ outlet_new(&x->x_ob, &s_float);
+ return (x);
+}
+
+static void atan2_float(t_atan2 *x, t_float f)
+{
+ float r = (f == 0 && x->x_y == 0 ? 0 : atan2f(x->x_y, f));
+ outlet_float(x->x_ob.ob_outlet, r);
+}
+
+static t_class *sqrt_class; /* ----------- sqrt --------------- */
+
+static void *sqrt_new(void)
+{
+ t_object *x = (t_object *)pd_new(sqrt_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+static void sqrt_float(t_object *x, t_float f)
+{
+ float r = (f > 0 ? sqrtf(f) : 0);
+ outlet_float(x->ob_outlet, r);
+}
+
+static t_class *log_class; /* ----------- log --------------- */
+
+static void *log_new(void)
+{
+ t_object *x = (t_object *)pd_new(log_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+static void log_float(t_object *x, t_float f)
+{
+ float r = (f > 0 ? logf(f) : -1000);
+ outlet_float(x->ob_outlet, r);
+}
+
+
+static t_class *exp_class; /* ----------- exp --------------- */
+
+static void *exp_new(void)
+{
+ t_object *x = (t_object *)pd_new(exp_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+#define MAXLOG 87.3365
+static void exp_float(t_object *x, t_float f)
+{
+ float g;
+#ifdef NT
+ char buf[10];
+#endif
+ if (f > MAXLOG) f = MAXLOG;
+ g = expf(f);
+ outlet_float(x->ob_outlet, g);
+}
+
+static t_class *abs_class; /* ----------- abs --------------- */
+
+static void *abs_new(void)
+{
+ t_object *x = (t_object *)pd_new(abs_class);
+ outlet_new(x, &s_float);
+ return (x);
+}
+
+static void abs_float(t_object *x, t_float f)
+{
+ outlet_float(x->ob_outlet, fabsf(f));
+}
+
+/* ------------------------ misc ------------------------ */
+
+static t_class *clip_class;
+
+typedef struct _clip
+{
+ t_object x_ob;
+ t_outlet *x_out2;
+ float x_f1;
+ float x_f2;
+} t_clip;
+
+static void *clip_new(t_floatarg f1, t_floatarg f2)
+{
+ t_clip *x = (t_clip *)pd_new(clip_class);
+ floatinlet_new(&x->x_ob, &x->x_f1);
+ floatinlet_new(&x->x_ob, &x->x_f2);
+ outlet_new(&x->x_ob, &s_float);
+ x->x_out2 = outlet_new(&x->x_ob, &s_float);
+ x->x_f1 = f1;
+ x->x_f2 = f2;
+ return (x);
+}
+
+static void clip_float(t_clip *x, t_float f)
+{
+ outlet_float(x->x_ob.ob_outlet, (f < x->x_f1 ? x->x_f1 : (
+ f > x->x_f2 ? x->x_f2 : f)));
+}
+
+static void clip_setup(void)
+{
+ clip_class = class_new(gensym("clip"), (t_newmethod)clip_new, 0,
+ sizeof(t_clip), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_addfloat(clip_class, clip_float);
+}
+
+void x_arithmetic_setup(void)
+{
+ t_symbol *binop1_sym = gensym("operators");
+ t_symbol *binop23_sym = gensym("otherbinops");
+ t_symbol *math_sym = gensym("math");
+
+ binop1_plus_class = class_new(gensym("+"), (t_newmethod)binop1_plus_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop1_plus_class, binop1_plus_bang);
+ class_addfloat(binop1_plus_class, (t_method)binop1_plus_float);
+ class_sethelpsymbol(binop1_plus_class, binop1_sym);
+
+ binop1_minus_class = class_new(gensym("-"),
+ (t_newmethod)binop1_minus_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop1_minus_class, binop1_minus_bang);
+ class_addfloat(binop1_minus_class, (t_method)binop1_minus_float);
+ class_sethelpsymbol(binop1_minus_class, binop1_sym);
+
+ binop1_times_class = class_new(gensym("*"),
+ (t_newmethod)binop1_times_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop1_times_class, binop1_times_bang);
+ class_addfloat(binop1_times_class, (t_method)binop1_times_float);
+ class_sethelpsymbol(binop1_times_class, binop1_sym);
+
+ binop1_div_class = class_new(gensym("/"),
+ (t_newmethod)binop1_div_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop1_div_class, binop1_div_bang);
+ class_addfloat(binop1_div_class, (t_method)binop1_div_float);
+ class_sethelpsymbol(binop1_div_class, binop1_sym);
+
+ binop1_pow_class = class_new(gensym("pow"),
+ (t_newmethod)binop1_pow_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop1_pow_class, binop1_pow_bang);
+ class_addfloat(binop1_pow_class, (t_method)binop1_pow_float);
+ class_sethelpsymbol(binop1_pow_class, binop1_sym);
+
+ binop1_max_class = class_new(gensym("max"),
+ (t_newmethod)binop1_max_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop1_max_class, binop1_max_bang);
+ class_addfloat(binop1_max_class, (t_method)binop1_max_float);
+ class_sethelpsymbol(binop1_max_class, binop1_sym);
+
+ binop1_min_class = class_new(gensym("min"),
+ (t_newmethod)binop1_min_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop1_min_class, binop1_min_bang);
+ class_addfloat(binop1_min_class, (t_method)binop1_min_float);
+ class_sethelpsymbol(binop1_min_class, binop1_sym);
+
+ /* ------------------ binop2 ----------------------- */
+
+ binop2_ee_class = class_new(gensym("=="), (t_newmethod)binop2_ee_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop2_ee_class, binop2_ee_bang);
+ class_addfloat(binop2_ee_class, (t_method)binop2_ee_float);
+ class_sethelpsymbol(binop2_ee_class, binop23_sym);
+
+ binop2_ne_class = class_new(gensym("!="), (t_newmethod)binop2_ne_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop2_ne_class, binop2_ne_bang);
+ class_addfloat(binop2_ne_class, (t_method)binop2_ne_float);
+ class_sethelpsymbol(binop2_ne_class, binop23_sym);
+
+ binop2_gt_class = class_new(gensym(">"), (t_newmethod)binop2_gt_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop2_gt_class, binop2_gt_bang);
+ class_addfloat(binop2_gt_class, (t_method)binop2_gt_float);
+ class_sethelpsymbol(binop2_gt_class, binop23_sym);
+
+ binop2_lt_class = class_new(gensym("<"), (t_newmethod)binop2_lt_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop2_lt_class, binop2_lt_bang);
+ class_addfloat(binop2_lt_class, (t_method)binop2_lt_float);
+ class_sethelpsymbol(binop2_lt_class, binop23_sym);
+
+ binop2_ge_class = class_new(gensym(">="), (t_newmethod)binop2_ge_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop2_ge_class, binop2_ge_bang);
+ class_addfloat(binop2_ge_class, (t_method)binop2_ge_float);
+ class_sethelpsymbol(binop2_ge_class, binop23_sym);
+
+ binop2_le_class = class_new(gensym("<="), (t_newmethod)binop2_le_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop2_le_class, binop2_le_bang);
+ class_addfloat(binop2_le_class, (t_method)binop2_le_float);
+ class_sethelpsymbol(binop2_le_class, binop23_sym);
+
+ /* ------------------ binop3 ----------------------- */
+
+ binop3_ba_class = class_new(gensym("&"), (t_newmethod)binop3_ba_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop3_ba_class, binop2_ba_bang);
+ class_addfloat(binop3_ba_class, (t_method)binop2_ba_float);
+ class_sethelpsymbol(binop3_ba_class, binop23_sym);
+
+ binop3_la_class = class_new(gensym("&&"), (t_newmethod)binop3_la_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop3_la_class, binop2_la_bang);
+ class_addfloat(binop3_la_class, (t_method)binop2_la_float);
+ class_sethelpsymbol(binop3_la_class, binop23_sym);
+
+ binop3_bo_class = class_new(gensym("|"), (t_newmethod)binop3_bo_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop3_bo_class, binop2_bo_bang);
+ class_addfloat(binop3_bo_class, (t_method)binop2_bo_float);
+ class_sethelpsymbol(binop3_bo_class, binop23_sym);
+
+ binop3_lo_class = class_new(gensym("||"), (t_newmethod)binop3_lo_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop3_lo_class, binop2_lo_bang);
+ class_addfloat(binop3_lo_class, (t_method)binop2_lo_float);
+ class_sethelpsymbol(binop3_lo_class, binop23_sym);
+
+ binop3_ls_class = class_new(gensym("<<"), (t_newmethod)binop3_ls_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop3_ls_class, binop2_ls_bang);
+ class_addfloat(binop3_ls_class, (t_method)binop2_ls_float);
+ class_sethelpsymbol(binop3_ls_class, binop23_sym);
+
+ binop3_rs_class = class_new(gensym(">>"), (t_newmethod)binop3_rs_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop3_rs_class, binop2_rs_bang);
+ class_addfloat(binop3_rs_class, (t_method)binop2_rs_float);
+ class_sethelpsymbol(binop3_rs_class, binop23_sym);
+
+ binop3_pc_class = class_new(gensym("%"), (t_newmethod)binop3_pc_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop3_pc_class, binop2_pc_bang);
+ class_addfloat(binop3_pc_class, (t_method)binop2_pc_float);
+ class_sethelpsymbol(binop3_pc_class, binop23_sym);
+
+ binop3_mod_class = class_new(gensym("mod"), (t_newmethod)binop3_mod_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop3_mod_class, binop3_mod_bang);
+ class_addfloat(binop3_mod_class, (t_method)binop3_mod_float);
+ class_sethelpsymbol(binop3_mod_class, binop23_sym);
+
+ binop3_div_class = class_new(gensym("div"), (t_newmethod)binop3_div_new, 0,
+ sizeof(t_binop), 0, A_DEFFLOAT, 0);
+ class_addbang(binop3_div_class, binop3_div_bang);
+ class_addfloat(binop3_div_class, (t_method)binop3_div_float);
+ class_sethelpsymbol(binop3_div_class, binop23_sym);
+
+ /* ------------------- math functions --------------- */
+
+ sin_class = class_new(gensym("sin"), sin_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(sin_class, (t_method)sin_float);
+ class_sethelpsymbol(sin_class, math_sym);
+
+ cos_class = class_new(gensym("cos"), cos_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(cos_class, (t_method)cos_float);
+ class_sethelpsymbol(cos_class, math_sym);
+
+ tan_class = class_new(gensym("tan"), tan_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(tan_class, (t_method)tan_float);
+ class_sethelpsymbol(tan_class, math_sym);
+
+ atan_class = class_new(gensym("atan"), atan_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(atan_class, (t_method)atan_float);
+ class_sethelpsymbol(atan_class, math_sym);
+
+ atan2_class = class_new(gensym("atan2"), atan2_new, 0,
+ sizeof(t_atan2), 0, 0);
+ class_addfloat(atan2_class, (t_method)atan2_float);
+ class_sethelpsymbol(atan2_class, math_sym);
+
+ sqrt_class = class_new(gensym("sqrt"), sqrt_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(sqrt_class, (t_method)sqrt_float);
+ class_sethelpsymbol(sqrt_class, math_sym);
+
+ log_class = class_new(gensym("log"), log_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(log_class, (t_method)log_float);
+ class_sethelpsymbol(log_class, math_sym);
+
+ exp_class = class_new(gensym("exp"), exp_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(exp_class, (t_method)exp_float);
+ class_sethelpsymbol(exp_class, math_sym);
+
+ abs_class = class_new(gensym("abs"), abs_new, 0,
+ sizeof(t_object), 0, 0);
+ class_addfloat(abs_class, (t_method)abs_float);
+ class_sethelpsymbol(abs_class, math_sym);
+
+/* ------------------------ misc ------------------------ */
+
+ clip_setup();
+}
+
+
diff --git a/pd/src/x_connective.c b/pd/src/x_connective.c
new file mode 100644
index 00000000..2b28f052
--- /dev/null
+++ b/pd/src/x_connective.c
@@ -0,0 +1,1452 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* connective objects */
+
+#include "m_pd.h"
+
+#include <string.h>
+#include <stdio.h>
+extern t_pd *newest;
+
+/* -------------------------- int ------------------------------ */
+static t_class *pdint_class;
+
+typedef struct _pdint
+{
+ t_object x_obj;
+ t_float x_f;
+} t_pdint;
+
+static void *pdint_new(t_floatarg f)
+{
+ t_pdint *x = (t_pdint *)pd_new(pdint_class);
+ x->x_f = f;
+ outlet_new(&x->x_obj, &s_float);
+ floatinlet_new(&x->x_obj, &x->x_f);
+ return (x);
+}
+
+static void pdint_bang(t_pdint *x)
+{
+ outlet_float(x->x_obj.ob_outlet, (t_float)(int)(x->x_f));
+}
+
+static void pdint_float(t_pdint *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, (t_float)(int)(x->x_f = f));
+}
+
+void pdint_setup(void)
+{
+ pdint_class = class_new(gensym("int"), (t_newmethod)pdint_new, 0,
+ sizeof(t_pdint), 0, A_DEFFLOAT, 0);
+ class_addcreator((t_newmethod)pdint_new, gensym("i"), A_DEFFLOAT, 0);
+ class_addbang(pdint_class, pdint_bang);
+ class_addfloat(pdint_class, pdint_float);
+}
+
+/* -------------------------- float ------------------------------ */
+static t_class *pdfloat_class;
+
+typedef struct _pdfloat
+{
+ t_object x_obj;
+ t_float x_f;
+} t_pdfloat;
+
+ /* "float," "symbol," and "bang" are special because
+ they're created by short-circuited messages to the "new"
+ object which are handled specially in pd_typedmess(). */
+
+static void *pdfloat_new(t_pd *dummy, t_float f)
+{
+ t_pdfloat *x = (t_pdfloat *)pd_new(pdfloat_class);
+ x->x_f = f;
+ outlet_new(&x->x_obj, &s_float);
+ floatinlet_new(&x->x_obj, &x->x_f);
+ newest = &x->x_obj.ob_pd;
+ return (x);
+}
+
+static void *pdfloat_new2(t_floatarg f)
+{
+ return (pdfloat_new(0, f));
+}
+
+static void pdfloat_bang(t_pdfloat *x)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_f);
+}
+
+static void pdfloat_float(t_pdfloat *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_f = f);
+}
+
+void pdfloat_setup(void)
+{
+ pdfloat_class = class_new(gensym("float"), (t_newmethod)pdfloat_new, 0,
+ sizeof(t_pdfloat), 0, A_FLOAT, 0);
+ class_addcreator((t_newmethod)pdfloat_new2, gensym("f"), A_DEFFLOAT, 0);
+ class_addbang(pdfloat_class, pdfloat_bang);
+ class_addfloat(pdfloat_class, (t_method)pdfloat_float);
+}
+
+/* -------------------------- symbol ------------------------------ */
+static t_class *pdsymbol_class;
+
+typedef struct _pdsymbol
+{
+ t_object x_obj;
+ t_symbol *x_s;
+} t_pdsymbol;
+
+static void *pdsymbol_new(t_pd *dummy, t_symbol *s)
+{
+ t_pdsymbol *x = (t_pdsymbol *)pd_new(pdsymbol_class);
+ x->x_s = s;
+ outlet_new(&x->x_obj, &s_symbol);
+ symbolinlet_new(&x->x_obj, &x->x_s);
+ newest = &x->x_obj.ob_pd;
+ return (x);
+}
+
+static void pdsymbol_bang(t_pdsymbol *x)
+{
+ outlet_symbol(x->x_obj.ob_outlet, x->x_s);
+}
+
+static void pdsymbol_symbol(t_pdsymbol *x, t_symbol *s)
+{
+ outlet_symbol(x->x_obj.ob_outlet, x->x_s = s);
+}
+
+static void pdsymbol_anything(t_pdsymbol *x, t_symbol *s, int ac, t_atom *av)
+{
+ outlet_symbol(x->x_obj.ob_outlet, x->x_s = s);
+}
+
+void pdsymbol_setup(void)
+{
+ pdsymbol_class = class_new(gensym("symbol"), (t_newmethod)pdsymbol_new, 0,
+ sizeof(t_pdsymbol), 0, A_SYMBOL, 0);
+ class_addbang(pdsymbol_class, pdsymbol_bang);
+ class_addsymbol(pdsymbol_class, pdsymbol_symbol);
+ class_addanything(pdsymbol_class, pdsymbol_anything);
+}
+
+/* -------------------------- bang ------------------------------ */
+static t_class *bang_class;
+
+typedef struct _bang
+{
+ t_object x_obj;
+} t_bang;
+
+static void *bang_new(t_pd *dummy)
+{
+ t_bang *x = (t_bang *)pd_new(bang_class);
+ outlet_new(&x->x_obj, &s_bang);
+ newest = &x->x_obj.ob_pd;
+ return (x);
+}
+
+static void *bang_new2(t_bang f)
+{
+ return (bang_new(0));
+}
+
+static void bang_bang(t_bang *x)
+{
+ outlet_bang(x->x_obj.ob_outlet);
+}
+
+void bang_setup(void)
+{
+ bang_class = class_new(gensym("bang"), (t_newmethod)bang_new, 0,
+ sizeof(t_bang), 0, 0);
+ class_addcreator((t_newmethod)bang_new2, gensym("b"), 0);
+ class_addbang(bang_class, bang_bang);
+ class_addfloat(bang_class, bang_bang);
+ class_addsymbol(bang_class, bang_bang);
+ class_addlist(bang_class, bang_bang);
+ class_addanything(bang_class, bang_bang);
+}
+
+/* -------------------- send ------------------------------ */
+
+static t_class *send_class;
+
+typedef struct _send
+{
+ t_object x_obj;
+ t_symbol *x_sym;
+} t_send;
+
+static void send_bang(t_send *x)
+{
+ if (x->x_sym->s_thing) pd_bang(x->x_sym->s_thing);
+}
+
+static void send_float(t_send *x, t_float f)
+{
+ if (x->x_sym->s_thing) pd_float(x->x_sym->s_thing, f);
+}
+
+static void send_symbol(t_send *x, t_symbol *s)
+{
+ if (x->x_sym->s_thing) pd_symbol(x->x_sym->s_thing, s);
+}
+
+static void send_pointer(t_send *x, t_gpointer *gp)
+{
+ if (x->x_sym->s_thing) pd_pointer(x->x_sym->s_thing, gp);
+}
+
+static void send_list(t_send *x, t_symbol *s, int argc, t_atom *argv)
+{
+ if (x->x_sym->s_thing) pd_list(x->x_sym->s_thing, s, argc, argv);
+}
+
+static void send_anything(t_send *x, t_symbol *s, int argc, t_atom *argv)
+{
+ if (x->x_sym->s_thing) typedmess(x->x_sym->s_thing, s, argc, argv);
+}
+
+static void *send_new(t_symbol *s)
+{
+ t_send *x = (t_send *)pd_new(send_class);
+ x->x_sym = s;
+ return (x);
+}
+
+static void send_setup(void)
+{
+ send_class = class_new(gensym("send"), (t_newmethod)send_new, 0,
+ sizeof(t_send), 0, A_DEFSYM, 0);
+ class_addcreator((t_newmethod)send_new, gensym("s"), A_DEFSYM, 0);
+ class_addbang(send_class, send_bang);
+ class_addfloat(send_class, send_float);
+ class_addsymbol(send_class, send_symbol);
+ class_addpointer(send_class, send_pointer);
+ class_addlist(send_class, send_list);
+ class_addanything(send_class, send_anything);
+}
+/* -------------------- receive ------------------------------ */
+
+static t_class *receive_class;
+
+typedef struct _receive
+{
+ t_object x_obj;
+ t_symbol *x_sym;
+} t_receive;
+
+static void receive_bang(t_receive *x)
+{
+ outlet_bang(x->x_obj.ob_outlet);
+}
+
+static void receive_float(t_receive *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, f);
+}
+
+static void receive_symbol(t_receive *x, t_symbol *s)
+{
+ outlet_symbol(x->x_obj.ob_outlet, s);
+}
+
+static void receive_pointer(t_receive *x, t_gpointer *gp)
+{
+ outlet_pointer(x->x_obj.ob_outlet, gp);
+}
+
+static void receive_list(t_receive *x, t_symbol *s, int argc, t_atom *argv)
+{
+ outlet_list(x->x_obj.ob_outlet, s, argc, argv);
+}
+
+static void receive_anything(t_receive *x, t_symbol *s, int argc, t_atom *argv)
+{
+ outlet_anything(x->x_obj.ob_outlet, s, argc, argv);
+}
+
+static void *receive_new(t_symbol *s)
+{
+ t_receive *x = (t_receive *)pd_new(receive_class);
+ x->x_sym = s;
+ pd_bind(&x->x_obj.ob_pd, s);
+ outlet_new(&x->x_obj, 0);
+ return (x);
+}
+
+static void receive_free(t_receive *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, x->x_sym);
+}
+
+static void receive_setup(void)
+{
+ receive_class = class_new(gensym("receive"), (t_newmethod)receive_new,
+ (t_method)receive_free, sizeof(t_receive), CLASS_NOINLET, A_DEFSYM, 0);
+ class_addcreator((t_newmethod)receive_new, gensym("r"), A_DEFSYM, 0);
+ class_addbang(receive_class, receive_bang);
+ class_addfloat(receive_class, (t_method)receive_float);
+ class_addsymbol(receive_class, receive_symbol);
+ class_addpointer(receive_class, receive_pointer);
+ class_addlist(receive_class, receive_list);
+ class_addanything(receive_class, receive_anything);
+}
+
+/* -------------------------- select ------------------------------ */
+
+static t_class *sel1_class;
+
+typedef struct _sel1
+{
+ t_object x_obj;
+ t_atom x_atom;
+ t_outlet *x_outlet1;
+ t_outlet *x_outlet2;
+} t_sel1;
+
+static void sel1_float(t_sel1 *x, t_float f)
+{
+ if (x->x_atom.a_type == A_FLOAT && f == x->x_atom.a_w.w_float)
+ outlet_bang(x->x_outlet1);
+ else outlet_float(x->x_outlet2, f);
+}
+
+static void sel1_symbol(t_sel1 *x, t_symbol *s)
+{
+ if (x->x_atom.a_type == A_SYMBOL && s == x->x_atom.a_w.w_symbol)
+ outlet_bang(x->x_outlet1);
+ else outlet_symbol(x->x_outlet2, s);
+}
+
+static t_class *sel2_class;
+
+typedef struct _selectelement
+{
+ t_word e_w;
+ t_outlet *e_outlet;
+} t_selectelement;
+
+typedef struct _sel2
+{
+ t_object x_obj;
+ t_atomtype x_type;
+ t_int x_nelement;
+ t_selectelement *x_vec;
+ t_outlet *x_rejectout;
+} t_sel2;
+
+static void sel2_float(t_sel2 *x, t_float f)
+{
+ t_selectelement *e;
+ int nelement;
+ if (x->x_type == A_FLOAT)
+ {
+ for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)
+ if (e->e_w.w_float == f)
+ {
+ outlet_bang(e->e_outlet);
+ return;
+ }
+ }
+ outlet_float(x->x_rejectout, f);
+}
+
+static void sel2_symbol(t_sel2 *x, t_symbol *s)
+{
+ t_selectelement *e;
+ int nelement;
+ if (x->x_type == A_SYMBOL)
+ {
+ for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)
+ if (e->e_w.w_symbol == s)
+ {
+ outlet_bang(e->e_outlet);
+ return;
+ }
+ }
+ outlet_symbol(x->x_rejectout, s);
+}
+
+static void sel2_free(t_sel2 *x)
+{
+ freebytes(x->x_vec, x->x_nelement * sizeof(*x->x_vec));
+}
+
+static void *select_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_atom a;
+ if (argc == 0)
+ {
+ argc = 1;
+ SETFLOAT(&a, 0);
+ argv = &a;
+ }
+ if (argc == 1)
+ {
+ t_sel1 *x = (t_sel1 *)pd_new(sel1_class);
+ x->x_atom = *argv;
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_bang);
+ if (argv->a_type == A_FLOAT)
+ {
+ floatinlet_new(&x->x_obj, &x->x_atom.a_w.w_float);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+ }
+ else
+ {
+ symbolinlet_new(&x->x_obj, &x->x_atom.a_w.w_symbol);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_symbol);
+ }
+ return (x);
+ }
+ else
+ {
+ int n;
+ t_selectelement *e;
+ t_sel2 *x = (t_sel2 *)pd_new(sel2_class);
+ x->x_nelement = argc;
+ x->x_vec = (t_selectelement *)getbytes(argc * sizeof(*x->x_vec));
+ x->x_type = argv[0].a_type;
+ for (n = 0, e = x->x_vec; n < argc; n++, e++)
+ {
+ e->e_outlet = outlet_new(&x->x_obj, &s_bang);
+ if ((x->x_type = argv->a_type) == A_FLOAT)
+ e->e_w.w_float = atom_getfloatarg(n, argc, argv);
+ else e->e_w.w_symbol = atom_getsymbolarg(n, argc, argv);
+ }
+ x->x_rejectout = outlet_new(&x->x_obj, &s_float);
+ return (x);
+ }
+
+}
+
+void select_setup(void)
+{
+ sel1_class = class_new(gensym("select"), 0, 0,
+ sizeof(t_sel1), 0, 0);
+ class_addfloat(sel1_class, sel1_float);
+ class_addsymbol(sel1_class, sel1_symbol);
+
+ sel2_class = class_new(gensym("select"), 0, (t_method)sel2_free,
+ sizeof(t_sel2), 0, 0);
+ class_addfloat(sel2_class, sel2_float);
+ class_addsymbol(sel2_class, sel2_symbol);
+
+ class_addcreator((t_newmethod)select_new, gensym("select"), A_GIMME, 0);
+ class_addcreator((t_newmethod)select_new, gensym("sel"), A_GIMME, 0);
+}
+
+/* -------------------------- route ------------------------------ */
+
+static t_class *route_class;
+
+typedef struct _routeelement
+{
+ t_word e_w;
+ t_outlet *e_outlet;
+} t_routeelement;
+
+typedef struct _route
+{
+ t_object x_obj;
+ t_atomtype x_type;
+ t_int x_nelement;
+ t_routeelement *x_vec;
+ t_outlet *x_rejectout;
+} t_route;
+
+static void route_anything(t_route *x, t_symbol *sel, int argc, t_atom *argv)
+{
+ t_routeelement *e;
+ int nelement;
+ if (x->x_type == A_SYMBOL)
+ {
+ for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)
+ if (e->e_w.w_symbol == sel)
+ {
+ if (argc > 0 && argv[0].a_type == A_SYMBOL)
+ outlet_anything(e->e_outlet, argv[0].a_w.w_symbol,
+ argc-1, argv+1);
+ else outlet_list(e->e_outlet, 0, argc, argv);
+ return;
+ }
+ }
+ outlet_anything(x->x_rejectout, sel, argc, argv);
+}
+
+static void route_list(t_route *x, t_symbol *sel, int argc, t_atom *argv)
+{
+ t_routeelement *e;
+ int nelement;
+ if (x->x_type == A_FLOAT)
+ {
+ float f;
+ if (!argc) return;
+ f = atom_getfloat(argv);
+ for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)
+ if (e->e_w.w_float == f)
+ {
+ if (argc > 1 && argv[1].a_type == A_SYMBOL)
+ outlet_anything(e->e_outlet, argv[1].a_w.w_symbol,
+ argc-2, argv+2);
+ else outlet_list(e->e_outlet, 0, argc-1, argv+1);
+ return;
+ }
+ }
+ else /* symbol arguments */
+ {
+ if (argc > 1) /* 2 or more args: treat as "list" */
+ {
+ for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)
+ {
+ if (e->e_w.w_symbol == &s_list)
+ {
+ if (argc > 0 && argv[0].a_type == A_SYMBOL)
+ outlet_anything(e->e_outlet, argv[0].a_w.w_symbol,
+ argc-1, argv+1);
+ else outlet_list(e->e_outlet, 0, argc, argv);
+ return;
+ }
+ }
+ }
+ else if (argc == 0) /* no args: treat as "bang" */
+ {
+ for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)
+ {
+ if (e->e_w.w_symbol == &s_bang)
+ {
+ outlet_bang(e->e_outlet);
+ return;
+ }
+ }
+ }
+ else if (argv[0].a_type == A_FLOAT) /* one float arg */
+ {
+ for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)
+ {
+ if (e->e_w.w_symbol == &s_float)
+ {
+ outlet_float(e->e_outlet, argv[0].a_w.w_float);
+ return;
+ }
+ }
+ }
+ else
+ {
+ for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)
+ {
+ if (e->e_w.w_symbol == &s_symbol)
+ {
+ outlet_symbol(e->e_outlet, argv[0].a_w.w_symbol);
+ return;
+ }
+ }
+ }
+ }
+ outlet_list(x->x_rejectout, 0, argc, argv);
+}
+
+
+static void route_free(t_route *x)
+{
+ freebytes(x->x_vec, x->x_nelement * sizeof(*x->x_vec));
+}
+
+static void *route_new(t_symbol *s, int argc, t_atom *argv)
+{
+ int n;
+ t_routeelement *e;
+ t_route *x = (t_route *)pd_new(route_class);
+ t_atom a;
+ if (argc == 0)
+ {
+ argc = 1;
+ SETFLOAT(&a, 0);
+ argv = &a;
+ }
+ x->x_type = argv[0].a_type;
+ x->x_nelement = argc;
+ x->x_vec = (t_routeelement *)getbytes(argc * sizeof(*x->x_vec));
+ for (n = 0, e = x->x_vec; n < argc; n++, e++)
+ {
+ e->e_outlet = outlet_new(&x->x_obj, &s_list);
+ if (x->x_type == A_FLOAT)
+ e->e_w.w_float = atom_getfloatarg(n, argc, argv);
+ else e->e_w.w_symbol = atom_getsymbolarg(n, argc, argv);
+ }
+ x->x_rejectout = outlet_new(&x->x_obj, &s_list);
+ return (x);
+}
+
+void route_setup(void)
+{
+ route_class = class_new(gensym("route"), (t_newmethod)route_new,
+ (t_method)route_free, sizeof(t_route), 0, A_GIMME, 0);
+ class_addlist(route_class, route_list);
+ class_addanything(route_class, route_anything);
+}
+
+/* -------------------------- pack ------------------------------ */
+
+static t_class *pack_class;
+
+typedef struct _pack
+{
+ t_object x_obj;
+ t_int x_n; /* number of args */
+ t_atom *x_vec; /* input values */
+ t_int x_nptr; /* number of pointers */
+ t_gpointer *x_gpointer; /* the pointers */
+ t_atom *x_outvec; /* space for output values */
+} t_pack;
+
+static void *pack_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_pack *x = (t_pack *)pd_new(pack_class);
+ t_atom defarg[2], *ap, *vec, *vp;
+ t_gpointer *gp;
+ int nptr = 0;
+ int i;
+ if (!argc)
+ {
+ argv = defarg;
+ argc = 2;
+ SETFLOAT(&defarg[0], 0);
+ SETFLOAT(&defarg[1], 0);
+ }
+
+ x->x_n = argc;
+ vec = x->x_vec = (t_atom *)getbytes(argc * sizeof(*x->x_vec));
+ x->x_outvec = (t_atom *)getbytes(argc * sizeof(*x->x_outvec));
+
+ for (i = argc, ap = argv; i--; ap++)
+ if (ap->a_type == A_SYMBOL && *ap->a_w.w_symbol->s_name == 'p')
+ nptr++;
+
+ gp = x->x_gpointer = (t_gpointer *)t_getbytes(nptr * sizeof (*gp));
+ x->x_nptr = nptr;
+
+ for (i = 0, vp = x->x_vec, ap = argv; i < argc; i++, ap++, vp++)
+ {
+ if (ap->a_type == A_FLOAT)
+ {
+ *vp = *ap;
+ if (i) floatinlet_new(&x->x_obj, &vp->a_w.w_float);
+ }
+ else if (ap->a_type == A_SYMBOL)
+ {
+ char c = *ap->a_w.w_symbol->s_name;
+ if (c == 's')
+ {
+ SETSYMBOL(vp, &s_symbol);
+ if (i) symbolinlet_new(&x->x_obj, &vp->a_w.w_symbol);
+ }
+ else if (c == 'p')
+ {
+ vp->a_type = A_POINTER;
+ vp->a_w.w_gpointer = gp;
+ gpointer_init(gp);
+ if (i) pointerinlet_new(&x->x_obj, gp);
+ gp++;
+ }
+ else
+ {
+ if (c != 'f') pd_error(x, "pack: %s: bad type",
+ ap->a_w.w_symbol->s_name);
+ SETFLOAT(vp, 0);
+ if (i) floatinlet_new(&x->x_obj, &vp->a_w.w_float);
+ }
+ }
+ }
+ outlet_new(&x->x_obj, &s_list);
+ return (x);
+}
+
+static void pack_bang(t_pack *x)
+{
+ int i, reentered = 0, size = x->x_n * sizeof (t_atom);
+ t_gpointer *gp;
+ t_atom *outvec;
+ for (i = x->x_nptr, gp = x->x_gpointer; i--; gp++)
+ if (!gpointer_check(gp, 1))
+ {
+ pd_error(x, "pack: stale pointer");
+ return;
+ }
+ /* reentrancy protection. The first time through use the pre-allocated
+ x_outvec; if we're reentered we have to allocate new memory. */
+ if (!x->x_outvec)
+ {
+ /* LATER figure out how to deal with reentrancy and pointers... */
+ if (x->x_nptr)
+ post("pack_bang: warning: reentry with pointers unprotected");
+ outvec = t_getbytes(size);
+ reentered = 1;
+ }
+ else
+ {
+ outvec = x->x_outvec;
+ x->x_outvec = 0;
+ }
+ memcpy(outvec, x->x_vec, size);
+ outlet_list(x->x_obj.ob_outlet, &s_list, x->x_n, outvec);
+ if (reentered)
+ t_freebytes(outvec, size);
+ else x->x_outvec = outvec;
+}
+
+static void pack_pointer(t_pack *x, t_gpointer *gp)
+{
+ if (x->x_vec->a_type == A_POINTER)
+ {
+ gpointer_unset(x->x_gpointer);
+ *x->x_gpointer = *gp;
+ if (gp->gp_stub) gp->gp_stub->gs_refcount++;
+ pack_bang(x);
+ }
+ else pd_error(x, "pack_pointer: wrong type");
+}
+
+static void pack_float(t_pack *x, t_float f)
+{
+ if (x->x_vec->a_type == A_FLOAT)
+ {
+ x->x_vec->a_w.w_float = f;
+ pack_bang(x);
+ }
+ else pd_error(x, "pack_float: wrong type");
+}
+
+static void pack_symbol(t_pack *x, t_symbol *s)
+{
+ if (x->x_vec->a_type == A_SYMBOL)
+ {
+ x->x_vec->a_w.w_symbol = s;
+ pack_bang(x);
+ }
+ else pd_error(x, "pack_symbol: wrong type");
+}
+
+static void pack_list(t_pack *x, t_symbol *s, int ac, t_atom *av)
+{
+ obj_list(&x->x_obj, 0, ac, av);
+}
+
+static void pack_anything(t_pack *x, t_symbol *s, int ac, t_atom *av)
+{
+ t_atom *av2 = (t_atom *)getbytes((ac + 1) * sizeof(t_atom));
+ int i;
+ for (i = 0; i < ac; i++)
+ av2[i + 1] = av[i];
+ SETSYMBOL(av2, s);
+ obj_list(&x->x_obj, 0, ac+1, av2);
+ freebytes(av2, (ac + 1) * sizeof(t_atom));
+}
+
+static void pack_free(t_pack *x)
+{
+ t_gpointer *gp;
+ int i;
+ for (gp = x->x_gpointer, i = x->x_nptr; i--; gp++)
+ gpointer_unset(gp);
+ freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec));
+ freebytes(x->x_outvec, x->x_n * sizeof(*x->x_outvec));
+ freebytes(x->x_gpointer, x->x_nptr * sizeof(*x->x_gpointer));
+}
+
+static void pack_setup(void)
+{
+ pack_class = class_new(gensym("pack"), (t_newmethod)pack_new,
+ (t_method)pack_free, sizeof(t_pack), 0, A_GIMME, 0);
+ class_addbang(pack_class, pack_bang);
+ class_addpointer(pack_class, pack_pointer);
+ class_addfloat(pack_class, pack_float);
+ class_addsymbol(pack_class, pack_symbol);
+ class_addlist(pack_class, pack_list);
+ class_addanything(pack_class, pack_anything);
+}
+
+/* -------------------------- unpack ------------------------------ */
+
+static t_class *unpack_class;
+
+typedef struct unpackout
+{
+ t_atomtype u_type;
+ t_outlet *u_outlet;
+} t_unpackout;
+
+typedef struct _unpack
+{
+ t_object x_obj;
+ t_int x_n;
+ t_unpackout *x_vec;
+} t_unpack;
+
+static void *unpack_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_unpack *x = (t_unpack *)pd_new(unpack_class);
+ t_atom defarg[2], *ap;
+ t_unpackout *u;
+ int i;
+ if (!argc)
+ {
+ argv = defarg;
+ argc = 2;
+ SETFLOAT(&defarg[0], 0);
+ SETFLOAT(&defarg[1], 0);
+ }
+ x->x_n = argc;
+ x->x_vec = (t_unpackout *)getbytes(argc * sizeof(*x->x_vec));
+ for (i = 0, ap = argv, u = x->x_vec; i < argc; u++, ap++, i++)
+ {
+ t_atomtype type = ap->a_type;
+ if (type == A_SYMBOL)
+ {
+ char c = *ap->a_w.w_symbol->s_name;
+ if (c == 's')
+ {
+ u->u_type = A_SYMBOL;
+ u->u_outlet = outlet_new(&x->x_obj, &s_symbol);
+ }
+ else if (c == 'p')
+ {
+ u->u_type = A_POINTER;
+ u->u_outlet = outlet_new(&x->x_obj, &s_pointer);
+ }
+ else
+ {
+ if (c != 'f') pd_error(x, "unpack: %s: bad type",
+ ap->a_w.w_symbol->s_name);
+ u->u_type = A_FLOAT;
+ u->u_outlet = outlet_new(&x->x_obj, &s_float);
+ }
+ }
+ else
+ {
+ u->u_type = A_FLOAT;
+ u->u_outlet = outlet_new(&x->x_obj, &s_float);
+ }
+ }
+ return (x);
+}
+
+static void unpack_list(t_unpack *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_atom *ap;
+ t_unpackout *u;
+ int i;
+ if (argc > x->x_n) argc = x->x_n;
+ for (i = argc, u = x->x_vec + i, ap = argv + i; u--, ap--, i--;)
+ {
+ t_atomtype type = u->u_type;
+ if (type != ap->a_type)
+ pd_error(x, "unpack: type mismatch");
+ else if (type == A_FLOAT)
+ outlet_float(u->u_outlet, ap->a_w.w_float);
+ else if (type == A_SYMBOL)
+ outlet_symbol(u->u_outlet, ap->a_w.w_symbol);
+ else outlet_pointer(u->u_outlet, ap->a_w.w_gpointer);
+ }
+}
+
+static void unpack_anything(t_unpack *x, t_symbol *s, int ac, t_atom *av)
+{
+ t_atom *av2 = (t_atom *)getbytes((ac + 1) * sizeof(t_atom));
+ int i;
+ for (i = 0; i < ac; i++)
+ av2[i + 1] = av[i];
+ SETSYMBOL(av2, s);
+ unpack_list(x, 0, ac+1, av2);
+ freebytes(av2, (ac + 1) * sizeof(t_atom));
+}
+
+static void unpack_free(t_unpack *x)
+{
+ freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec));
+}
+
+static void unpack_setup(void)
+{
+ unpack_class = class_new(gensym("unpack"), (t_newmethod)unpack_new,
+ (t_method)unpack_free, sizeof(t_unpack), 0, A_GIMME, 0);
+ class_addlist(unpack_class, unpack_list);
+ class_addanything(unpack_class, unpack_anything);
+}
+
+/* -------------------------- trigger ------------------------------ */
+
+static t_class *trigger_class;
+#define TR_BANG 0
+#define TR_FLOAT 1
+#define TR_SYMBOL 2
+#define TR_POINTER 3
+#define TR_LIST 4
+#define TR_ANYTHING 5
+
+typedef struct triggerout
+{
+ int u_type; /* outlet type from above */
+ t_outlet *u_outlet;
+} t_triggerout;
+
+typedef struct _trigger
+{
+ t_object x_obj;
+ t_int x_n;
+ t_triggerout *x_vec;
+} t_trigger;
+
+static void *trigger_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_trigger *x = (t_trigger *)pd_new(trigger_class);
+ t_atom defarg[2], *ap;
+ t_triggerout *u;
+ int i;
+ if (!argc)
+ {
+ argv = defarg;
+ argc = 2;
+ SETSYMBOL(&defarg[0], &s_bang);
+ SETSYMBOL(&defarg[1], &s_bang);
+ }
+ x->x_n = argc;
+ x->x_vec = (t_triggerout *)getbytes(argc * sizeof(*x->x_vec));
+ for (i = 0, ap = argv, u = x->x_vec; i < argc; u++, ap++, i++)
+ {
+ t_atomtype thistype = ap->a_type;
+ char c;
+ if (thistype == TR_SYMBOL) c = ap->a_w.w_symbol->s_name[0];
+ else if (thistype == TR_FLOAT) c = 'f';
+ else c = 0;
+ if (c == 'p')
+ u->u_type = TR_POINTER,
+ u->u_outlet = outlet_new(&x->x_obj, &s_pointer);
+ else if (c == 'f')
+ u->u_type = TR_FLOAT, u->u_outlet = outlet_new(&x->x_obj, &s_float);
+ else if (c == 'b')
+ u->u_type = TR_BANG, u->u_outlet = outlet_new(&x->x_obj, &s_bang);
+ else if (c == 'l')
+ u->u_type = TR_LIST, u->u_outlet = outlet_new(&x->x_obj, &s_list);
+ else if (c == 's')
+ u->u_type = TR_SYMBOL,
+ u->u_outlet = outlet_new(&x->x_obj, &s_symbol);
+ else if (c == 'a')
+ u->u_type = TR_ANYTHING,
+ u->u_outlet = outlet_new(&x->x_obj, &s_symbol);
+ else
+ {
+ pd_error(x, "trigger: %s: bad type", ap->a_w.w_symbol->s_name);
+ u->u_type = TR_FLOAT, u->u_outlet = outlet_new(&x->x_obj, &s_float);
+ }
+ }
+ return (x);
+}
+
+static void trigger_list(t_trigger *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_triggerout *u;
+ int i;
+ t_atom at;
+ if (!argc)
+ {
+ argc = 1;
+ SETFLOAT(&at, 0);
+ argv = &at;
+ }
+ for (i = x->x_n, u = x->x_vec + i; u--, i--;)
+ {
+ if (u->u_type == TR_FLOAT)
+ outlet_float(u->u_outlet, atom_getfloat(argv));
+ else if (u->u_type == TR_BANG)
+ outlet_bang(u->u_outlet);
+ else if (u->u_type == TR_SYMBOL)
+ outlet_symbol(u->u_outlet, atom_getsymbol(argv));
+ else if (u->u_type == TR_POINTER)
+ {
+ if (argv->a_type != TR_POINTER)
+ pd_error(x, "unpack: bad pointer");
+ else outlet_pointer(u->u_outlet, argv->a_w.w_gpointer);
+ }
+ else outlet_list(u->u_outlet, &s_list, argc, argv);
+ }
+}
+
+static void trigger_anything(t_trigger *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_triggerout *u;
+ int i;
+ for (i = x->x_n, u = x->x_vec + i; u--, i--;)
+ {
+ if (u->u_type == TR_BANG)
+ outlet_bang(u->u_outlet);
+ else if (u->u_type == TR_ANYTHING)
+ outlet_anything(u->u_outlet, s, argc, argv);
+ else pd_error(x, "trigger: can only convert 's' to 'b' or 'a'",
+ s->s_name);
+ }
+}
+
+static void trigger_bang(t_trigger *x)
+{
+ trigger_list(x, 0, 0, 0);
+}
+
+static void trigger_pointer(t_trigger *x, t_gpointer *gp)
+{
+ t_atom at;
+ SETPOINTER(&at, gp);
+ trigger_list(x, 0, 1, &at);
+}
+
+static void trigger_float(t_trigger *x, t_float f)
+{
+ t_atom at;
+ SETFLOAT(&at, f);
+ trigger_list(x, 0, 1, &at);
+}
+
+static void trigger_symbol(t_trigger *x, t_symbol *s)
+{
+ t_atom at;
+ SETSYMBOL(&at, s);
+ trigger_list(x, 0, 1, &at);
+}
+
+static void trigger_free(t_trigger *x)
+{
+ freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec));
+}
+
+static void trigger_setup(void)
+{
+ trigger_class = class_new(gensym("trigger"), (t_newmethod)trigger_new,
+ (t_method)trigger_free, sizeof(t_trigger), 0, A_GIMME, 0);
+ class_addcreator((t_newmethod)trigger_new, gensym("t"), A_GIMME, 0);
+ class_addlist(trigger_class, trigger_list);
+ class_addbang(trigger_class, trigger_bang);
+ class_addpointer(trigger_class, trigger_pointer);
+ class_addfloat(trigger_class, (t_method)trigger_float);
+ class_addsymbol(trigger_class, trigger_symbol);
+ class_addanything(trigger_class, trigger_anything);
+}
+
+/* -------------------------- spigot ------------------------------ */
+static t_class *spigot_class;
+
+typedef struct _spigot
+{
+ t_object x_obj;
+ float x_state;
+} t_spigot;
+
+static void *spigot_new(void)
+{
+ t_spigot *x = (t_spigot *)pd_new(spigot_class);
+ floatinlet_new(&x->x_obj, &x->x_state);
+ outlet_new(&x->x_obj, 0);
+ x->x_state = 0;
+ return (x);
+}
+
+static void spigot_bang(t_spigot *x)
+{
+ if (x->x_state != 0) outlet_bang(x->x_obj.ob_outlet);
+}
+
+static void spigot_pointer(t_spigot *x, t_gpointer *gp)
+{
+ if (x->x_state != 0) outlet_pointer(x->x_obj.ob_outlet, gp);
+}
+
+static void spigot_float(t_spigot *x, t_float f)
+{
+ if (x->x_state != 0) outlet_float(x->x_obj.ob_outlet, f);
+}
+
+static void spigot_symbol(t_spigot *x, t_symbol *s)
+{
+ if (x->x_state != 0) outlet_symbol(x->x_obj.ob_outlet, s);
+}
+
+static void spigot_list(t_spigot *x, t_symbol *s, int argc, t_atom *argv)
+{
+ if (x->x_state != 0) outlet_list(x->x_obj.ob_outlet, s, argc, argv);
+}
+
+static void spigot_anything(t_spigot *x, t_symbol *s, int argc, t_atom *argv)
+{
+ if (x->x_state != 0) outlet_anything(x->x_obj.ob_outlet, s, argc, argv);
+}
+
+static void spigot_setup(void)
+{
+ spigot_class = class_new(gensym("spigot"), (t_newmethod)spigot_new, 0,
+ sizeof(t_spigot), 0, A_DEFSYM, 0);
+ class_addbang(spigot_class, spigot_bang);
+ class_addpointer(spigot_class, spigot_pointer);
+ class_addfloat(spigot_class, spigot_float);
+ class_addsymbol(spigot_class, spigot_symbol);
+ class_addlist(spigot_class, spigot_list);
+ class_addanything(spigot_class, spigot_anything);
+}
+
+/* --------------------------- moses ----------------------------- */
+static t_class *moses_class;
+
+typedef struct _moses
+{
+ t_object x_ob;
+ t_outlet *x_out2;
+ float x_y;
+} t_moses;
+
+static void *moses_new(t_floatarg f)
+{
+ t_moses *x = (t_moses *)pd_new(moses_class);
+ floatinlet_new(&x->x_ob, &x->x_y);
+ outlet_new(&x->x_ob, &s_float);
+ x->x_out2 = outlet_new(&x->x_ob, &s_float);
+ x->x_y = f;
+ return (x);
+}
+
+static void moses_float(t_moses *x, t_float f)
+{
+ if (f < x->x_y) outlet_float(x->x_ob.ob_outlet, f);
+ else outlet_float(x->x_out2, f);
+}
+
+static void moses_setup(void)
+{
+ moses_class = class_new(gensym("moses"), (t_newmethod)moses_new, 0,
+ sizeof(t_moses), 0, A_DEFFLOAT, 0);
+ class_addfloat(moses_class, moses_float);
+}
+
+/* ----------------------- until --------------------- */
+
+static t_class *until_class;
+
+typedef struct _until
+{
+ t_object x_obj;
+ int x_run;
+ int x_count;
+} t_until;
+
+static void *until_new(void)
+{
+ t_until *x = (t_until *)pd_new(until_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("bang"), gensym("bang2"));
+ outlet_new(&x->x_obj, &s_bang);
+ x->x_run = 0;
+ return (x);
+}
+
+static void until_bang(t_until *x)
+{
+ x->x_run = 1;
+ x->x_count = -1;
+ while (x->x_run && x->x_count)
+ x->x_count--, outlet_bang(x->x_obj.ob_outlet);
+}
+
+static void until_float(t_until *x, t_float f)
+{
+ x->x_run = 1;
+ x->x_count = f;
+ while (x->x_run && x->x_count)
+ x->x_count--, outlet_bang(x->x_obj.ob_outlet);
+}
+
+static void until_bang2(t_until *x)
+{
+ x->x_run = 0;
+}
+
+static void until_setup(void)
+{
+ until_class = class_new(gensym("until"), (t_newmethod)until_new, 0,
+ sizeof(t_until), 0, 0);
+ class_addbang(until_class, until_bang);
+ class_addfloat(until_class, until_float);
+ class_addmethod(until_class, (t_method)until_bang2, gensym("bang2"), 0);
+}
+
+/* ----------------------- makefilename --------------------- */
+
+static t_class *makefilename_class;
+
+typedef struct _makefilename
+{
+ t_object x_obj;
+ t_symbol *x_format;
+} t_makefilename;
+
+static void *makefilename_new(t_symbol *s)
+{
+ t_makefilename *x = (t_makefilename *)pd_new(makefilename_class);
+ if (!s->s_name) s = gensym("file.%d");
+ outlet_new(&x->x_obj, &s_symbol);
+ x->x_format = s;
+ return (x);
+}
+
+static void makefilename_float(t_makefilename *x, t_floatarg f)
+{
+ char buf[MAXPDSTRING];
+ sprintf(buf, x->x_format->s_name, (int)f);
+ outlet_symbol(x->x_obj.ob_outlet, gensym(buf));
+}
+
+static void makefilename_symbol(t_makefilename *x, t_symbol *s)
+{
+ char buf[MAXPDSTRING];
+ sprintf(buf, x->x_format->s_name, s->s_name);
+ outlet_symbol(x->x_obj.ob_outlet, gensym(buf));
+}
+
+static void makefilename_setup(void)
+{
+ makefilename_class = class_new(gensym("makefilename"),
+ (t_newmethod)makefilename_new, 0,
+ sizeof(t_makefilename), 0, A_DEFSYM, 0);
+ class_addfloat(makefilename_class, makefilename_float);
+ class_addsymbol(makefilename_class, makefilename_symbol);
+}
+
+/* -------------------------- swap ------------------------------ */
+static t_class *swap_class;
+
+typedef struct _swap
+{
+ t_object x_obj;
+ t_outlet *x_out2;
+ t_float x_f1;
+ t_float x_f2;
+} t_swap;
+
+static void *swap_new(t_floatarg f)
+{
+ t_swap *x = (t_swap *)pd_new(swap_class);
+ x->x_f2 = f;
+ x->x_f1 = 0;
+ outlet_new(&x->x_obj, &s_float);
+ x->x_out2 = outlet_new(&x->x_obj, &s_float);
+ floatinlet_new(&x->x_obj, &x->x_f2);
+ return (x);
+}
+
+static void swap_bang(t_swap *x)
+{
+ outlet_float(x->x_out2, x->x_f1);
+ outlet_float(x->x_obj.ob_outlet, x->x_f2);
+}
+
+static void swap_float(t_swap *x, t_float f)
+{
+ x->x_f1 = f;
+ swap_bang(x);
+}
+
+void swap_setup(void)
+{
+ swap_class = class_new(gensym("swap"), (t_newmethod)swap_new, 0,
+ sizeof(t_swap), 0, A_DEFFLOAT, 0);
+ class_addcreator((t_newmethod)swap_new, gensym("fswap"), A_DEFFLOAT, 0);
+ class_addbang(swap_class, swap_bang);
+ class_addfloat(swap_class, swap_float);
+}
+
+/* -------------------------- change ------------------------------ */
+static t_class *change_class;
+
+typedef struct _change
+{
+ t_object x_obj;
+ t_float x_f;
+} t_change;
+
+static void *change_new(t_floatarg f)
+{
+ t_change *x = (t_change *)pd_new(change_class);
+ x->x_f = f;
+ outlet_new(&x->x_obj, &s_float);
+ return (x);
+}
+
+static void change_bang(t_change *x)
+{
+ outlet_float(x->x_obj.ob_outlet, x->x_f);
+}
+
+static void change_float(t_change *x, t_float f)
+{
+ if (f != x->x_f)
+ {
+ x->x_f = f;
+ outlet_float(x->x_obj.ob_outlet, x->x_f);
+ }
+}
+
+static void change_set(t_change *x, t_float f)
+{
+ x->x_f = f;
+}
+
+void change_setup(void)
+{
+ change_class = class_new(gensym("change"), (t_newmethod)change_new, 0,
+ sizeof(t_change), 0, A_DEFFLOAT, 0);
+ class_addbang(change_class, change_bang);
+ class_addfloat(change_class, change_float);
+ class_addmethod(change_class, (t_method)change_set, gensym("set"),
+ A_DEFFLOAT, 0);
+}
+
+/* -------------------- value ------------------------------ */
+
+static t_class *value_class, *vcommon_class;
+
+typedef struct vcommon
+{
+ t_pd c_pd;
+ int c_refcount;
+ t_float c_f;
+} t_vcommon;
+
+typedef struct _value
+{
+ t_object x_obj;
+ t_symbol *x_sym;
+ t_float *x_floatstar;
+} t_value;
+
+ /* get a pointer to a named floating-point variable. The variable
+ belongs to a "vcommon" object, which is created if necessary. */
+t_float *value_get(t_symbol *s)
+{
+ t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class);
+ if (!c)
+ {
+ c = (t_vcommon *)pd_new(vcommon_class);
+ c->c_f = 0;
+ c->c_refcount = 0;
+ pd_bind(&c->c_pd, s);
+ }
+ c->c_refcount++;
+ return (&c->c_f);
+}
+
+ /* release a variable. This only frees the "vcommon" resource when the
+ last interested party releases it. */
+void value_release(t_symbol *s)
+{
+ t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class);
+ if (c)
+ {
+ if (!--c->c_refcount)
+ {
+ pd_unbind(&c->c_pd, s);
+ pd_free(&c->c_pd);
+ }
+ }
+ else bug("value_release");
+}
+
+/*
+ * value_getfloat -- obtain the float value of a "value" object
+ * return 0 on success, 1 otherwise
+ */
+int
+value_getfloat(t_symbol *s, t_float *f)
+{
+ t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class);
+ if (!c)
+ return (1);
+ *f = c->c_f;
+ return (0);
+}
+
+/*
+ * value_setfloat -- set the float value of a "value" object
+ * return 0 on success, 1 otherwise
+ */
+int
+value_setfloat(t_symbol *s, t_float f)
+{
+ t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class);
+ if (!c)
+ return (1);
+ c->c_f = f;
+ return (0);
+}
+
+static void *value_new(t_symbol *s)
+{
+ t_value *x = (t_value *)pd_new(value_class);
+ x->x_sym = s;
+ x->x_floatstar = value_get(s);
+ outlet_new(&x->x_obj, &s_float);
+ return (x);
+}
+
+static void value_bang(t_value *x)
+{
+ outlet_float(x->x_obj.ob_outlet, *x->x_floatstar);
+}
+
+static void value_float(t_value *x, t_float f)
+{
+ *x->x_floatstar = f;
+}
+
+static void value_ff(t_value *x)
+{
+ value_release(x->x_sym);
+}
+
+static void value_setup(void)
+{
+ value_class = class_new(gensym("value"), (t_newmethod)value_new,
+ (t_method)value_ff,
+ sizeof(t_value), 0, A_DEFSYM, 0);
+ class_addcreator((t_newmethod)value_new, gensym("v"), A_DEFSYM, 0);
+ class_addbang(value_class, value_bang);
+ class_addfloat(value_class, value_float);
+ vcommon_class = class_new(gensym("value"), 0, 0,
+ sizeof(t_vcommon), CLASS_PD, 0);
+}
+
+/* -------------- overall setup routine for this file ----------------- */
+
+void x_connective_setup(void)
+{
+ pdint_setup();
+ pdfloat_setup();
+ pdsymbol_setup();
+ bang_setup();
+ send_setup();
+ receive_setup();
+ select_setup();
+ route_setup();
+ pack_setup();
+ unpack_setup();
+ trigger_setup();
+ spigot_setup();
+ moses_setup();
+ until_setup();
+ makefilename_setup();
+ swap_setup();
+ change_setup();
+ value_setup();
+}
diff --git a/pd/src/x_gui.c b/pd/src/x_gui.c
new file mode 100644
index 00000000..03e1b0dd
--- /dev/null
+++ b/pd/src/x_gui.c
@@ -0,0 +1,377 @@
+/* Copyright (c) 1997-2000 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* dialogs. LATER, deal with the situation where the object goes
+away before the panel does... */
+
+#include "m_pd.h"
+#include <stdio.h>
+#include <string.h>
+#ifdef UNIX
+#include <unistd.h>
+#endif
+
+/* --------------------- graphics responder ---------------- */
+
+/* make one of these if you want to put up a dialog window but want to be
+protected from getting deleted and then having the dialog call you back. In
+this design the calling object doesn't have to keep the address of the dialog
+window around; instead we keep a list of all open dialogs. Any object that
+might have dialogs, when it is deleted, simply checks down the dialog window
+list and breaks off any dialogs that might later have sent messages to it.
+Only when the dialog window itself closes do we delete the gfxstub object. */
+
+static t_class *gfxstub_class;
+
+typedef struct _gfxstub
+{
+ t_pd x_pd;
+ t_pd *x_owner;
+ void *x_key;
+ t_symbol *x_sym;
+ struct _gfxstub *x_next;
+} t_gfxstub;
+
+static t_gfxstub *gfxstub_list;
+
+ /* create a new one. the "key" is an address by which the owner
+ will identify it later; if the owner only wants one dialog, this
+ could just be a pointer to the owner itself. The string "cmd"
+ is a TK command to create the dialog, with "%s" embedded in
+ it so we can provide a name by which the GUI can send us back
+ messages; e.g., "pdtk_canvas_dofont %s 10". */
+
+void gfxstub_new(t_pd *owner, void *key, const char *cmd)
+{
+ char buf[MAXPDSTRING];
+ char namebuf[80];
+ t_gfxstub *x;
+ t_symbol *s;
+ /* if any exists with matching key, no need to make a
+ new one; just tell tk to send it front. */
+ for (x = gfxstub_list; x; x = x->x_next)
+ {
+ if (x->x_key == key)
+ {
+ sys_vgui("raise .gfxstub%x\n", x);
+ sys_vgui("focus .gfxstub%x\n", x);
+ return;
+ }
+ }
+ if (strlen(cmd) + 84 > MAXPDSTRING)
+ return;
+ x = (t_gfxstub *)pd_new(gfxstub_class);
+ sprintf(namebuf, ".gfxstub%x", (t_int)x);
+
+ s = gensym(namebuf);
+ pd_bind(&x->x_pd, s);
+ x->x_owner = owner;
+ x->x_sym = s;
+ x->x_key = key;
+ x->x_next = gfxstub_list;
+ gfxstub_list = x;
+ sprintf(buf, cmd, s->s_name);
+ sys_gui(buf);
+}
+
+static void gfxstub_offlist(t_gfxstub *x)
+{
+ t_gfxstub *y1, *y2;
+ if (gfxstub_list == x)
+ gfxstub_list = x->x_next;
+ else for (y1 = gfxstub_list; y2 = y1->x_next; y1 = y2)
+ if (y2 == x)
+ {
+ y1->x_next = y2->x_next;
+ break;
+ }
+}
+
+ /* if the owner disappears, we still may have to stay around until our
+ dialog window signs off. Anyway we can now tell the GUI to destroy the
+ window. */
+void gfxstub_deleteforkey(void *key)
+{
+ t_gfxstub *y;
+ int didit = 1;
+ while (didit)
+ {
+ didit = 0;
+ for (y = gfxstub_list; y; y = y->x_next)
+ {
+ if (y->x_key == key)
+ {
+ sys_vgui("destroy .gfxstub%x\n", y);
+ y->x_owner = 0;
+ gfxstub_offlist(y);
+ didit = 1;
+ break;
+ }
+ }
+ }
+}
+
+/* --------- pd messages for gfxstub (these come from the GUI) ---------- */
+
+ /* "cancel" to request that we close the dialog window. */
+static void gfxstub_cancel(t_gfxstub *x)
+{
+ gfxstub_deleteforkey(x->x_key);
+}
+
+ /* "signoff" comes from the GUI to say the dialog window closed. */
+static void gfxstub_signoff(t_gfxstub *x)
+{
+ gfxstub_offlist(x);
+ pd_free(&x->x_pd);
+}
+
+static t_binbuf *gfxstub_binbuf;
+
+ /* a series of "data" messages rebuilds a scalar */
+static void gfxstub_data(t_gfxstub *x, t_symbol *s, int argc, t_atom *argv)
+{
+ if (!gfxstub_binbuf)
+ gfxstub_binbuf = binbuf_new();
+ binbuf_add(gfxstub_binbuf, argc, argv);
+ binbuf_addsemi(gfxstub_binbuf);
+}
+ /* the "end" message terminates rebuilding the scalar */
+static void gfxstub_end(t_gfxstub *x)
+{
+ canvas_dataproperties((t_canvas *)x->x_owner,
+ (t_scalar *)x->x_key, gfxstub_binbuf);
+ binbuf_free(gfxstub_binbuf);
+ gfxstub_binbuf = 0;
+}
+
+ /* anything else is a message from the dialog window to the owner;
+ just forward it. */
+static void gfxstub_anything(t_gfxstub *x, t_symbol *s, int argc, t_atom *argv)
+{
+ if (x->x_owner)
+ pd_typedmess(x->x_owner, s, argc, argv);
+}
+
+static void gfxstub_free(t_gfxstub *x)
+{
+ pd_unbind(&x->x_pd, x->x_sym);
+}
+
+static void gfxstub_setup(void)
+{
+ gfxstub_class = class_new(gensym("gfxstub"), (t_newmethod)gfxstub_new,
+ (t_method)gfxstub_free,
+ sizeof(t_gfxstub), CLASS_PD, 0);
+ class_addanything(gfxstub_class, gfxstub_anything);
+ class_addmethod(gfxstub_class, (t_method)gfxstub_signoff,
+ gensym("signoff"), 0);
+ class_addmethod(gfxstub_class, (t_method)gfxstub_data,
+ gensym("data"), A_GIMME, 0);
+ class_addmethod(gfxstub_class, (t_method)gfxstub_end,
+ gensym("end"), 0);
+ class_addmethod(gfxstub_class, (t_method)gfxstub_cancel,
+ gensym("cancel"), 0);
+}
+
+/* -------------------------- openpanel ------------------------------ */
+
+static t_class *openpanel_class;
+
+typedef struct _openpanel
+{
+ t_object x_obj;
+ t_symbol *x_s;
+} t_openpanel;
+
+static void *openpanel_new(void)
+{
+ char buf[50];
+ t_openpanel *x = (t_openpanel *)pd_new(openpanel_class);
+ sprintf(buf, "d%x", (t_int)x);
+ x->x_s = gensym(buf);
+ pd_bind(&x->x_obj.ob_pd, x->x_s);
+ outlet_new(&x->x_obj, &s_symbol);
+ return (x);
+}
+
+static void openpanel_bang(t_openpanel *x)
+{
+ sys_vgui("pdtk_openpanel %s\n", x->x_s->s_name);
+}
+
+static void openpanel_symbol(t_openpanel *x, t_symbol *s)
+{
+ outlet_symbol(x->x_obj.ob_outlet, s);
+}
+
+static void openpanel_free(t_openpanel *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, x->x_s);
+}
+
+static void openpanel_setup(void)
+{
+ openpanel_class = class_new(gensym("openpanel"),
+ (t_newmethod)openpanel_new, (t_method)openpanel_free,
+ sizeof(t_openpanel), 0, A_DEFFLOAT, 0);
+ class_addbang(openpanel_class, openpanel_bang);
+ class_addsymbol(openpanel_class, openpanel_symbol);
+}
+
+/* -------------------------- savepanel ------------------------------ */
+
+static t_class *savepanel_class;
+
+typedef struct _savepanel
+{
+ t_object x_obj;
+ t_symbol *x_s;
+} t_savepanel;
+
+static void *savepanel_new(void)
+{
+ char buf[50];
+ t_savepanel *x = (t_savepanel *)pd_new(savepanel_class);
+ sprintf(buf, "d%x", (t_int)x);
+ x->x_s = gensym(buf);
+ pd_bind(&x->x_obj.ob_pd, x->x_s);
+ outlet_new(&x->x_obj, &s_symbol);
+ return (x);
+}
+
+static void savepanel_bang(t_savepanel *x)
+{
+ sys_vgui("pdtk_savepanel %s\n", x->x_s->s_name);
+}
+
+static void savepanel_symbol(t_savepanel *x, t_symbol *s)
+{
+ outlet_symbol(x->x_obj.ob_outlet, s);
+}
+
+static void savepanel_free(t_savepanel *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, x->x_s);
+}
+
+static void savepanel_setup(void)
+{
+ savepanel_class = class_new(gensym("savepanel"),
+ (t_newmethod)savepanel_new, (t_method)savepanel_free,
+ sizeof(t_savepanel), 0, A_DEFFLOAT, 0);
+ class_addbang(savepanel_class, savepanel_bang);
+ class_addsymbol(savepanel_class, savepanel_symbol);
+}
+
+/* ---------------------- key and its relatives ------------------ */
+
+static t_symbol *key_sym, *keyup_sym, *keyname_sym;
+static t_class *key_class, *keyup_class, *keyname_class;
+
+typedef struct _key
+{
+ t_object x_obj;
+} t_key;
+
+static void *key_new( void)
+{
+ t_key *x = (t_key *)pd_new(key_class);
+ outlet_new(&x->x_obj, &s_float);
+ pd_bind(&x->x_obj.ob_pd, key_sym);
+ return (x);
+}
+
+static void key_float(t_key *x, t_floatarg f)
+{
+ outlet_float(x->x_obj.ob_outlet, f);
+}
+
+static void key_free(t_key *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, key_sym);
+}
+
+typedef struct _keyup
+{
+ t_object x_obj;
+} t_keyup;
+
+static void *keyup_new( void)
+{
+ t_keyup *x = (t_keyup *)pd_new(keyup_class);
+ outlet_new(&x->x_obj, &s_float);
+ pd_bind(&x->x_obj.ob_pd, keyup_sym);
+ return (x);
+}
+
+static void keyup_float(t_keyup *x, t_floatarg f)
+{
+ outlet_float(x->x_obj.ob_outlet, f);
+}
+
+static void keyup_free(t_keyup *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, keyup_sym);
+}
+
+typedef struct _keyname
+{
+ t_object x_obj;
+ t_outlet *x_outlet1;
+ t_outlet *x_outlet2;
+} t_keyname;
+
+static void *keyname_new( void)
+{
+ t_keyname *x = (t_keyname *)pd_new(keyname_class);
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_symbol);
+ pd_bind(&x->x_obj.ob_pd, keyname_sym);
+ return (x);
+}
+
+static void keyname_list(t_keyname *x, t_symbol *s, int ac, t_atom *av)
+{
+ outlet_symbol(x->x_outlet2, atom_getsymbolarg(1, ac, av));
+ outlet_float(x->x_outlet1, atom_getfloatarg(0, ac, av));
+}
+
+static void keyname_free(t_keyname *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, keyname_sym);
+}
+
+static void key_setup(void)
+{
+ key_class = class_new(gensym("key"),
+ (t_newmethod)key_new, (t_method)key_free,
+ sizeof(t_key), CLASS_NOINLET, 0);
+ class_addfloat(key_class, key_float);
+ key_sym = gensym("#key");
+
+ keyup_class = class_new(gensym("keyup"),
+ (t_newmethod)keyup_new, (t_method)keyup_free,
+ sizeof(t_keyup), CLASS_NOINLET, 0);
+ class_addfloat(keyup_class, keyup_float);
+ keyup_sym = gensym("#keyup");
+ class_sethelpsymbol(keyup_class, gensym("key"));
+
+ keyname_class = class_new(gensym("keyname"),
+ (t_newmethod)keyname_new, (t_method)keyname_free,
+ sizeof(t_keyname), CLASS_NOINLET, 0);
+ class_addlist(keyname_class, keyname_list);
+ keyname_sym = gensym("#keyname");
+ class_sethelpsymbol(keyname_class, gensym("key"));
+}
+
+/* -------------------------- setup routine ------------------------------ */
+
+void x_gui_setup(void)
+{
+ gfxstub_setup();
+ openpanel_setup();
+ savepanel_setup();
+ key_setup();
+}
diff --git a/pd/src/x_interface.c b/pd/src/x_interface.c
new file mode 100644
index 00000000..068b0bbc
--- /dev/null
+++ b/pd/src/x_interface.c
@@ -0,0 +1,78 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* interface objects */
+
+#include "m_pd.h"
+
+/* -------------------------- print ------------------------------ */
+static t_class *print_class;
+
+typedef struct _print
+{
+ t_object x_obj;
+ t_symbol *x_sym;
+} t_print;
+
+static void *print_new(t_symbol *s)
+{
+ t_print *x = (t_print *)pd_new(print_class);
+ if (*s->s_name) x->x_sym = s;
+ else x->x_sym = gensym("print");
+ return (x);
+}
+
+static void print_bang(t_print *x)
+{
+ post("%s: bang", x->x_sym->s_name);
+}
+
+static void print_pointer(t_print *x, t_gpointer *gp)
+{
+ post("%s: (gpointer)", x->x_sym->s_name);
+}
+
+static void print_float(t_print *x, t_float f)
+{
+ post("%s: %g", x->x_sym->s_name, f);
+}
+
+static void print_list(t_print *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int i;
+ char buf[80];
+ if (argc && argv->a_type != A_SYMBOL) startpost("%s:", x->x_sym->s_name);
+ else startpost("%s: %s", x->x_sym->s_name,
+ (argc > 1 ? s_list.s_name : (argc == 1 ? s_symbol.s_name :
+ s_bang.s_name)));
+ postatom(argc, argv);
+ endpost();
+}
+
+static void print_anything(t_print *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int i;
+ char buf[80];
+ startpost("%s: %s", x->x_sym->s_name, s->s_name);
+ postatom(argc, argv);
+ endpost();
+}
+
+static void print_setup(void)
+{
+ print_class = class_new(gensym("print"), (t_newmethod)print_new, 0,
+ sizeof(t_print), 0, A_DEFSYM, 0);
+ class_addbang(print_class, print_bang);
+ class_addfloat(print_class, print_float);
+ class_addpointer(print_class, print_pointer);
+ class_addlist(print_class, print_list);
+ class_addanything(print_class, print_anything);
+}
+
+
+
+void x_interface_setup(void)
+{
+ print_setup();
+}
diff --git a/pd/src/x_midi.c b/pd/src/x_midi.c
new file mode 100644
index 00000000..f7d6529f
--- /dev/null
+++ b/pd/src/x_midi.c
@@ -0,0 +1,1314 @@
+/* Copyright (c) 1997-2001 Miller Puckette and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* MIDI. */
+
+#include "m_pd.h"
+void outmidi_noteon(int portno, int channel, int pitch, int velo);
+void outmidi_controlchange(int portno, int channel, int ctlno, int value);
+void outmidi_programchange(int portno, int channel, int value);
+void outmidi_pitchbend(int portno, int channel, int value);
+void outmidi_aftertouch(int portno, int channel, int value);
+void outmidi_polyaftertouch(int portno, int channel, int pitch, int value);
+void outmidi_mclk(int portno);
+
+/* ----------------------- midiin and sysexin ------------------------- */
+
+static t_symbol *midiin_sym, *sysexin_sym;
+
+static t_class *midiin_class, *sysexin_class;
+
+typedef struct _midiin
+{
+ t_object x_obj;
+ t_outlet *x_outlet1;
+ t_outlet *x_outlet2;
+} t_midiin;
+
+static void *midiin_new( void)
+{
+ t_midiin *x = (t_midiin *)pd_new(midiin_class);
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+ pd_bind(&x->x_obj.ob_pd, midiin_sym);
+#ifndef __linux__
+ pd_error(x, "midiin: works under Linux only");
+#endif
+ return (x);
+}
+
+static void midiin_list(t_midiin *x, t_symbol *s, int ac, t_atom *av)
+{
+ outlet_float(x->x_outlet2, atom_getfloatarg(1, ac, av) + 1);
+ outlet_float(x->x_outlet1, atom_getfloatarg(0, ac, av));
+}
+
+static void midiin_free(t_midiin *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, midiin_sym);
+}
+
+static void *sysexin_new( void)
+{
+ t_midiin *x = (t_midiin *)pd_new(sysexin_class);
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+ pd_bind(&x->x_obj.ob_pd, sysexin_sym);
+#ifndef __linux__
+ pd_error(x, "sysexin: works under Linux only");
+#endif
+ return (x);
+}
+
+static void sysexin_free(t_midiin *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, sysexin_sym);
+}
+
+static void midiin_setup(void)
+{
+ midiin_class = class_new(gensym("midiin"), (t_newmethod)midiin_new,
+ (t_method)midiin_free, sizeof(t_midiin),
+ CLASS_NOINLET, A_DEFFLOAT, 0);
+ class_addlist(midiin_class, midiin_list);
+ class_sethelpsymbol(midiin_class, gensym("midi"));
+ midiin_sym = gensym("#midiin");
+
+ sysexin_class = class_new(gensym("sysexin"), (t_newmethod)sysexin_new,
+ (t_method)sysexin_free, sizeof(t_midiin),
+ CLASS_NOINLET, A_DEFFLOAT, 0);
+ class_addlist(sysexin_class, midiin_list);
+ class_sethelpsymbol(sysexin_class, gensym("midi"));
+ sysexin_sym = gensym("#sysexin");
+}
+
+void inmidi_byte(int portno, int byte)
+{
+ static int sysex;
+ t_atom at[2];
+ if (byte == 0xf0)
+ sysex |= (1 << portno);
+ if (sysexin_sym->s_thing && (sysex & (1 << portno)))
+ {
+ SETFLOAT(at, byte);
+ SETFLOAT(at+1, portno + 1);
+ pd_list(sysexin_sym->s_thing, 0, 2, at);
+ }
+ if (byte == 0xf7)
+ sysex &= (~(1 << portno));
+
+ if (midiin_sym->s_thing)
+ {
+ SETFLOAT(at, byte);
+ SETFLOAT(at+1, portno + 1);
+ pd_list(midiin_sym->s_thing, 0, 2, at);
+ }
+}
+
+/* ----------------------- notein ------------------------- */
+
+static t_symbol *notein_sym;
+
+static t_class *notein_class;
+
+typedef struct _notein
+{
+ t_object x_obj;
+ t_float x_channel;
+ t_outlet *x_outlet1;
+ t_outlet *x_outlet2;
+ t_outlet *x_outlet3;
+} t_notein;
+
+static void *notein_new(t_floatarg f)
+{
+ t_notein *x = (t_notein *)pd_new(notein_class);
+ x->x_channel = f;
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+ if (f == 0) x->x_outlet3 = outlet_new(&x->x_obj, &s_float);
+ pd_bind(&x->x_obj.ob_pd, notein_sym);
+ return (x);
+}
+
+static void notein_list(t_notein *x, t_symbol *s, int argc, t_atom *argv)
+{
+ float pitch = atom_getfloatarg(0, argc, argv);
+ float velo = atom_getfloatarg(1, argc, argv);
+ float channel = atom_getfloatarg(2, argc, argv);
+ if (x->x_channel != 0)
+ {
+ if (channel != x->x_channel) return;
+ outlet_float(x->x_outlet2, velo);
+ outlet_float(x->x_outlet1, pitch);
+ }
+ else
+ {
+ outlet_float(x->x_outlet3, channel);
+ outlet_float(x->x_outlet2, velo);
+ outlet_float(x->x_outlet1, pitch);
+ }
+}
+
+static void notein_free(t_notein *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, notein_sym);
+}
+
+static void notein_setup(void)
+{
+ notein_class = class_new(gensym("notein"), (t_newmethod)notein_new,
+ (t_method)notein_free, sizeof(t_notein), CLASS_NOINLET, A_DEFFLOAT, 0);
+ class_addlist(notein_class, notein_list);
+ class_sethelpsymbol(notein_class, gensym("midi"));
+ notein_sym = gensym("#notein");
+}
+
+void inmidi_noteon(int portno, int channel, int pitch, int velo)
+{
+ if (notein_sym->s_thing)
+ {
+ t_atom at[3];
+ SETFLOAT(at, pitch);
+ SETFLOAT(at+1, velo);
+ SETFLOAT(at+2, (channel + (portno << 4) + 1));
+ pd_list(notein_sym->s_thing, &s_list, 3, at);
+ }
+}
+
+/* ----------------------- ctlin ------------------------- */
+
+static t_symbol *ctlin_sym;
+
+static t_class *ctlin_class;
+
+typedef struct _ctlin
+{
+ t_object x_obj;
+ t_float x_channel;
+ t_float x_ctlno;
+ t_outlet *x_outlet1;
+ t_outlet *x_outlet2;
+ t_outlet *x_outlet3;
+} t_ctlin;
+
+static void *ctlin_new(t_symbol *s, int argc, t_atom *argv)
+{
+ int ctlno, channel;
+ t_ctlin *x = (t_ctlin *)pd_new(ctlin_class);
+ if (!argc) ctlno = -1;
+ else ctlno = atom_getfloatarg(0, argc, argv);
+ channel = atom_getfloatarg(1, argc, argv);
+ x->x_channel = channel;
+ x->x_ctlno = ctlno;
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+ if (!channel)
+ {
+ if (x->x_ctlno < 0) x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet3 = outlet_new(&x->x_obj, &s_float);
+ }
+ pd_bind(&x->x_obj.ob_pd, ctlin_sym);
+ return (x);
+}
+
+static void ctlin_list(t_ctlin *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_float ctlnumber = atom_getfloatarg(0, argc, argv);
+ t_float value = atom_getfloatarg(1, argc, argv);
+ t_float channel = atom_getfloatarg(2, argc, argv);
+ if (x->x_ctlno >= 0 && x->x_ctlno != ctlnumber) return;
+ if (x->x_channel > 0 && x->x_channel != channel) return;
+ if (x->x_channel == 0) outlet_float(x->x_outlet3, channel);
+ if (x->x_ctlno < 0) outlet_float(x->x_outlet2, ctlnumber);
+ outlet_float(x->x_outlet1, value);
+}
+
+static void ctlin_free(t_ctlin *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, ctlin_sym);
+}
+
+static void ctlin_setup(void)
+{
+ ctlin_class = class_new(gensym("ctlin"), (t_newmethod)ctlin_new,
+ (t_method)ctlin_free, sizeof(t_ctlin),
+ CLASS_NOINLET, A_GIMME, 0);
+ class_addlist(ctlin_class, ctlin_list);
+ class_sethelpsymbol(ctlin_class, gensym("midi"));
+ ctlin_sym = gensym("#ctlin");
+}
+
+void inmidi_controlchange(int portno, int channel, int ctlnumber, int value)
+{
+ if (ctlin_sym->s_thing)
+ {
+ t_atom at[3];
+ SETFLOAT(at, ctlnumber);
+ SETFLOAT(at+1, value);
+ SETFLOAT(at+2, (channel + (portno << 4) + 1));
+ pd_list(ctlin_sym->s_thing, &s_list, 3, at);
+ }
+}
+
+/* ----------------------- pgmin ------------------------- */
+
+static t_symbol *pgmin_sym;
+
+static t_class *pgmin_class;
+
+typedef struct _pgmin
+{
+ t_object x_obj;
+ t_float x_channel;
+ t_outlet *x_outlet1;
+ t_outlet *x_outlet2;
+} t_pgmin;
+
+static void *pgmin_new(t_floatarg f)
+{
+ t_pgmin *x = (t_pgmin *)pd_new(pgmin_class);
+ x->x_channel = f;
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+ if (f == 0) x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+ pd_bind(&x->x_obj.ob_pd, pgmin_sym);
+ return (x);
+}
+
+static void pgmin_list(t_pgmin *x, t_symbol *s, int argc, t_atom *argv)
+{
+ float value = atom_getfloatarg(0, argc, argv);
+ float channel = atom_getfloatarg(1, argc, argv);
+ if (x->x_channel != 0)
+ {
+ if (channel != x->x_channel) return;
+ outlet_float(x->x_outlet1, value);
+ }
+ else
+ {
+ outlet_float(x->x_outlet2, channel);
+ outlet_float(x->x_outlet1, value);
+ }
+}
+
+static void pgmin_free(t_pgmin *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, pgmin_sym);
+}
+
+static void pgmin_setup(void)
+{
+ pgmin_class = class_new(gensym("pgmin"), (t_newmethod)pgmin_new,
+ (t_method)pgmin_free, sizeof(t_pgmin),
+ CLASS_NOINLET, A_DEFFLOAT, 0);
+ class_addlist(pgmin_class, pgmin_list);
+ class_sethelpsymbol(pgmin_class, gensym("midi"));
+ pgmin_sym = gensym("#pgmin");
+}
+
+void inmidi_programchange(int portno, int channel, int value)
+{
+ if (pgmin_sym->s_thing)
+ {
+ t_atom at[2];
+ SETFLOAT(at, value + 1);
+ SETFLOAT(at+1, (channel + (portno << 4) + 1));
+ pd_list(pgmin_sym->s_thing, &s_list, 2, at);
+ }
+}
+
+/* ----------------------- bendin ------------------------- */
+
+static t_symbol *bendin_sym;
+
+static t_class *bendin_class;
+
+typedef struct _bendin
+{
+ t_object x_obj;
+ t_float x_channel;
+ t_outlet *x_outlet1;
+ t_outlet *x_outlet2;
+} t_bendin;
+
+static void *bendin_new(t_floatarg f)
+{
+ t_bendin *x = (t_bendin *)pd_new(bendin_class);
+ x->x_channel = f;
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+ if (f == 0) x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+ pd_bind(&x->x_obj.ob_pd, bendin_sym);
+ return (x);
+}
+
+static void bendin_list(t_bendin *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_float value = atom_getfloatarg(0, argc, argv);
+ t_float channel = atom_getfloatarg(1, argc, argv);
+ if (x->x_channel != 0)
+ {
+ if (channel != x->x_channel) return;
+ outlet_float(x->x_outlet1, value);
+ }
+ else
+ {
+ outlet_float(x->x_outlet2, channel);
+ outlet_float(x->x_outlet1, value);
+ }
+}
+
+static void bendin_free(t_bendin *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, bendin_sym);
+}
+
+static void bendin_setup(void)
+{
+ bendin_class = class_new(gensym("bendin"), (t_newmethod)bendin_new,
+ (t_method)bendin_free, sizeof(t_bendin), CLASS_NOINLET, A_DEFFLOAT, 0);
+ class_addlist(bendin_class, bendin_list);
+ class_sethelpsymbol(bendin_class, gensym("midi"));
+ bendin_sym = gensym("#bendin");
+}
+
+void inmidi_pitchbend(int portno, int channel, int value)
+{
+ if (bendin_sym->s_thing)
+ {
+ t_atom at[2];
+ SETFLOAT(at, value);
+ SETFLOAT(at+1, (channel + (portno << 4) + 1));
+ pd_list(bendin_sym->s_thing, &s_list, 2, at);
+ }
+}
+
+/* ----------------------- touchin ------------------------- */
+
+static t_symbol *touchin_sym;
+
+static t_class *touchin_class;
+
+typedef struct _touchin
+{
+ t_object x_obj;
+ t_float x_channel;
+ t_outlet *x_outlet1;
+ t_outlet *x_outlet2;
+} t_touchin;
+
+static void *touchin_new(t_floatarg f)
+{
+ t_touchin *x = (t_touchin *)pd_new(touchin_class);
+ x->x_channel = f;
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+ if (f == 0) x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+ pd_bind(&x->x_obj.ob_pd, touchin_sym);
+ return (x);
+}
+
+static void touchin_list(t_touchin *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_float value = atom_getfloatarg(0, argc, argv);
+ t_float channel = atom_getfloatarg(1, argc, argv);
+ if (x->x_channel)
+ {
+ if (channel != x->x_channel) return;
+ outlet_float(x->x_outlet1, value);
+ }
+ else
+ {
+ outlet_float(x->x_outlet2, channel);
+ outlet_float(x->x_outlet1, value);
+ }
+}
+
+static void touchin_free(t_touchin *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, touchin_sym);
+}
+
+static void touchin_setup(void)
+{
+ touchin_class = class_new(gensym("touchin"), (t_newmethod)touchin_new,
+ (t_method)touchin_free, sizeof(t_touchin),
+ CLASS_NOINLET, A_DEFFLOAT, 0);
+ class_addlist(touchin_class, touchin_list);
+ class_sethelpsymbol(touchin_class, gensym("midi"));
+ touchin_sym = gensym("#touchin");
+}
+
+void inmidi_aftertouch(int portno, int channel, int value)
+{
+ if (touchin_sym->s_thing)
+ {
+ t_atom at[2];
+ SETFLOAT(at, value);
+ SETFLOAT(at+1, (channel + (portno << 4) + 1));
+ pd_list(touchin_sym->s_thing, &s_list, 2, at);
+ }
+}
+
+/* ----------------------- polytouchin ------------------------- */
+
+static t_symbol *polytouchin_sym;
+
+static t_class *polytouchin_class;
+
+typedef struct _polytouchin
+{
+ t_object x_obj;
+ t_float x_channel;
+ t_outlet *x_outlet1;
+ t_outlet *x_outlet2;
+ t_outlet *x_outlet3;
+} t_polytouchin;
+
+static void *polytouchin_new(t_floatarg f)
+{
+ t_polytouchin *x = (t_polytouchin *)pd_new(polytouchin_class);
+ x->x_channel = f;
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+ if (f == 0) x->x_outlet3 = outlet_new(&x->x_obj, &s_float);
+ pd_bind(&x->x_obj.ob_pd, polytouchin_sym);
+ return (x);
+}
+
+static void polytouchin_list(t_polytouchin *x, t_symbol *s, int argc,
+ t_atom *argv)
+{
+ t_float pitch = atom_getfloatarg(0, argc, argv);
+ t_float value = atom_getfloatarg(1, argc, argv);
+ t_float channel = atom_getfloatarg(2, argc, argv);
+ if (x->x_channel != 0)
+ {
+ if (channel != x->x_channel) return;
+ outlet_float(x->x_outlet2, pitch);
+ outlet_float(x->x_outlet1, value);
+ }
+ else
+ {
+ outlet_float(x->x_outlet3, channel);
+ outlet_float(x->x_outlet2, pitch);
+ outlet_float(x->x_outlet1, value);
+ }
+}
+
+static void polytouchin_free(t_polytouchin *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, polytouchin_sym);
+}
+
+static void polytouchin_setup(void)
+{
+ polytouchin_class = class_new(gensym("polytouchin"),
+ (t_newmethod)polytouchin_new, (t_method)polytouchin_free,
+ sizeof(t_polytouchin), CLASS_NOINLET, A_DEFFLOAT, 0);
+ class_addlist(polytouchin_class, polytouchin_list);
+ class_sethelpsymbol(polytouchin_class, gensym("midi"));
+ polytouchin_sym = gensym("#polytouchin");
+}
+
+void inmidi_polyaftertouch(int portno, int channel, int pitch, int value)
+{
+ if (polytouchin_sym->s_thing)
+ {
+ t_atom at[3];
+ SETFLOAT(at, pitch);
+ SETFLOAT(at+1, value);
+ SETFLOAT(at+2, (channel + (portno << 4) + 1));
+ pd_list(polytouchin_sym->s_thing, &s_list, 3, at);
+ }
+}
+
+/*----------------------- midiclkin--(midi F8 message )---------------------*/
+static t_symbol *midiclkin_sym;
+
+static t_class *midiclkin_class;
+
+
+typedef struct _midiclkin
+{
+ t_object x_obj;
+ t_outlet *x_outlet1;
+ t_outlet *x_outlet2;
+} t_midiclkin;
+
+static void *midiclkin_new(t_floatarg f)
+{
+ t_midiclkin *x = (t_midiclkin *)pd_new(midiclkin_class);
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+ pd_bind(&x->x_obj.ob_pd, midiclkin_sym);
+ return (x);
+}
+
+static void midiclkin_list(t_midiclkin *x, t_symbol *s, int argc, t_atom *argv)
+{
+ float value = atom_getfloatarg(0, argc, argv);
+ float count = atom_getfloatarg(1, argc, argv);
+ outlet_float(x->x_outlet2, count);
+ outlet_float(x->x_outlet1, value);
+}
+
+static void midiclkin_free(t_midiclkin *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, midiclkin_sym);
+}
+
+static void midiclkin_setup(void)
+{
+ midiclkin_class = class_new(gensym("midiclkin"),
+ (t_newmethod)midiclkin_new, (t_method)midiclkin_free,
+ sizeof(t_midiclkin), CLASS_NOINLET, A_DEFFLOAT, 0);
+ class_addlist(midiclkin_class, midiclkin_list);
+ class_sethelpsymbol(midiclkin_class, gensym("midi"));
+ midiclkin_sym = gensym("#midiclkin");
+}
+
+void inmidi_clk(double timing)
+{
+
+ static float prev = 0;
+ static float count = 0;
+ float cur,diff;
+
+ if (midiclkin_sym->s_thing)
+ {
+ t_atom at[2];
+ diff =timing - prev;
+ count++;
+
+ if (count == 3)
+ { /* 24 count per quoter note */
+ SETFLOAT(at, 1 );
+ count = 0;
+ }
+ else SETFLOAT(at, 0);
+
+ SETFLOAT(at+1, diff);
+ pd_list(midiclkin_sym->s_thing, &s_list, 2, at);
+ prev = timing;
+ }
+}
+
+/*----------midirealtimein (midi FA,FB,FC,FF message )-----------------*/
+
+static t_symbol *midirealtimein_sym;
+
+static t_class *midirealtimein_class;
+
+typedef struct _midirealtimein
+{
+ t_object x_obj;
+ t_outlet *x_outlet1;
+ t_outlet *x_outlet2;
+} t_midirealtimein;
+
+static void *midirealtimein_new( void)
+{
+ t_midirealtimein *x = (t_midirealtimein *)pd_new(midirealtimein_class);
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+ pd_bind(&x->x_obj.ob_pd, midirealtimein_sym);
+#ifndef NT
+ pd_error(x, "midirealtimein: works under NT only");
+#endif
+ return (x);
+}
+
+static void midirealtimein_list(t_midirealtimein *x, t_symbol *s,
+ int argc, t_atom *argv)
+{
+ float portno = atom_getfloatarg(0, argc, argv);
+ float byte = atom_getfloatarg(1, argc, argv);
+
+ outlet_float(x->x_outlet2, portno);
+ outlet_float(x->x_outlet1, byte);
+}
+
+static void midirealtimein_free(t_midirealtimein *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, midirealtimein_sym);
+}
+
+static void midirealtimein_setup(void)
+{
+ midirealtimein_class = class_new(gensym("midirealtimein"),
+ (t_newmethod)midirealtimein_new, (t_method)midirealtimein_free,
+ sizeof(t_midirealtimein), CLASS_NOINLET, A_DEFFLOAT, 0);
+ class_addlist(midirealtimein_class, midirealtimein_list);
+ class_sethelpsymbol(midirealtimein_class, gensym("midi"));
+ midirealtimein_sym = gensym("#midirealtimein");
+}
+
+void inmidi_realtimein(int portno, int SysMsg)
+{
+ if (midirealtimein_sym->s_thing)
+ {
+ t_atom at[2];
+ SETFLOAT(at, portno);
+ SETFLOAT(at+1, SysMsg);
+ pd_list(midirealtimein_sym->s_thing, &s_list, 1, at);
+ }
+}
+
+/* -------------------------- midiout -------------------------- */
+
+static t_class *midiout_class;
+
+void sys_putmidibyte(int portno, int byte);
+
+typedef struct _midiout
+{
+ t_object x_obj;
+ t_float x_portno;
+} t_midiout;
+
+static void *midiout_new(t_floatarg portno)
+{
+ t_midiout *x = (t_midiout *)pd_new(midiout_class);
+ if (portno <= 0) portno = 1;
+ x->x_portno = portno;
+ floatinlet_new(&x->x_obj, &x->x_portno);
+#ifdef __irix__
+ post("midiout: unimplemented in IRIX");
+#endif
+ return (x);
+}
+
+static void midiout_float(t_midiout *x, t_floatarg f)
+{
+ sys_putmidibyte(x->x_portno - 1, f);
+}
+
+static void midiout_setup(void)
+{
+ midiout_class = class_new(gensym("midiout"), (t_newmethod)midiout_new, 0,
+ sizeof(t_midiout), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_addfloat(midiout_class, midiout_float);
+ class_sethelpsymbol(midiout_class, gensym("midi"));
+}
+
+/* -------------------------- noteout -------------------------- */
+
+static t_class *noteout_class;
+
+typedef struct _noteout
+{
+ t_object x_obj;
+ t_float x_velo;
+ t_float x_channel;
+} t_noteout;
+
+static void *noteout_new(t_floatarg channel)
+{
+ t_noteout *x = (t_noteout *)pd_new(noteout_class);
+ x->x_velo = 0;
+ if (channel < 1) channel = 1;
+ x->x_channel = channel;
+ floatinlet_new(&x->x_obj, &x->x_velo);
+ floatinlet_new(&x->x_obj, &x->x_channel);
+ return (x);
+}
+
+static void noteout_float(t_noteout *x, t_float f)
+{
+ int binchan = x->x_channel - 1;
+ if (binchan < 0)
+ binchan = 0;
+ outmidi_noteon((binchan >> 4),
+ (binchan & 15), (int)f, (int)x->x_velo);
+}
+
+static void noteout_setup(void)
+{
+ noteout_class = class_new(gensym("noteout"), (t_newmethod)noteout_new, 0,
+ sizeof(t_noteout), 0, A_DEFFLOAT, 0);
+ class_addfloat(noteout_class, noteout_float);
+ class_sethelpsymbol(noteout_class, gensym("midi"));
+}
+
+
+/* -------------------------- ctlout -------------------------- */
+
+static t_class *ctlout_class;
+
+typedef struct _ctlout
+{
+ t_object x_obj;
+ t_float x_ctl;
+ t_float x_channel;
+} t_ctlout;
+
+static void *ctlout_new(t_floatarg ctl, t_floatarg channel)
+{
+ t_ctlout *x = (t_ctlout *)pd_new(ctlout_class);
+ x->x_ctl = ctl;
+ if (channel <= 0) channel = 1;
+ x->x_channel = channel;
+ floatinlet_new(&x->x_obj, &x->x_ctl);
+ floatinlet_new(&x->x_obj, &x->x_channel);
+ return (x);
+}
+
+static void ctlout_float(t_ctlout *x, t_float f)
+{
+ int binchan = x->x_channel - 1;
+ if (binchan < 0)
+ binchan = 0;
+ outmidi_controlchange((binchan >> 4),
+ (binchan & 15), (int)(x->x_ctl), (int)f);
+}
+
+static void ctlout_setup(void)
+{
+ ctlout_class = class_new(gensym("ctlout"), (t_newmethod)ctlout_new, 0,
+ sizeof(t_ctlout), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_addfloat(ctlout_class, ctlout_float);
+ class_sethelpsymbol(ctlout_class, gensym("midi"));
+}
+
+
+/* -------------------------- pgmout -------------------------- */
+
+static t_class *pgmout_class;
+
+typedef struct _pgmout
+{
+ t_object x_obj;
+ t_float x_channel;
+} t_pgmout;
+
+static void *pgmout_new(t_floatarg channel)
+{
+ t_pgmout *x = (t_pgmout *)pd_new(pgmout_class);
+ if (channel <= 0) channel = 1;
+ x->x_channel = channel;
+ floatinlet_new(&x->x_obj, &x->x_channel);
+ return (x);
+}
+
+static void pgmout_float(t_pgmout *x, t_floatarg f)
+{
+ int binchan = x->x_channel - 1;
+ int n = f - 1;
+ if (binchan < 0)
+ binchan = 0;
+ if (n < 0) n = 0;
+ else if (n > 127) n = 127;
+ outmidi_programchange((binchan >> 4),
+ (binchan & 15), n);
+}
+
+static void pgmout_setup(void)
+{
+ pgmout_class = class_new(gensym("pgmout"), (t_newmethod)pgmout_new, 0,
+ sizeof(t_pgmout), 0, A_DEFFLOAT, 0);
+ class_addfloat(pgmout_class, pgmout_float);
+ class_sethelpsymbol(pgmout_class, gensym("midi"));
+}
+
+
+/* -------------------------- bendout -------------------------- */
+
+static t_class *bendout_class;
+
+typedef struct _bendout
+{
+ t_object x_obj;
+ t_float x_channel;
+} t_bendout;
+
+static void *bendout_new(t_floatarg channel)
+{
+ t_bendout *x = (t_bendout *)pd_new(bendout_class);
+ if (channel <= 0) channel = 1;
+ x->x_channel = channel;
+ floatinlet_new(&x->x_obj, &x->x_channel);
+ return (x);
+}
+
+static void bendout_float(t_bendout *x, t_float f)
+{
+ int binchan = x->x_channel - 1;
+ int n = (int)f + 8192;
+ if (binchan < 0)
+ binchan = 0;
+ outmidi_pitchbend((binchan >> 4), (binchan & 15), n);
+}
+
+static void bendout_setup(void)
+{
+ bendout_class = class_new(gensym("bendout"), (t_newmethod)bendout_new, 0,
+ sizeof(t_bendout), 0, A_DEFFLOAT, 0);
+ class_addfloat(bendout_class, bendout_float);
+ class_sethelpsymbol(bendout_class, gensym("midi"));
+}
+
+/* -------------------------- touch -------------------------- */
+
+static t_class *touchout_class;
+
+typedef struct _touchout
+{
+ t_object x_obj;
+ t_float x_channel;
+} t_touchout;
+
+static void *touchout_new(t_floatarg channel)
+{
+ t_touchout *x = (t_touchout *)pd_new(touchout_class);
+ if (channel <= 0) channel = 1;
+ x->x_channel = channel;
+ floatinlet_new(&x->x_obj, &x->x_channel);
+ return (x);
+}
+
+static void touchout_float(t_touchout *x, t_float f)
+{
+ int binchan = x->x_channel - 1;
+ if (binchan < 0)
+ binchan = 0;
+ outmidi_aftertouch((binchan >> 4), (binchan & 15), (int)f);
+}
+
+static void touchout_setup(void)
+{
+ touchout_class = class_new(gensym("touchout"), (t_newmethod)touchout_new, 0,
+ sizeof(t_touchout), 0, A_DEFFLOAT, 0);
+ class_addfloat(touchout_class, touchout_float);
+ class_sethelpsymbol(touchout_class, gensym("midi"));
+}
+
+/* -------------------------- polytouch -------------------------- */
+
+static t_class *polytouchout_class;
+
+typedef struct _polytouchout
+{
+ t_object x_obj;
+ t_float x_channel;
+ t_float x_pitch;
+} t_polytouchout;
+
+static void *polytouchout_new(t_floatarg channel)
+{
+ t_polytouchout *x = (t_polytouchout *)pd_new(polytouchout_class);
+ if (channel <= 0) channel = 1;
+ x->x_channel = channel;
+ x->x_pitch = 0;
+ floatinlet_new(&x->x_obj, &x->x_pitch);
+ floatinlet_new(&x->x_obj, &x->x_channel);
+ return (x);
+}
+
+static void polytouchout_float(t_polytouchout *x, t_float n)
+{
+ int binchan = x->x_channel - 1;
+ if (binchan < 0)
+ binchan = 0;
+ outmidi_polyaftertouch((binchan >> 4), (binchan & 15), x->x_pitch, n);
+}
+
+static void polytouchout_setup(void)
+{
+ polytouchout_class = class_new(gensym("polytouchout"),
+ (t_newmethod)polytouchout_new, 0,
+ sizeof(t_polytouchout), 0, A_DEFFLOAT, 0);
+ class_addfloat(polytouchout_class, polytouchout_float);
+ class_sethelpsymbol(polytouchout_class, gensym("midi"));
+}
+
+/* -------------------------- makenote -------------------------- */
+
+static t_class *makenote_class;
+
+typedef struct _hang
+{
+ t_clock *h_clock;
+ struct _hang *h_next;
+ t_float h_pitch;
+ struct _makenote *h_owner;
+} t_hang;
+
+typedef struct _makenote
+{
+ t_object x_obj;
+ t_float x_velo;
+ t_float x_dur;
+ t_outlet *x_pitchout;
+ t_outlet *x_velout;
+ t_hang *x_hang;
+} t_makenote;
+
+static void *makenote_new(t_floatarg velo, t_floatarg dur)
+{
+ t_makenote *x = (t_makenote *)pd_new(makenote_class);
+ x->x_velo = velo;
+ x->x_dur = dur;
+ floatinlet_new(&x->x_obj, &x->x_velo);
+ floatinlet_new(&x->x_obj, &x->x_dur);
+ x->x_pitchout = outlet_new(&x->x_obj, &s_float);
+ x->x_velout = outlet_new(&x->x_obj, &s_float);
+ x->x_hang = 0;
+ return (x);
+}
+
+static void makenote_tick(t_hang *hang)
+{
+ t_makenote *x = hang->h_owner;
+ t_hang *h2, *h3;
+ outlet_float(x->x_velout, 0);
+ outlet_float(x->x_pitchout, hang->h_pitch);
+ if (x->x_hang == hang) x->x_hang = hang->h_next;
+ else for (h2 = x->x_hang; h3 = h2->h_next; h2 = h3)
+ {
+ if (h3 == hang)
+ {
+ h2->h_next = h3->h_next;
+ break;
+ }
+ }
+ clock_free(hang->h_clock);
+ freebytes(hang, sizeof(*hang));
+}
+
+static void makenote_float(t_makenote *x, t_float f)
+{
+ t_hang *hang;
+ if (!x->x_velo) return;
+ outlet_float(x->x_velout, x->x_velo);
+ outlet_float(x->x_pitchout, f);
+ hang = (t_hang *)getbytes(sizeof *hang);
+ hang->h_next = x->x_hang;
+ x->x_hang = hang;
+ hang->h_pitch = f;
+ hang->h_owner = x;
+ hang->h_clock = clock_new(hang, (t_method)makenote_tick);
+ clock_delay(hang->h_clock, (x->x_dur >= 0 ? x->x_dur : 0));
+}
+
+static void makenote_stop(t_makenote *x)
+{
+ t_hang *hang;
+ while (hang = x->x_hang)
+ {
+ outlet_float(x->x_velout, 0);
+ outlet_float(x->x_pitchout, hang->h_pitch);
+ x->x_hang = hang->h_next;
+ clock_free(hang->h_clock);
+ freebytes(hang, sizeof(*hang));
+ }
+}
+
+static void makenote_clear(t_makenote *x)
+{
+ t_hang *hang;
+ while (hang = x->x_hang)
+ {
+ x->x_hang = hang->h_next;
+ clock_free(hang->h_clock);
+ freebytes(hang, sizeof(*hang));
+ }
+}
+
+static void makenote_setup(void)
+{
+ makenote_class = class_new(gensym("makenote"),
+ (t_newmethod)makenote_new, (t_method)makenote_clear,
+ sizeof(t_makenote), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_addfloat(makenote_class, makenote_float);
+ class_addmethod(makenote_class, (t_method)makenote_stop, gensym("stop"),
+ 0);
+ class_addmethod(makenote_class, (t_method)makenote_clear, gensym("clear"),
+ 0);
+}
+
+/* -------------------------- stripnote -------------------------- */
+
+static t_class *stripnote_class;
+
+typedef struct _stripnote
+{
+ t_object x_obj;
+ t_float x_velo;
+ t_outlet *x_pitchout;
+ t_outlet *x_velout;
+} t_stripnote;
+
+static void *stripnote_new(void )
+{
+ t_stripnote *x = (t_stripnote *)pd_new(stripnote_class);
+ floatinlet_new(&x->x_obj, &x->x_velo);
+ x->x_pitchout = outlet_new(&x->x_obj, &s_float);
+ x->x_velout = outlet_new(&x->x_obj, &s_float);
+ return (x);
+}
+
+static void stripnote_float(t_stripnote *x, t_float f)
+{
+ t_hang *hang;
+ if (!x->x_velo) return;
+ outlet_float(x->x_velout, x->x_velo);
+ outlet_float(x->x_pitchout, f);
+}
+
+static void stripnote_setup(void)
+{
+ stripnote_class = class_new(gensym("stripnote"),
+ (t_newmethod)stripnote_new, 0, sizeof(t_stripnote), 0, 0);
+ class_addfloat(stripnote_class, stripnote_float);
+}
+
+/* -------------------------- poly -------------------------- */
+
+static t_class *poly_class;
+
+typedef struct voice
+{
+ float v_pitch;
+ int v_used;
+ unsigned long v_serial;
+} t_voice;
+
+typedef struct poly
+{
+ t_object x_obj;
+ int x_n;
+ t_voice *x_vec;
+ float x_vel;
+ t_outlet *x_pitchout;
+ t_outlet *x_velout;
+ unsigned long x_serial;
+ int x_steal;
+} t_poly;
+
+static void *poly_new(float fnvoice, float fsteal)
+{
+ int i, n = fnvoice;
+ t_poly *x = (t_poly *)pd_new(poly_class);
+ t_voice *v;
+ if (n < 1) n = 1;
+ x->x_n = n;
+ x->x_vec = (t_voice *)getbytes(n * sizeof(*x->x_vec));
+ for (v = x->x_vec, i = n; i--; v++)
+ v->v_pitch = v->v_used = v->v_serial = 0;
+ x->x_vel = 0;
+ x->x_steal = (fsteal != 0);
+ floatinlet_new(&x->x_obj, &x->x_vel);
+ outlet_new(&x->x_obj, &s_float);
+ x->x_pitchout = outlet_new(&x->x_obj, &s_float);
+ x->x_velout = outlet_new(&x->x_obj, &s_float);
+ x->x_serial = 0;
+ return (x);
+}
+
+static void poly_float(t_poly *x, t_float f)
+{
+ int i;
+ t_voice *v;
+ t_voice *firston, *firstoff;
+ unsigned int serialon, serialoff, onindex = 0, offindex = 0;
+ if (x->x_vel > 0)
+ {
+ /* note on. Look for a vacant voice */
+ for (v = x->x_vec, i = 0, firston = firstoff = 0,
+ serialon = serialoff = 0xffffffff; i < x->x_n; v++, i++)
+ {
+ if (v->v_used && v->v_serial < serialon)
+ firston = v, serialon = v->v_serial, onindex = i;
+ else if (!v->v_used && v->v_serial < serialoff)
+ firstoff = v, serialoff = v->v_serial, offindex = i;
+ }
+ if (firstoff)
+ {
+ outlet_float(x->x_velout, x->x_vel);
+ outlet_float(x->x_pitchout, firstoff->v_pitch = f);
+ outlet_float(x->x_obj.ob_outlet, offindex+1);
+ firstoff->v_used = 1;
+ firstoff->v_serial = x->x_serial++;
+ }
+ /* if none, steal one */
+ else if (firston && x->x_steal)
+ {
+ outlet_float(x->x_velout, 0);
+ outlet_float(x->x_pitchout, firston->v_pitch);
+ outlet_float(x->x_obj.ob_outlet, onindex+1);
+ outlet_float(x->x_velout, x->x_vel);
+ outlet_float(x->x_pitchout, firston->v_pitch = f);
+ outlet_float(x->x_obj.ob_outlet, onindex+1);
+ firston->v_serial = x->x_serial++;
+ }
+ }
+ else /* note off. Turn off oldest match */
+ {
+ for (v = x->x_vec, i = 0, firston = 0, serialon = 0xffffffff;
+ i < x->x_n; v++, i++)
+ if (v->v_used && v->v_pitch == f && v->v_serial < serialon)
+ firston = v, serialon = v->v_serial, onindex = i;
+ if (firston)
+ {
+ firston->v_used = 0;
+ firston->v_serial = x->x_serial++;
+ outlet_float(x->x_velout, 0);
+ outlet_float(x->x_pitchout, firston->v_pitch);
+ outlet_float(x->x_obj.ob_outlet, onindex+1);
+ }
+ }
+}
+
+static void poly_stop(t_poly *x)
+{
+ int i;
+ t_voice *v;
+ for (i = 0, v = x->x_vec; i < x->x_n; i++, v++)
+ if (v->v_used)
+ {
+ outlet_float(x->x_velout, 0L);
+ outlet_float(x->x_pitchout, v->v_pitch);
+ outlet_float(x->x_obj.ob_outlet, i+1);
+ v->v_used = 0;
+ v->v_serial = x->x_serial++;
+ }
+}
+
+static void poly_clear(t_poly *x)
+{
+ int i;
+ t_voice *v;
+ for (v = x->x_vec, i = x->x_n; i--; v++) v->v_used = v->v_serial = 0;
+}
+
+static void poly_free(t_poly *x)
+{
+ freebytes(x->x_vec, x->x_n * sizeof (*x->x_vec));
+}
+
+static void poly_setup(void)
+{
+ poly_class = class_new(gensym("poly"),
+ (t_newmethod)poly_new, (t_method)poly_clear,
+ sizeof(t_poly), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_addfloat(poly_class, poly_float);
+ class_addmethod(poly_class, (t_method)poly_stop, gensym("stop"), 0);
+ class_addmethod(poly_class, (t_method)poly_clear, gensym("clear"), 0);
+}
+
+/* -------------------------- bag -------------------------- */
+
+static t_class *bag_class;
+
+typedef struct _bagelem
+{
+ struct _bagelem *e_next;
+ t_float e_value;
+} t_bagelem;
+
+typedef struct _bag
+{
+ t_object x_obj;
+ t_float x_velo;
+ t_bagelem *x_first;
+} t_bag;
+
+static void *bag_new(void )
+{
+ t_bag *x = (t_bag *)pd_new(bag_class);
+ x->x_velo = 0;
+ floatinlet_new(&x->x_obj, &x->x_velo);
+ outlet_new(&x->x_obj, &s_float);
+ x->x_first = 0;
+ return (x);
+}
+
+static void bag_float(t_bag *x, t_float f)
+{
+ t_bagelem *bagelem, *e2, *e3;
+ if (x->x_velo != 0)
+ {
+ bagelem = (t_bagelem *)getbytes(sizeof *bagelem);
+ bagelem->e_next = 0;
+ bagelem->e_value = f;
+ if (!x->x_first) x->x_first = bagelem;
+ else /* LATER replace with a faster algorithm */
+ {
+ for (e2 = x->x_first; e3 = e2->e_next; e2 = e3)
+ ;
+ e2->e_next = bagelem;
+ }
+ }
+ else
+ {
+ if (!x->x_first) return;
+ if (x->x_first->e_value == f)
+ {
+ bagelem = x->x_first;
+ x->x_first = x->x_first->e_next;
+ freebytes(bagelem, sizeof(*bagelem));
+ return;
+ }
+ for (e2 = x->x_first; e3 = e2->e_next; e2 = e3)
+ if (e3->e_value == f)
+ {
+ e2->e_next = e3->e_next;
+ freebytes(e3, sizeof(*e3));
+ return;
+ }
+ }
+}
+
+static void bag_flush(t_bag *x)
+{
+ t_bagelem *bagelem;
+ while (bagelem = x->x_first)
+ {
+ outlet_float(x->x_obj.ob_outlet, bagelem->e_value);
+ x->x_first = bagelem->e_next;
+ freebytes(bagelem, sizeof(*bagelem));
+ }
+}
+
+static void bag_clear(t_bag *x)
+{
+ t_bagelem *bagelem;
+ while (bagelem = x->x_first)
+ {
+ x->x_first = bagelem->e_next;
+ freebytes(bagelem, sizeof(*bagelem));
+ }
+}
+
+static void bag_setup(void)
+{
+ bag_class = class_new(gensym("bag"),
+ (t_newmethod)bag_new, (t_method)bag_clear,
+ sizeof(t_bag), 0, 0);
+ class_addfloat(bag_class, bag_float);
+ class_addmethod(bag_class, (t_method)bag_flush, gensym("flush"), 0);
+ class_addmethod(bag_class, (t_method)bag_clear, gensym("clear"), 0);
+}
+
+void x_midi_setup(void)
+{
+ midiin_setup();
+ midirealtimein_setup();
+ notein_setup();
+ ctlin_setup();
+ pgmin_setup();
+ bendin_setup();
+ touchin_setup();
+ polytouchin_setup();
+ midiclkin_setup();
+ midiout_setup();
+ noteout_setup();
+ ctlout_setup();
+ pgmout_setup();
+ bendout_setup();
+ touchout_setup();
+ polytouchout_setup();
+ makenote_setup();
+ stripnote_setup();
+ poly_setup();
+ bag_setup();
+}
diff --git a/pd/src/x_misc.c b/pd/src/x_misc.c
new file mode 100644
index 00000000..8ba5191f
--- /dev/null
+++ b/pd/src/x_misc.c
@@ -0,0 +1,319 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* misc. */
+
+#include "m_imp.h"
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef UNIX
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/param.h>
+#endif
+#ifdef NT
+#include <wtypes.h>
+#include <time.h>
+#endif
+
+#if defined (MACOSX) || defined (__FreeBSD__)
+#define HZ CLK_TCK
+#endif
+
+/* -------------------------- random ------------------------------ */
+/* this is strictly homebrew and untested. */
+
+static t_class *random_class;
+
+typedef struct _random
+{
+ t_object x_obj;
+ t_float x_f;
+ unsigned int x_state;
+} t_random;
+
+
+static int makeseed(void)
+{
+ static unsigned int random_nextseed = 1489853723;
+ random_nextseed = random_nextseed * 435898247 + 938284287;
+ return (random_nextseed & 0x7fffffff);
+}
+
+static void *random_new(t_floatarg f)
+{
+ t_random *x = (t_random *)pd_new(random_class);
+ x->x_f = f;
+ x->x_state = makeseed();
+ floatinlet_new(&x->x_obj, &x->x_f);
+ outlet_new(&x->x_obj, &s_float);
+ return (x);
+}
+
+static void random_bang(t_random *x)
+{
+ int n = x->x_f, nval;
+ int range = (n < 1 ? 1 : n);
+ unsigned int randval = x->x_state;
+ x->x_state = randval = randval * 472940017 + 832416023;
+ nval = ((double)range) * ((double)randval)
+ * (1./4294967296.);
+ if (nval >= range) nval = range-1;
+ outlet_float(x->x_obj.ob_outlet, nval);
+}
+
+static void random_seed(t_random *x, float f, float glob)
+{
+ x->x_state = f;
+}
+
+static void random_setup(void)
+{
+ random_class = class_new(gensym("random"), (t_newmethod)random_new, 0,
+ sizeof(t_random), 0, A_DEFFLOAT, 0);
+ class_addbang(random_class, random_bang);
+ class_addmethod(random_class, (t_method)random_seed,
+ gensym("seed"), A_FLOAT, 0);
+}
+
+
+/* -------------------------- loadbang ------------------------------ */
+static t_class *loadbang_class;
+
+typedef struct _loadbang
+{
+ t_object x_obj;
+} t_loadbang;
+
+static void *loadbang_new(void)
+{
+ t_loadbang *x = (t_loadbang *)pd_new(loadbang_class);
+ outlet_new(&x->x_obj, &s_bang);
+ return (x);
+}
+
+static void loadbang_loadbang(t_loadbang *x)
+{
+ if (!sys_noloadbang)
+ outlet_bang(x->x_obj.ob_outlet);
+}
+
+static void loadbang_setup(void)
+{
+ loadbang_class = class_new(gensym("loadbang"), (t_newmethod)loadbang_new, 0,
+ sizeof(t_loadbang), 0, 0);
+ class_addmethod(loadbang_class, (t_method)loadbang_loadbang,
+ gensym("loadbang"), 0);
+}
+
+/* ------------- namecanvas (delete this later) --------------------- */
+static t_class *namecanvas_class;
+
+typedef struct _namecanvas
+{
+ t_object x_obj;
+ t_symbol *x_sym;
+ t_pd *x_owner;
+} t_namecanvas;
+
+static void *namecanvas_new(t_symbol *s)
+{
+ t_namecanvas *x = (t_namecanvas *)pd_new(namecanvas_class);
+ x->x_owner = (t_pd *)canvas_getcurrent();
+ x->x_sym = s;
+ if (*s->s_name) pd_bind(x->x_owner, s);
+ return (x);
+}
+
+static void namecanvas_free(t_namecanvas *x)
+{
+ if (*x->x_sym->s_name) pd_unbind(x->x_owner, x->x_sym);
+}
+
+static void namecanvas_setup(void)
+{
+ namecanvas_class = class_new(gensym("namecanvas"),
+ (t_newmethod)namecanvas_new, (t_method)namecanvas_free,
+ sizeof(t_namecanvas), CLASS_NOINLET, A_DEFSYM, 0);
+}
+
+/* ---------------serial ports (NT only -- hack) ------------------------- */
+#define MAXSERIAL 100
+
+static t_class *serial_class;
+
+typedef struct _serial
+{
+ t_object x_obj;
+ int x_portno;
+ int x_open;
+} t_serial;
+
+static void serial_float(t_serial *x, t_float f)
+{
+ int n = f;
+ char message[MAXSERIAL * 4 + 100];
+ if (!x->x_open)
+ {
+ sys_vgui("com%d_open\n", x->x_portno);
+ x->x_open = 1;
+ }
+ sprintf(message, "com%d_send \"\\%3.3o\"\n", x->x_portno, n);
+ sys_gui(message);
+}
+
+static void *serial_new(t_floatarg fportno)
+{
+ int portno = fportno;
+ t_serial *x = (t_serial *)pd_new(serial_class);
+ if (!portno) portno = 1;
+ x->x_portno = portno;
+ x->x_open = 0;
+ return (x);
+}
+
+static void serial_setup(void)
+{
+ serial_class = class_new(gensym("serial"), (t_newmethod)serial_new, 0,
+ sizeof(t_serial), 0, A_DEFFLOAT, 0);
+ class_addfloat(serial_class, serial_float);
+}
+
+/* -------------------------- cputime ------------------------------ */
+
+static t_class *cputime_class;
+
+typedef struct _cputime
+{
+ t_object x_obj;
+#ifdef UNIX
+ struct tms x_setcputime;
+#endif
+#ifdef NT
+ LARGE_INTEGER x_kerneltime;
+ LARGE_INTEGER x_usertime;
+ int x_warned;
+#endif
+} t_cputime;
+
+static void cputime_bang(t_cputime *x)
+{
+#ifdef UNIX
+ times(&x->x_setcputime);
+#endif
+#ifdef NT
+ FILETIME ignorethis, ignorethat;
+ BOOL retval;
+ retval = GetProcessTimes(GetCurrentProcess(), &ignorethis, &ignorethat,
+ (FILETIME *)&x->x_kerneltime, (FILETIME *)&x->x_usertime);
+ if (!retval)
+ {
+ if (!x->x_warned)
+ post("cputime is apparently not supported on your platform");
+ x->x_warned = 1;
+ x->x_kerneltime.QuadPart = 0;
+ x->x_usertime.QuadPart = 0;
+ }
+#endif
+}
+
+static void cputime_bang2(t_cputime *x)
+{
+#ifdef UNIX
+ float elapsedcpu;
+ struct tms newcputime;
+ times(&newcputime);
+ elapsedcpu = 1000 * (
+ newcputime.tms_utime + newcputime.tms_stime -
+ x->x_setcputime.tms_utime - x->x_setcputime.tms_stime) / HZ;
+ outlet_float(x->x_obj.ob_outlet, elapsedcpu);
+#endif
+#ifdef NT
+ float elapsedcpu;
+ FILETIME ignorethis, ignorethat;
+ LARGE_INTEGER usertime, kerneltime;
+ BOOL retval;
+
+ retval = GetProcessTimes(GetCurrentProcess(), &ignorethis, &ignorethat,
+ (FILETIME *)&kerneltime, (FILETIME *)&usertime);
+ if (retval)
+ elapsedcpu = 0.0001 *
+ ((kerneltime.QuadPart - x->x_kerneltime.QuadPart) +
+ (usertime.QuadPart - x->x_usertime.QuadPart));
+ else elapsedcpu = 0;
+ outlet_float(x->x_obj.ob_outlet, elapsedcpu);
+#endif
+}
+
+static void *cputime_new(void)
+{
+ t_cputime *x = (t_cputime *)pd_new(cputime_class);
+ outlet_new(&x->x_obj, gensym("float"));
+
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("bang"), gensym("bang2"));
+#ifdef NT
+ x->x_warned = 0;
+#endif
+ cputime_bang(x);
+ return (x);
+}
+
+static void cputime_setup(void)
+{
+ cputime_class = class_new(gensym("cputime"), (t_newmethod)cputime_new, 0,
+ sizeof(t_cputime), 0, 0);
+ class_addbang(cputime_class, cputime_bang);
+ class_addmethod(cputime_class, (t_method)cputime_bang2, gensym("bang2"), 0);
+}
+
+/* -------------------------- realtime ------------------------------ */
+
+static t_class *realtime_class;
+
+typedef struct _realtime
+{
+ t_object x_obj;
+ double x_setrealtime;
+} t_realtime;
+
+static void realtime_bang(t_realtime *x)
+{
+ x->x_setrealtime = sys_getrealtime();
+}
+
+static void realtime_bang2(t_realtime *x)
+{
+ outlet_float(x->x_obj.ob_outlet,
+ (sys_getrealtime() - x->x_setrealtime) * 1000.);
+}
+
+static void *realtime_new(void)
+{
+ t_realtime *x = (t_realtime *)pd_new(realtime_class);
+ outlet_new(&x->x_obj, gensym("float"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("bang"), gensym("bang2"));
+ realtime_bang(x);
+ return (x);
+}
+
+static void realtime_setup(void)
+{
+ realtime_class = class_new(gensym("realtime"), (t_newmethod)realtime_new, 0,
+ sizeof(t_realtime), 0, 0);
+ class_addbang(realtime_class, realtime_bang);
+ class_addmethod(realtime_class, (t_method)realtime_bang2, gensym("bang2"),
+ 0);
+}
+
+void x_misc_setup(void)
+{
+ random_setup();
+ loadbang_setup();
+ namecanvas_setup();
+ serial_setup();
+ cputime_setup();
+ realtime_setup();
+}
diff --git a/pd/src/x_net.c b/pd/src/x_net.c
new file mode 100644
index 00000000..d14538e7
--- /dev/null
+++ b/pd/src/x_net.c
@@ -0,0 +1,362 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* network */
+
+#include "m_imp.h"
+
+#include <sys/types.h>
+#include <string.h>
+#ifdef UNIX
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <stdio.h>
+#define SOCKET_ERROR -1
+#else
+#include <winsock.h>
+#endif
+
+static t_class *netsend_class;
+
+typedef struct _netsend
+{
+ t_object x_obj;
+ int x_fd;
+ int x_protocol;
+} t_netsend;
+
+static void *netsend_new(t_floatarg udpflag)
+{
+ t_netsend *x = (t_netsend *)pd_new(netsend_class);
+ outlet_new(&x->x_obj, &s_float);
+ x->x_fd = -1;
+ x->x_protocol = (udpflag != 0 ? SOCK_DGRAM : SOCK_STREAM);
+ return (x);
+}
+
+static void netsend_connect(t_netsend *x, t_symbol *hostname,
+ t_floatarg fportno)
+{
+ struct sockaddr_in server;
+ struct hostent *hp;
+ int sockfd;
+ int portno = fportno;
+ int intarg;
+ if (x->x_fd >= 0)
+ {
+ error("netsend_connect: already connected");
+ return;
+ }
+
+ /* create a socket */
+ sockfd = socket(AF_INET, x->x_protocol, 0);
+#if 0
+ fprintf(stderr, "send socket %d\n", sockfd);
+#endif
+ if (sockfd < 0)
+ {
+ sys_sockerror("socket");
+ return;
+ }
+ /* connect socket using hostname provided in command line */
+ server.sin_family = AF_INET;
+ hp = gethostbyname(hostname->s_name);
+ if (hp == 0)
+ {
+ post("bad host?\n");
+ return;
+ }
+#if 0
+ intarg = 0;
+ if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF,
+ &intarg, sizeof(intarg)) < 0)
+ post("setsockopt (SO_RCVBUF) failed\n");
+#endif
+ /* for stream (TCP) sockets, specify "nodelay" */
+ if (x->x_protocol == SOCK_STREAM)
+ {
+ intarg = 1;
+ if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
+ &intarg, sizeof(intarg)) < 0)
+ post("setsockopt (TCP_NODELAY) failed\n");
+ }
+ memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+
+ /* assign client port number */
+ server.sin_port = htons((u_short)portno);
+
+ post("connecting to port %d", portno);
+ /* try to connect. LATER make a separate thread to do this
+ because it might block */
+ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0)
+ {
+ sys_sockerror("connecting stream socket");
+ sys_closesocket(sockfd);
+ return;
+ }
+ x->x_fd = sockfd;
+ outlet_float(x->x_obj.ob_outlet, 1);
+}
+
+static void netsend_disconnect(t_netsend *x)
+{
+ if (x->x_fd >= 0)
+ {
+ sys_closesocket(x->x_fd);
+ x->x_fd = -1;
+ outlet_float(x->x_obj.ob_outlet, 0);
+ }
+}
+
+static void netsend_send(t_netsend *x, t_symbol *s, int argc, t_atom *argv)
+{
+ if (x->x_fd >= 0)
+ {
+ t_binbuf *b = binbuf_new();
+ char *buf, *bp;
+ int length, sent;
+ t_atom at;
+ binbuf_add(b, argc, argv);
+ SETSEMI(&at);
+ binbuf_add(b, 1, &at);
+ binbuf_gettext(b, &buf, &length);
+ for (bp = buf, sent = 0; sent < length;)
+ {
+ static double lastwarntime;
+ static double pleasewarn;
+ double timebefore = sys_getrealtime();
+ int res = send(x->x_fd, buf, length-sent, 0);
+ double timeafter = sys_getrealtime();
+ int late = (timeafter - timebefore > 0.005);
+ if (late || pleasewarn)
+ {
+ if (timeafter > lastwarntime + 2)
+ {
+ post("netsend blocked %d msec",
+ (int)(1000 * ((timeafter - timebefore) + pleasewarn)));
+ pleasewarn = 0;
+ lastwarntime = timeafter;
+ }
+ else if (late) pleasewarn += timeafter - timebefore;
+ }
+ if (res <= 0)
+ {
+ sys_sockerror("netsend");
+ netsend_disconnect(x);
+ break;
+ }
+ else
+ {
+ sent += res;
+ bp += res;
+ }
+ }
+ t_freebytes(buf, length);
+ binbuf_free(b);
+ }
+ else error("netsend: not connected");
+}
+
+static void netsend_free(t_netsend *x)
+{
+ netsend_disconnect(x);
+}
+
+static void netsend_setup(void)
+{
+ netsend_class = class_new(gensym("netsend"), (t_newmethod)netsend_new,
+ (t_method)netsend_free,
+ sizeof(t_netsend), 0, A_DEFFLOAT, 0);
+ class_addmethod(netsend_class, (t_method)netsend_connect,
+ gensym("connect"), A_SYMBOL, A_FLOAT, 0);
+ class_addmethod(netsend_class, (t_method)netsend_disconnect,
+ gensym("disconnect"), 0);
+ class_addmethod(netsend_class, (t_method)netsend_send, gensym("send"),
+ A_GIMME, 0);
+}
+
+/* ----------------------------- netreceive ------------------------- */
+
+static t_class *netreceive_class;
+
+typedef struct _netreceive
+{
+ t_object x_obj;
+ t_outlet *x_msgout;
+ t_outlet *x_connectout;
+ int x_connectsocket;
+ int x_nconnections;
+ int x_udp;
+} t_netreceive;
+
+static void netreceive_notify(t_netreceive *x)
+{
+ outlet_float(x->x_connectout, --x->x_nconnections);
+}
+
+static void netreceive_doit(void *z, t_binbuf *b)
+{
+ t_atom messbuf[1024];
+ t_netreceive *x = (t_netreceive *)z;
+ int msg, natom = binbuf_getnatom(b);
+ t_atom *at = binbuf_getvec(b);
+ for (msg = 0; msg < natom;)
+ {
+ int emsg;
+ for (emsg = msg; emsg < natom && at[emsg].a_type != A_COMMA
+ && at[emsg].a_type != A_SEMI; emsg++)
+ ;
+ if (emsg > msg)
+ {
+ int i;
+ for (i = msg; i < emsg; i++)
+ if (at[i].a_type == A_DOLLAR || at[i].a_type == A_DOLLSYM)
+ {
+ pd_error(x, "netreceive: got dollar sign in message");
+ goto nodice;
+ }
+ if (at[msg].a_type == A_FLOAT)
+ {
+ if (emsg > msg + 1)
+ outlet_list(x->x_msgout, 0, emsg-msg, at + msg);
+ else outlet_float(x->x_msgout, at[msg].a_w.w_float);
+ }
+ else if (at[msg].a_type == A_SYMBOL)
+ outlet_anything(x->x_msgout, at[msg].a_w.w_symbol,
+ emsg-msg-1, at + msg + 1);
+ }
+ nodice:
+ msg = emsg + 1;
+ }
+}
+
+static void netreceive_connectpoll(t_netreceive *x)
+{
+ int fd = accept(x->x_connectsocket, 0, 0);
+ if (fd < 0) post("netreceive: accept failed");
+ else
+ {
+ t_socketreceiver *y = socketreceiver_new((void *)x,
+ (t_socketnotifier)netreceive_notify,
+ (x->x_msgout ? netreceive_doit : 0), 0);
+ sys_addpollfn(fd, (t_fdpollfn)socketreceiver_read, y);
+ outlet_float(x->x_connectout, ++x->x_nconnections);
+ }
+}
+
+static void *netreceive_new(t_symbol *compatflag,
+ t_floatarg fportno, t_floatarg udpflag)
+{
+ t_netreceive *x;
+ struct sockaddr_in server;
+ int sockfd, portno = fportno, udp = (udpflag != 0);
+ int old = !strcmp(compatflag->s_name , "old");
+ int intarg;
+ /* create a socket */
+ sockfd = socket(AF_INET, (udp ? SOCK_DGRAM : SOCK_STREAM), 0);
+#if 0
+ fprintf(stderr, "receive socket %d\n", sockfd);
+#endif
+ if (sockfd < 0)
+ {
+ sys_sockerror("socket");
+ return (0);
+ }
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_ANY;
+
+#if 1
+ /* ask OS to allow another Pd to repoen this port after we close it. */
+ intarg = 1;
+ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
+ &intarg, sizeof(intarg)) < 0)
+ post("setsockopt (SO_REUSEADDR) failed\n");
+#endif
+#if 0
+ intarg = 0;
+ if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
+ &intarg, sizeof(intarg)) < 0)
+ post("setsockopt (SO_RCVBUF) failed\n");
+#endif
+ /* Stream (TCP) sockets are set NODELAY */
+ if (!udp)
+ {
+ intarg = 1;
+ if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
+ &intarg, sizeof(intarg)) < 0)
+ post("setsockopt (TCP_NODELAY) failed\n");
+ }
+ /* assign server port number */
+ server.sin_port = htons((u_short)portno);
+
+ /* name the socket */
+ if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
+ {
+ sys_sockerror("bind");
+ sys_closesocket(sockfd);
+ return (0);
+ }
+ x = (t_netreceive *)pd_new(netreceive_class);
+ if (old)
+ {
+ /* old style, nonsecure version */
+ x->x_msgout = 0;
+ }
+ else x->x_msgout = outlet_new(&x->x_obj, &s_anything);
+
+ if (udp) /* datagram protocol */
+ {
+ t_socketreceiver *y = socketreceiver_new((void *)x,
+ (t_socketnotifier)netreceive_notify,
+ (x->x_msgout ? netreceive_doit : 0), 1);
+ sys_addpollfn(sockfd, (t_fdpollfn)socketreceiver_read, y);
+ x->x_connectout = 0;
+ }
+ else /* streaming protocol */
+ {
+ if (listen(sockfd, 5) < 0)
+ {
+ sys_sockerror("listen");
+ sys_closesocket(sockfd);
+ sockfd = -1;
+ }
+ else
+ {
+ sys_addpollfn(sockfd, (t_fdpollfn)netreceive_connectpoll, x);
+ x->x_connectout = outlet_new(&x->x_obj, &s_float);
+ }
+ }
+ x->x_connectsocket = sockfd;
+ x->x_nconnections = 0;
+ x->x_udp = udp;
+
+ return (x);
+}
+
+static void netreceive_free(t_netreceive *x)
+{
+ /* LATER make me clean up open connections */
+ if (x->x_connectsocket >= 0)
+ {
+ sys_rmpollfn(x->x_connectsocket);
+ sys_closesocket(x->x_connectsocket);
+ }
+}
+
+static void netreceive_setup(void)
+{
+ netreceive_class = class_new(gensym("netreceive"),
+ (t_newmethod)netreceive_new, (t_method)netreceive_free,
+ sizeof(t_netreceive), CLASS_NOINLET, A_DEFFLOAT, A_DEFFLOAT,
+ A_DEFSYM, 0);
+}
+
+void x_net_setup(void)
+{
+ netsend_setup();
+ netreceive_setup();
+}
+
diff --git a/pd/src/x_qlist.c b/pd/src/x_qlist.c
new file mode 100644
index 00000000..922cfe12
--- /dev/null
+++ b/pd/src/x_qlist.c
@@ -0,0 +1,345 @@
+/* Copyright (c) 1997-1999 Miller Puckette and others.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include "m_pd.h"
+#include <string.h>
+#ifdef UNIX
+#include <unistd.h>
+#endif
+#ifdef NT
+#include <io.h>
+#endif
+
+typedef struct _qlist
+{
+ t_object x_ob;
+ t_outlet *x_bangout;
+ void *x_binbuf;
+ int x_onset; /* playback position */
+ t_clock *x_clock;
+ float x_tempo;
+ double x_whenclockset;
+ float x_clockdelay;
+ t_symbol *x_dir;
+ t_canvas *x_canvas;
+ int x_reentered;
+} t_qlist;
+
+static void qlist_tick(t_qlist *x);
+
+static t_class *qlist_class;
+
+static void *qlist_new( void)
+{
+ t_symbol *name, *filename = 0;
+ t_qlist *x = (t_qlist *)pd_new(qlist_class);
+ x->x_binbuf = binbuf_new();
+ x->x_clock = clock_new(x, (t_method)qlist_tick);
+ outlet_new(&x->x_ob, &s_list);
+ x->x_bangout = outlet_new(&x->x_ob, &s_bang);
+ x->x_onset = 0x7fffffff;
+ x->x_tempo = 1;
+ x->x_whenclockset = 0;
+ x->x_clockdelay = 0;
+ x->x_canvas = canvas_getcurrent();
+ x->x_reentered = 0;
+ return (x);
+}
+
+static void qlist_rewind(t_qlist *x)
+{
+ x->x_onset = 0;
+ if (x->x_clock) clock_unset(x->x_clock);
+ x->x_whenclockset = 0;
+ x->x_reentered = 1;
+}
+
+static void qlist_donext(t_qlist *x, int drop, int automatic)
+{
+ t_pd *target = 0;
+ while (1)
+ {
+ int argc = binbuf_getnatom(x->x_binbuf),
+ count, onset = x->x_onset, onset2, wasreentered;
+ t_atom *argv = binbuf_getvec(x->x_binbuf);
+ t_atom *ap = argv + onset, *ap2;
+ if (onset >= argc) goto end;
+ while (ap->a_type == A_SEMI || ap->a_type == A_COMMA)
+ {
+ if (ap->a_type == A_SEMI) target = 0;
+ onset++, ap++;
+ if (onset >= argc) goto end;
+ }
+
+ if (!target && ap->a_type == A_FLOAT)
+ {
+ ap2 = ap + 1;
+ onset2 = onset + 1;
+ while (onset2 < argc && ap2->a_type == A_FLOAT)
+ onset2++, ap2++;
+ x->x_onset = onset2;
+ if (automatic)
+ {
+ clock_delay(x->x_clock,
+ x->x_clockdelay = ap->a_w.w_float * x->x_tempo);
+ x->x_whenclockset = clock_getsystime();
+ }
+ else outlet_list(x->x_ob.ob_outlet, 0, onset2-onset, ap);
+ return;
+ }
+ ap2 = ap + 1;
+ onset2 = onset + 1;
+ while (onset2 < argc &&
+ (ap2->a_type == A_FLOAT || ap2->a_type == A_SYMBOL))
+ onset2++, ap2++;
+ x->x_onset = onset2;
+ count = onset2 - onset;
+ if (!target)
+ {
+ if (ap->a_type != A_SYMBOL) continue;
+ else if (!(target = ap->a_w.w_symbol->s_thing))
+ {
+ error("qlist: %s: no such object", ap->a_w.w_symbol->s_name);
+ continue;
+ }
+ ap++;
+ onset++;
+ count--;
+ if (!count)
+ {
+ x->x_onset = onset2;
+ continue;
+ }
+ }
+ wasreentered = x->x_reentered;
+ x->x_reentered = 0;
+ if (!drop)
+ {
+ if (ap->a_type == A_FLOAT)
+ typedmess(target, &s_list, count, ap);
+ else if (ap->a_type == A_SYMBOL)
+ typedmess(target, ap->a_w.w_symbol, count-1, ap+1);
+ }
+ if (x->x_reentered)
+ return;
+ x->x_reentered = wasreentered;
+ } /* while (1); never falls through */
+
+end:
+ x->x_onset = 0x7fffffff;
+ outlet_bang(x->x_bangout);
+ x->x_whenclockset = 0;
+}
+
+static void qlist_next(t_qlist *x, t_floatarg drop)
+{
+ qlist_donext(x, drop != 0, 0);
+}
+
+static void qlist_bang(t_qlist *x)
+{
+ qlist_rewind(x);
+ qlist_donext(x, 0, 1);
+}
+
+static void qlist_tick(t_qlist *x)
+{
+ x->x_whenclockset = 0;
+ qlist_donext(x, 0, 1);
+}
+
+static void qlist_add(t_qlist *x, t_symbol *s, int ac, t_atom *av)
+{
+ t_atom a;
+ SETSEMI(&a);
+ binbuf_add(x->x_binbuf, ac, av);
+ binbuf_add(x->x_binbuf, 1, &a);
+}
+
+static void qlist_add2(t_qlist *x, t_symbol *s, int ac, t_atom *av)
+{
+ binbuf_add(x->x_binbuf, ac, av);
+}
+
+static void qlist_clear(t_qlist *x)
+{
+ qlist_rewind(x);
+ binbuf_clear(x->x_binbuf);
+}
+
+static void qlist_set(t_qlist *x, t_symbol *s, int ac, t_atom *av)
+{
+ qlist_clear(x);
+ qlist_add(x, s, ac, av);
+}
+
+static void qlist_read(t_qlist *x, t_symbol *filename, t_symbol *format)
+{
+ int cr = 0;
+ if (!strcmp(format->s_name, "cr"))
+ cr = 1;
+ else if (*format->s_name)
+ error("qlist_read: unknown flag: %s", format->s_name);
+
+ if (binbuf_read_via_path(x->x_binbuf, filename->s_name,
+ canvas_getdir(x->x_canvas)->s_name, cr))
+ error("%s: read failed", filename->s_name);
+ x->x_onset = 0x7fffffff;
+ x->x_reentered = 1;
+}
+
+static void qlist_write(t_qlist *x, t_symbol *filename, t_symbol *format)
+{
+ int cr = 0;
+ char buf[MAXPDSTRING];
+ canvas_makefilename(x->x_canvas, filename->s_name,
+ buf, MAXPDSTRING);
+ if (!strcmp(format->s_name, "cr"))
+ cr = 1;
+ else if (*format->s_name)
+ error("qlist_read: unknown flag: %s", format->s_name);
+ if (binbuf_write(x->x_binbuf, buf, "", cr))
+ error("%s: write failed", filename->s_name);
+}
+
+static void qlist_print(t_qlist *x)
+{
+ post("--------- textfile or qlist contents: -----------");
+ binbuf_print(x->x_binbuf);
+}
+
+static void qlist_tempo(t_qlist *x, t_float f)
+{
+ float newtempo;
+ if (f < 1e-20) f = 1e-20;
+ else if (f > 1e20) f = 1e20;
+ newtempo = 1./f;
+ if (x->x_whenclockset != 0)
+ {
+ float elapsed = clock_gettimesince(x->x_whenclockset);
+ float left = x->x_clockdelay - elapsed;
+ if (left < 0) left = 0;
+ left *= newtempo / x->x_tempo;
+ clock_delay(x->x_clock, left);
+ }
+ x->x_tempo = newtempo;
+}
+
+static void qlist_free(t_qlist *x)
+{
+ binbuf_free(x->x_binbuf);
+ if (x->x_clock) clock_free(x->x_clock);
+}
+
+/* -------------------- textfile ------------------------------- */
+
+static t_class *textfile_class;
+typedef t_qlist t_textfile;
+
+static void *textfile_new( void)
+{
+ t_symbol *name, *filename = 0;
+ t_textfile *x = (t_textfile *)pd_new(textfile_class);
+ x->x_binbuf = binbuf_new();
+ outlet_new(&x->x_ob, &s_list);
+ x->x_bangout = outlet_new(&x->x_ob, &s_bang);
+ x->x_onset = 0x7fffffff;
+ x->x_reentered = 0;
+ x->x_tempo = 1;
+ x->x_whenclockset = 0;
+ x->x_clockdelay = 0;
+ x->x_clock = NULL;
+ x->x_canvas = canvas_getcurrent();
+ return (x);
+}
+
+static void textfile_bang(t_textfile *x)
+{
+ int argc = binbuf_getnatom(x->x_binbuf),
+ count, onset = x->x_onset, onset2;
+ t_atom *argv = binbuf_getvec(x->x_binbuf);
+ t_atom *ap = argv + onset, *ap2;
+ while (onset < argc &&
+ (ap->a_type == A_SEMI || ap->a_type == A_COMMA))
+ onset++, ap++;
+ onset2 = onset;
+ ap2 = ap;
+ while (onset2 < argc &&
+ (ap2->a_type != A_SEMI && ap2->a_type != A_COMMA))
+ onset2++, ap2++;
+ if (onset2 > onset)
+ {
+ x->x_onset = onset2;
+ if (ap->a_type == A_SYMBOL)
+ outlet_anything(x->x_ob.ob_outlet, ap->a_w.w_symbol,
+ onset2-onset-1, ap+1);
+ else outlet_list(x->x_ob.ob_outlet, 0, onset2-onset, ap);
+ }
+ else
+ {
+ x->x_onset = 0x7fffffff;
+ outlet_bang(x->x_bangout);
+ }
+}
+
+static void textfile_rewind(t_qlist *x)
+{
+ x->x_onset = 0;
+}
+
+static void textfile_free(t_textfile *x)
+{
+ binbuf_free(x->x_binbuf);
+}
+
+/* ---------------- global setup function -------------------- */
+
+void x_qlist_setup(void )
+{
+ qlist_class = class_new(gensym("qlist"), (t_newmethod)qlist_new,
+ (t_method)qlist_free, sizeof(t_qlist), 0, 0);
+ class_addmethod(qlist_class, (t_method)qlist_rewind, gensym("rewind"), 0);
+ class_addmethod(qlist_class, (t_method)qlist_next,
+ gensym("next"), A_DEFFLOAT, 0);
+ class_addmethod(qlist_class, (t_method)qlist_set, gensym("set"),
+ A_GIMME, 0);
+ class_addmethod(qlist_class, (t_method)qlist_clear, gensym("clear"), 0);
+ class_addmethod(qlist_class, (t_method)qlist_add, gensym("add"),
+ A_GIMME, 0);
+ class_addmethod(qlist_class, (t_method)qlist_add2, gensym("add2"),
+ A_GIMME, 0);
+ class_addmethod(qlist_class, (t_method)qlist_add, gensym("append"),
+ A_GIMME, 0);
+ class_addmethod(qlist_class, (t_method)qlist_read, gensym("read"),
+ A_SYMBOL, A_DEFSYM, 0);
+ class_addmethod(qlist_class, (t_method)qlist_write, gensym("write"),
+ A_SYMBOL, A_DEFSYM, 0);
+ class_addmethod(qlist_class, (t_method)qlist_print, gensym("print"),
+ A_DEFSYM, 0);
+ class_addmethod(qlist_class, (t_method)qlist_tempo,
+ gensym("tempo"), A_FLOAT, 0);
+ class_addbang(qlist_class, qlist_bang);
+
+ textfile_class = class_new(gensym("textfile"), (t_newmethod)textfile_new,
+ (t_method)textfile_free, sizeof(t_textfile), 0, 0);
+ class_addmethod(textfile_class, (t_method)textfile_rewind, gensym("rewind"),
+ 0);
+ class_addmethod(textfile_class, (t_method)qlist_set, gensym("set"),
+ A_GIMME, 0);
+ class_addmethod(textfile_class, (t_method)qlist_clear, gensym("clear"), 0);
+ class_addmethod(textfile_class, (t_method)qlist_add, gensym("add"),
+ A_GIMME, 0);
+ class_addmethod(textfile_class, (t_method)qlist_add2, gensym("add2"),
+ A_GIMME, 0);
+ class_addmethod(textfile_class, (t_method)qlist_add, gensym("append"),
+ A_GIMME, 0);
+ class_addmethod(textfile_class, (t_method)qlist_read, gensym("read"),
+ A_SYMBOL, A_DEFSYM, 0);
+ class_addmethod(textfile_class, (t_method)qlist_write, gensym("write"),
+ A_SYMBOL, A_DEFSYM, 0);
+ class_addmethod(textfile_class, (t_method)qlist_print, gensym("print"),
+ A_DEFSYM, 0);
+ class_addbang(textfile_class, textfile_bang);
+}
+
diff --git a/pd/src/x_time.c b/pd/src/x_time.c
new file mode 100644
index 00000000..2ea17fd3
--- /dev/null
+++ b/pd/src/x_time.c
@@ -0,0 +1,519 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* clock objects */
+
+#include "m_pd.h"
+#include <stdio.h>
+/* -------------------------- delay ------------------------------ */
+static t_class *delay_class;
+
+typedef struct _delay
+{
+ t_object x_obj;
+ t_clock *x_clock;
+ double x_deltime;
+} t_delay;
+
+static void delay_bang(t_delay *x)
+{
+ clock_delay(x->x_clock, x->x_deltime);
+}
+
+static void delay_stop(t_delay *x)
+{
+ clock_unset(x->x_clock);
+}
+
+static void delay_ft1(t_delay *x, t_floatarg g)
+{
+ if (g < 0) g = 0;
+ x->x_deltime = g;
+}
+
+static void delay_float(t_delay *x, t_float f)
+{
+ delay_ft1(x, f);
+ delay_bang(x);
+}
+
+static void delay_tick(t_delay *x)
+{
+ outlet_bang(x->x_obj.ob_outlet);
+}
+
+static void delay_free(t_delay *x)
+{
+ clock_free(x->x_clock);
+}
+
+static void *delay_new(t_floatarg f)
+{
+ t_delay *x = (t_delay *)pd_new(delay_class);
+ delay_ft1(x, f);
+ x->x_clock = clock_new(x, (t_method)delay_tick);
+ outlet_new(&x->x_obj, gensym("bang"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1"));
+ return (x);
+}
+
+static void delay_setup(void)
+{
+ delay_class = class_new(gensym("delay"), (t_newmethod)delay_new,
+ (t_method)delay_free, sizeof(t_delay), 0, A_DEFFLOAT, 0);
+ class_addcreator((t_newmethod)delay_new, gensym("del"), A_DEFFLOAT, 0);
+ class_addbang(delay_class, delay_bang);
+ class_addmethod(delay_class, (t_method)delay_stop, gensym("stop"), 0);
+ class_addmethod(delay_class, (t_method)delay_ft1,
+ gensym("ft1"), A_FLOAT, 0);
+ class_addfloat(delay_class, (t_method)delay_float);
+}
+
+/* -------------------------- metro ------------------------------ */
+static t_class *metro_class;
+
+typedef struct _metro
+{
+ t_object x_obj;
+ t_clock *x_clock;
+ double x_deltime;
+ int x_hit;
+} t_metro;
+
+static void metro_tick(t_metro *x)
+{
+ x->x_hit = 0;
+ outlet_bang(x->x_obj.ob_outlet);
+ if (!x->x_hit) clock_delay(x->x_clock, x->x_deltime);
+}
+
+static void metro_float(t_metro *x, t_float f)
+{
+ if (f != 0) metro_tick(x);
+ else clock_unset(x->x_clock);
+ x->x_hit = 1;
+}
+
+static void metro_bang(t_metro *x)
+{
+ metro_float(x, 1);
+}
+
+static void metro_stop(t_metro *x)
+{
+ metro_float(x, 0);
+}
+
+static void metro_ft1(t_metro *x, t_floatarg g)
+{
+ if (g < 1) g = 1;
+ x->x_deltime = g;
+}
+
+static void metro_free(t_metro *x)
+{
+ clock_free(x->x_clock);
+}
+
+static void *metro_new(t_floatarg f)
+{
+ t_metro *x = (t_metro *)pd_new(metro_class);
+ metro_ft1(x, f);
+ x->x_hit = 0;
+ x->x_clock = clock_new(x, (t_method)metro_tick);
+ outlet_new(&x->x_obj, gensym("bang"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1"));
+ return (x);
+}
+
+static void metro_setup(void)
+{
+ metro_class = class_new(gensym("metro"), (t_newmethod)metro_new,
+ (t_method)metro_free, sizeof(t_metro), 0, A_DEFFLOAT, 0);
+ class_addbang(metro_class, metro_bang);
+ class_addmethod(metro_class, (t_method)metro_stop, gensym("stop"), 0);
+ class_addmethod(metro_class, (t_method)metro_ft1, gensym("ft1"),
+ A_FLOAT, 0);
+ class_addfloat(metro_class, (t_method)metro_float);
+}
+
+/* -------------------------- line ------------------------------ */
+static t_class *line_class;
+
+typedef struct _line
+{
+ t_object x_obj;
+ t_clock *x_clock;
+ double x_targettime;
+ t_float x_targetval;
+ double x_prevtime;
+ t_float x_setval;
+ int x_gotinlet;
+ t_float x_grain;
+ double x_1overtimediff;
+ double x_in1val;
+} t_line;
+
+static void line_tick(t_line *x)
+{
+ double timenow = clock_getsystime();
+ double msectogo = - clock_gettimesince(x->x_targettime);
+ if (msectogo < 1E-9)
+ {
+ outlet_float(x->x_obj.ob_outlet, x->x_targetval);
+ }
+ else
+ {
+ outlet_float(x->x_obj.ob_outlet,
+ x->x_setval + x->x_1overtimediff * (timenow - x->x_prevtime)
+ * (x->x_targetval - x->x_setval));
+ clock_delay(x->x_clock,
+ (x->x_grain > msectogo ? msectogo : x->x_grain));
+ }
+}
+
+static void line_float(t_line *x, t_float f)
+{
+ double timenow = clock_getsystime();
+ if (x->x_gotinlet && x->x_in1val > 0)
+ {
+ if (timenow > x->x_targettime) x->x_setval = x->x_targetval;
+ else x->x_setval = x->x_setval + x->x_1overtimediff *
+ (timenow - x->x_prevtime)
+ * (x->x_targetval - x->x_setval);
+ x->x_prevtime = timenow;
+ x->x_targettime = clock_getsystimeafter(x->x_in1val);
+ x->x_targetval = f;
+ line_tick(x);
+ x->x_gotinlet = 0;
+ x->x_1overtimediff = 1./ (x->x_targettime - timenow);
+ clock_delay(x->x_clock,
+ (x->x_grain > x->x_in1val ? x->x_in1val : x->x_grain));
+
+ }
+ else
+ {
+ clock_unset(x->x_clock);
+ x->x_targetval = x->x_setval = f;
+ outlet_float(x->x_obj.ob_outlet, f);
+ }
+ x->x_gotinlet = 0;
+}
+
+static void line_ft1(t_line *x, t_floatarg g)
+{
+ x->x_in1val = g;
+ x->x_gotinlet = 1;
+}
+
+static void line_stop(t_line *x)
+{
+ x->x_targetval = x->x_setval;
+ clock_unset(x->x_clock);
+}
+
+static void line_free(t_line *x)
+{
+ clock_free(x->x_clock);
+}
+
+static void *line_new(t_floatarg f, t_floatarg grain)
+{
+ t_line *x = (t_line *)pd_new(line_class);
+ x->x_targetval = x->x_setval = f;
+ x->x_gotinlet = 0;
+ x->x_1overtimediff = 1;
+ x->x_clock = clock_new(x, (t_method)line_tick);
+ x->x_targettime = x->x_prevtime = clock_getsystime();
+ if (grain <= 0) grain = 20;
+ x->x_grain = grain;
+ outlet_new(&x->x_obj, gensym("float"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1"));
+ return (x);
+}
+
+static void line_setup(void)
+{
+ line_class = class_new(gensym("line"), (t_newmethod)line_new,
+ (t_method)line_free, sizeof(t_line), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_addmethod(line_class, (t_method)line_ft1,
+ gensym("ft1"), A_FLOAT, 0);
+ class_addmethod(line_class, (t_method)line_stop,
+ gensym("stop"), 0);
+ class_addfloat(line_class, (t_method)line_float);
+}
+
+/* -------------------------- timer ------------------------------ */
+static t_class *timer_class;
+
+typedef struct _timer
+{
+ t_object x_obj;
+ double x_settime;
+} t_timer;
+
+static void timer_bang(t_timer *x)
+{
+ x->x_settime = clock_getsystime();
+}
+
+static void timer_bang2(t_timer *x)
+{
+ outlet_float(x->x_obj.ob_outlet, clock_gettimesince(x->x_settime));
+}
+
+static void *timer_new(t_floatarg f)
+{
+ t_timer *x = (t_timer *)pd_new(timer_class);
+ timer_bang(x);
+ outlet_new(&x->x_obj, gensym("float"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("bang"), gensym("bang2"));
+ return (x);
+}
+
+static void timer_setup(void)
+{
+ timer_class = class_new(gensym("timer"), (t_newmethod)timer_new, 0,
+ sizeof(t_timer), 0, A_DEFFLOAT, 0);
+ class_addbang(timer_class, timer_bang);
+ class_addmethod(timer_class, (t_method)timer_bang2, gensym("bang2"), 0);
+}
+
+
+/* -------------------------- pipe -------------------------- */
+
+static t_class *pipe_class;
+
+typedef struct _hang
+{
+ t_clock *h_clock;
+ struct _hang *h_next;
+ struct _pipe *h_owner;
+ t_gpointer *h_gp;
+ union word h_vec[1]; /* not the actual number. */
+} t_hang;
+
+typedef struct pipeout
+{
+ t_atom p_atom;
+ t_outlet *p_outlet;
+} t_pipeout;
+
+typedef struct _pipe
+{
+ t_object x_obj;
+ int x_n;
+ int x_nptr;
+ float x_deltime;
+ t_pipeout *x_vec;
+ t_gpointer *x_gp;
+ t_hang *x_hang;
+} t_pipe;
+
+static void *pipe_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_pipe *x = (t_pipe *)pd_new(pipe_class);
+ t_atom defarg, *ap;
+ t_pipeout *vec, *vp;
+ t_gpointer *gp;
+ int nptr = 0;
+ int i;
+ float deltime;
+ if (argc)
+ {
+ if (argv[argc-1].a_type != A_FLOAT)
+ {
+ char stupid[80];
+ atom_string(&argv[argc-1], stupid, 79);
+ post("pipe: %s: bad time delay value", stupid);
+ deltime = 0;
+ }
+ else deltime = argv[argc-1].a_w.w_float;
+ argc--;
+ }
+ else deltime = 0;
+ if (!argc)
+ {
+ argv = &defarg;
+ argc = 1;
+ SETFLOAT(&defarg, 0);
+ }
+ x->x_n = argc;
+ vec = x->x_vec = (t_pipeout *)getbytes(argc * sizeof(*x->x_vec));
+
+ for (i = argc, ap = argv; i--; ap++)
+ if (ap->a_type == A_SYMBOL && *ap->a_w.w_symbol->s_name == 'p')
+ nptr++;
+
+ gp = x->x_gp = (t_gpointer *)t_getbytes(nptr * sizeof (*gp));
+ x->x_nptr = nptr;
+
+ for (i = 0, vp = vec, ap = argv; i < argc; i++, ap++, vp++)
+ {
+ if (ap->a_type == A_FLOAT)
+ {
+ vp->p_atom = *ap;
+ vp->p_outlet = outlet_new(&x->x_obj, &s_float);
+ if (i) floatinlet_new(&x->x_obj, &vp->p_atom.a_w.w_float);
+ }
+ else if (ap->a_type == A_SYMBOL)
+ {
+ char c = *ap->a_w.w_symbol->s_name;
+ if (c == 's')
+ {
+ SETSYMBOL(&vp->p_atom, &s_symbol);
+ vp->p_outlet = outlet_new(&x->x_obj, &s_symbol);
+ if (i) symbolinlet_new(&x->x_obj, &vp->p_atom.a_w.w_symbol);
+ }
+ else if (c == 'p')
+ {
+ vp->p_atom.a_type = A_POINTER;
+ vp->p_atom.a_w.w_gpointer = gp;
+ gpointer_init(gp);
+ vp->p_outlet = outlet_new(&x->x_obj, &s_pointer);
+ if (i) pointerinlet_new(&x->x_obj, gp);
+ gp++;
+ }
+ else
+ {
+ if (c != 'f') error("pack: %s: bad type",
+ ap->a_w.w_symbol->s_name);
+ SETFLOAT(&vp->p_atom, 0);
+ vp->p_outlet = outlet_new(&x->x_obj, &s_float);
+ if (i) floatinlet_new(&x->x_obj, &vp->p_atom.a_w.w_float);
+ }
+ }
+ }
+ floatinlet_new(&x->x_obj, &x->x_deltime);
+ x->x_hang = 0;
+ x->x_deltime = deltime;
+ return (x);
+}
+
+static void hang_free(t_hang *h)
+{
+ t_pipe *x = h->h_owner;
+ t_gpointer *gp;
+ int i;
+ for (gp = h->h_gp, i = x->x_nptr; i--; gp++)
+ gpointer_unset(gp);
+ freebytes(h->h_gp, x->x_nptr * sizeof(*h->h_gp));
+ clock_free(h->h_clock);
+ freebytes(h, sizeof(*h) + (x->x_n - 1) * sizeof(*h->h_vec));
+}
+
+static void hang_tick(t_hang *h)
+{
+ t_pipe *x = h->h_owner;
+ t_hang *h2, *h3;
+ t_pipeout *p;
+ int i;
+ union word *w;
+ if (x->x_hang == h) x->x_hang = h->h_next;
+ else for (h2 = x->x_hang; h3 = h2->h_next; h2 = h3)
+ {
+ if (h3 == h)
+ {
+ h2->h_next = h3->h_next;
+ break;
+ }
+ }
+ for (i = x->x_n, p = x->x_vec + (x->x_n - 1), w = h->h_vec + (x->x_n - 1);
+ i--; p--, w--)
+ {
+ switch (p->p_atom.a_type)
+ {
+ case A_FLOAT: outlet_float(p->p_outlet, w->w_float); break;
+ case A_SYMBOL: outlet_symbol(p->p_outlet, w->w_symbol); break;
+ case A_POINTER:
+ if (gpointer_check(w->w_gpointer, 1))
+ outlet_pointer(p->p_outlet, w->w_gpointer);
+ else post("pipe: stale pointer");
+ break;
+ }
+ }
+ hang_free(h);
+}
+
+static void pipe_list(t_pipe *x, t_symbol *s, int ac, t_atom *av)
+{
+ t_hang *h = (t_hang *)
+ getbytes(sizeof(*h) + (x->x_n - 1) * sizeof(*h->h_vec));
+ t_gpointer *gp, *gp2;
+ t_pipeout *p;
+ int i, n = x->x_n;
+ t_atom *ap;
+ t_word *w;
+ h->h_gp = (t_gpointer *)getbytes(x->x_nptr * sizeof(t_gpointer));
+ if (ac > n) ac = n;
+ for (i = 0, gp = x->x_gp, p = x->x_vec, ap = av; i < ac;
+ i++, p++, ap++)
+ {
+ switch (p->p_atom.a_type)
+ {
+ case A_FLOAT: p->p_atom.a_w.w_float = atom_getfloat(ap); break;
+ case A_SYMBOL: p->p_atom.a_w.w_symbol = atom_getsymbol(ap); break;
+ case A_POINTER:
+ gpointer_unset(gp);
+ if (ap->a_type != A_POINTER)
+ post("pipe: bad pointer");
+ else
+ {
+ *gp = *(ap->a_w.w_gpointer);
+ if (gp->gp_stub) gp->gp_stub->gs_refcount++;
+ }
+ gp++;
+ }
+ }
+ for (i = 0, gp = x->x_gp, gp2 = h->h_gp, p = x->x_vec, w = h->h_vec;
+ i < n; i++, p++, w++)
+ {
+ if (p->p_atom.a_type == A_POINTER)
+ {
+ if (gp->gp_stub) gp->gp_stub->gs_refcount++;
+ w->w_gpointer = gp2;
+ *gp2++ = *gp++;
+ }
+ else *w = p->p_atom.a_w;
+ }
+ h->h_next = x->x_hang;
+ x->x_hang = h;
+ h->h_owner = x;
+ h->h_clock = clock_new(h, (t_method)hang_tick);
+ clock_delay(h->h_clock, (x->x_deltime >= 0 ? x->x_deltime : 0));
+}
+
+static void pipe_flush(t_pipe *x)
+{
+ while (x->x_hang) hang_tick(x->x_hang);
+}
+
+static void pipe_clear(t_pipe *x)
+{
+ t_hang *hang;
+ while (hang = x->x_hang)
+ {
+ x->x_hang = hang->h_next;
+ hang_free(hang);
+ }
+}
+
+static void pipe_setup(void)
+{
+ pipe_class = class_new(gensym("pipe"),
+ (t_newmethod)pipe_new, (t_method)pipe_clear,
+ sizeof(t_pipe), 0, A_GIMME, 0);
+ class_addlist(pipe_class, pipe_list);
+ class_addmethod(pipe_class, (t_method)pipe_flush, gensym("flush"), 0);
+ class_addmethod(pipe_class, (t_method)pipe_clear, gensym("clear"), 0);
+}
+
+void x_time_setup(void)
+{
+ delay_setup();
+ metro_setup();
+ line_setup();
+ timer_setup();
+ pipe_setup();
+}
diff --git a/pd/src/z.pd b/pd/src/z.pd
new file mode 100644
index 00000000..09812537
--- /dev/null
+++ b/pd/src/z.pd
@@ -0,0 +1,41 @@
+#N canvas 471 67 626 431 10;
+#X obj 30 45 vsl 15 128 0 127 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 2200 1;
+#X obj 83 132 vsl 15 128 0 127 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 2200 1;
+#X obj 527 56 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 0 1
+;
+#X obj 421 222 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 0
+1;
+#X obj 405 241 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 0
+1;
+#X obj 358 219 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 0
+1;
+#N canvas 0 0 450 300 graph1 0;
+#X array array1 100 float 1;
+#A 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 -0.0142856 -0.0428569 -0.0714282 -0.128571 -0.185713 -0.233332
+-0.280951 -0.32857 -0.366665 -0.40476 -0.442855 -0.482415 -0.521975
+-0.561535 -0.601096 -0.640656 -0.680216 -0.719776 -0.759336 -0.798897
+-0.838457 -0.878017 -0.917577 -0.957137 -0.985709 -1.02857 -0.285713
+-0.0857139 -0.0285714 0.0285711 0.157142 0.299998 0.357141 0.442854
+0.490473 0.538092 0.628568 0.628568 0.642853 0.657139 0.657139 0.657139
+0.642853 0.599996 0.566663 0.53333 0.499997 0.471426 0.442854 0.414283
+0.364283 0.314284 0.271427 0.235713 0.199999 0.157142 0.12857 0.109523
+0.0904755 0.071428 0.0428567 0.0142854 -2.0396e-07 -2.0396e-07 0 0
+;
+#X coords 0 -1 99 1 200 140 1;
+#X restore 115 241 graph;
+#X msg 424 249 \; array1 ylabel -10 -1 1;
+#X floatatom 94 47 5 0 0;
+#X floatatom 202 29 5 0 0;
+#N canvas 468 345 450 300 foo3 0;
+#X obj 100 117 spigot;
+#X obj 265 75 r foo1;
+#X obj 259 114 +;
+#X connect 1 0 2 0;
+#X restore 356 29 pd foo3;
+#X obj 230 59 z2 \$0-doo;
+#X msg 503 105 \; foo1 sdf;
+#X connect 0 0 11 0;
+#X connect 11 0 1 0;
diff --git a/pd/src/z2.pd b/pd/src/z2.pd
new file mode 100644
index 00000000..1f36fbf3
--- /dev/null
+++ b/pd/src/z2.pd
@@ -0,0 +1,12 @@
+#N canvas 682 208 558 499 10;
+#X obj 225 80 inlet;
+#X obj 119 198 outlet;
+#X obj 109 160 hsl 128 15 0 127 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 0 1;
+#X floatatom 495 43 5 0 0;
+#N canvas 0 0 450 300 foo3 0;
+#X restore 82 78 pd foo3;
+#X obj 54 151 print;
+#X connect 0 0 2 0;
+#X connect 2 0 1 0;
+#X coords 0 0 1 1 200 140 1;
diff --git a/pd/src/z3.pd b/pd/src/z3.pd
new file mode 100644
index 00000000..ee8dc6f6
--- /dev/null
+++ b/pd/src/z3.pd
@@ -0,0 +1,3 @@
+#N canvas 408 176 626 431 12;
+#X obj 104 154 z4 foo;
+#X obj 102 200 z4 baz;
diff --git a/pd/src/z4.pd b/pd/src/z4.pd
new file mode 100644
index 00000000..653e036b
--- /dev/null
+++ b/pd/src/z4.pd
@@ -0,0 +1,2 @@
+#N canvas 140 411 616 323 12;
+#X obj 293 98 table \$1-bar 45;
diff --git a/pd/src/z5.pd b/pd/src/z5.pd
new file mode 100644
index 00000000..1adc7b08
--- /dev/null
+++ b/pd/src/z5.pd
@@ -0,0 +1,8 @@
+#N canvas 223 88 626 431 12;
+#N canvas 0 0 450 300 numbox 0;
+#X obj 125 47 loadbang;
+#X msg 124 75 123.456;
+#X floatatom 124 99 3 0 0;
+#X connect 0 0 1 0;
+#X connect 1 0 2 0;
+#X restore 212 48 pd numbox;
diff --git a/pd/src/z6.pd b/pd/src/z6.pd
new file mode 100644
index 00000000..f8502dc2
--- /dev/null
+++ b/pd/src/z6.pd
@@ -0,0 +1,4 @@
+#N canvas 121 193 452 302 12;
+#X obj 101 40 inlet;
+#X obj 92 138 outlet;
+#X coords 0 0 1 1 200 140 1;